Webpack5+React搭建项目

Webpack + React

初始化项目
  1. 创建packge.json
    npm init -y

  2. 安装webpack基本依赖包
    npm install webpack webpack-dev-server webpack-cli -D

  3. 创建文件
    src/index.js, 添加测试代码: console.log(‘hello webpack!’)

  4. 在 package.json中添加脚本 “build”: “webpack”
    即在终端执行命令 npm run build 时进行webpack打包

配置webpack
  1. 在根目录下创建 config 目录,创建config/webpack.config.dev.js 用来进行开发环境的webpack配置
    添加webpack基本配置
    package.json中修改脚本脚本:
    “dev”: “webpack --config ./config/webpack.config.dev.js”
    npm run dev 打包测试
    开发环境中使用webpack-dev-server插件启动服务
    先下载:npm i webpack-dev-server -D
    配置devServer:
    开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)

  2. 安装插件clean-webpack-plugin, 每次打包前删除上次生成的打包目录
    npm install clean-webpack-plugin -D
    在plugins中配置

  3. 创建src/index.html
    webpack中添加打包html的配置
    安装插件: npm install html-webpack-plugin -D

  4. 配置devtool
    dev=> devtool: ‘eval-cheap-module-source-map’
    test=> devtool: ‘cheap-module-source-map’
    prod=> devtool: false

引入react框架
react全家桶(react+react-router+axios+antd+mobx)
  1. 先安装react
    npm install react react-dom -S

  2. 修改index.js,使用react挂在dom

  3. 配置支持react的打包构建
    要想把react的代码使用webpack编译构建成浏览器可以运行的代码,需要使用babel等工具进行"翻译一下"
    (webpack默认只能处理js和json,其他类型的文件需要通过loader和plugins来处理)
    Babel的作用:
    1.将 ECMAScript 2015+ 代码转换为 JavaScript 向后兼容版本的代码;
    2.转换 JSX 语法
    Babel 有两种并行的配置文件方式:
    1.项目范围的配置
    在根目录下创建 babel.config.json 文件,以及不同扩展名的文件 (.js, .cjs, .mjs)
    这里我们创建babel.config.js文件来配置babel
    2.相对文件的配置
    - .babelrc.json 文件,以及不同扩展名的文件 (.babelrc, .js, .cjs, .mjs)
    - 带有 “babel” key 的 package.json 文件
    官方预设
    @babel/preset-env 用于编译 ES2015+ 语法
    @babel/preset-typescript 用于 TypeScript
    @babel/preset-react 用于 React

安装Babel
- 安装基础模块:
npm install babel-loader @babel/core @babel/preset-env -D
* Babel 的核心功能容纳于 @babel/core 模块
* babel-loader: 对js做兼容性处理 只能做一些简单的如箭头函数,扩展运算符等的处理
* @babel/preset-env: 对Promise等高级的语法处理
在rules中配置babel-loader
在babel.config.js中添加@babel/preset-env
- 安装预设
npm install @babel/preset-typescript @babel/preset-react -D

方案一:@babel/preset-env + @babel/polyfill (不推荐使用)

方案二:@babel/preset-env + @babel/plugin-transform-runtime + @babel/runtime-corejs3
npm install @babel/plugin-transform-runtime -D
npm install @babel/runtime-corejs3 -S
npm install core-js@3 regenerator-runtime -S

修改babel.config.js中plugins的配置
(  
    解决babel-loader过慢! 引入 Babel runtime 作为一个独立模块,来避免重复引入
    corejs@3 兼容性处理的按需加载
)

运行项目,这时会报错
在webpack配置文件中添加
// 解析
resolve: {
  extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.mjs', '.cjs', '.css', '.less'],
},
安装loaders来支持样式
处理样式文件

1.下载loaders
npm install less less-loader style-loader css-loader -D
若想配置得更全面,也可以下载和配置sass和sass-loader
配置 rules,注意这时开发环境和生产环境的配置有区别
2.css兼容性处理:postcss
npm install postcss-loader postcss-preset-env -D
帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式
“browserslist”: {
// 开发环境 --> 设置node环境变量:process.env.NODE_ENV = development
“development”: [
“last 1 chrome version”,
“last 1 firefox version”,
“last 1 safari version”
],
// 生产环境:默认是看生产环境
“production”: [
“>0.2%”,
“not dead”,
“not op_mini all”
]
}
3.在生产环境中提取js中的css成单独的文件,安装插件 mini-css-extract-plugin
npm install mini-css-extract-plugin -D
在生产环境中引入
const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’);
用MiniCssExtractPlugin.loader替换style-loader
在plugins中配置输出的文件名

webpack5内置了处理图片、字体文件,不需要额外的loader来处理

webpack5之前我们处理静态资源比如。图片字体之类的资源的时候等,需要用到url-loader,file-loader,raw-loader,webpack5则放弃了这三个loader,这三个loader在github上也停止了更新。

webpack5使用四种新增的资源模块(Asset Modules)替代了这些loader的功能。

asset/resource 将资源分割为单独的文件,并导出url,就是之前的 file-loader的功能.
asset/inline 将资源导出为dataURL(url(data:))的形式,之前的 url-loader的功能.
asset/source 将资源导出为源码(source code). 之前的 raw-loader 功能.
asset 自动选择导出为单独文件或者 dataURL形式(默认为8KB). 之前有url-loader设置asset size limit 限制实现。

复制其他静态资源如字体、第三方库等

npm install copy-webpack-plugin -D

引入react全家桶

(react+react-router+axios+antd+redux)
npm install react-router-dom -S
npm install axios -S
npm install antd -S

路由配置
路由组件的注册使用 import动态导入语法:能将某个文件单独打包

安装插件

npm install -D
@babel/plugin-proposal-decorators // 配置对装饰器的支持
@babel/plugin-proposal-class-properties // 支持类属性的插件
@babel/plugin-proposal-async-generator-functions
@babel/plugin-syntax-dynamic-import

打包优化

配置 optimization 属性
压缩css的插件 npm install css-minimizer-webpack-plugin -D
下载插件:
npm install clean-webpack-plugin webpack-bundle-analyzer -D
clean-webpack-plugin 在打包的时候会删除之前的打包目录
webpack-bundle-analyzer 在打包结束的时候,会启动启动一个服务在浏览器查看打包的大小和包含的内容等
配置 new BundleAnalyzerPlugin()

提取webpack的配置文件

为了区分开发环境和生产环境,我们将webpack的配置文件分为:
webpack.config.base.js // 是webpack在不同环境的公共配置
webpack.config.dev.js // 开发环境的配置
webpack.config.prod.js // 生产环境的配置
webpack.utils.js // 配置用的工具方法

1.下载webpack合并插件
npm install webpack-merge -D
2.提取webpack的dev和prod配置中公共部分到webpack.config.base
3.利用 const { merge } = require(‘webpack-merge’);
将各自的配置与公共配置merge到一起
const output = merge(
{ fruit: “apple”, color: “red” },
{ fruit: “strawberries” }
);
console.log(output);
// { color: “red”, fruit: “strawberries”}

项目目录结构

在这里插入图片描述

webpack配置

** webpack.config.base.js**

const webpack = require('webpack');
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CssMinimizerPlugin =require('css-minimizer-webpack-plugin');
const copyWebpackPlugin = require('copy-webpack-plugin');
const { getGlobalConstants } = require('./webpack.utils');

// webpack公共配置
module.exports = {
    entry: {
        index: resolve('./src/index')
    },
    output: {
        path: resolve(__dirname, '../dist'),
        filename: 'js/[name].bundle.[contenthash:8].js',  // index.js 被打包出来的文件
        chunkFilename: 'js/[name].chunk.[contenthash:8].js',
        // assetModuleFilename: 'assets/[hash][ext]', //引用资源文件打包后的文件名
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx|ts|tsx|mjs|cjs)$/, // 匹配哪些文件
                exclude: /(node_modules|bower_components)/,
                loader: 'babel-loader',
            },
            {
                test: /\.(css|less)$/,
                use: ['style-loader', 'css-loader', {
                    loader: 'postcss-loader',
                    options: {
                        postcssOptions: {
                            ident: 'postcss',
                            plugins: () => [ // postcss的插件
                                require('postcss-preset-env')()
                            ]
                        }
                    }
                }, {
                    loader: 'less-loader',
                    options: {
                        lessOptions: {
                            javascriptEnabled: true,
                        }
                    }
                }],
            },
            {
                test: /\.(png|jpg|jpeg|gif)$/,
                type: 'asset',
                parser: {
                dataUrlCondition: {
                    maxSize: 8 * 1024,
                },
                },
                generator: {
                filename: 'assets/images/[hash][ext]',
                },
            },
            { test: /\.(woff(2)?|eot|ttf|otf)$/, type: 'asset/fonts' },
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: resolve('./src/index.html'),
            favicon: resolve('./src/static/favicon.png'),
            minify: { // 对html压缩
                collapseWhitespace: true, // 移除空格
                removeComments: true // 移除注释
            }
        }),
        // 配置全局的常量
        new webpack.DefinePlugin(getGlobalConstants(process.env.NODE_ENV)),
        // copy 静态目录的资源
        new copyWebpackPlugin({
            patterns: [{
                from: './src/static', // 从哪个目录
                to: '../dist/static',  // copy到哪个目录,默认是output到输出路径
                globOptions: {
                    ignore: ['.*']
                }
            }]
        }),
    ],
    // 解析
    resolve: {
        extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.mjs', '.cjs', '.css', '.less'],
        alias: {  // 配置别名
            'src': resolve('./src'),
            'components': resolve('./src/components'),
            'assets': resolve('./src/assets'),
            'pages': resolve('./src/pages'),
            'api': resolve('./src/api'),
        }
    },
    // 优化 
    optimization: {
        runtimeChunk: { name: 'runtime' },
        minimizer: ['...', new CssMinimizerPlugin()],  // '...' 使用默认值(TerserPlugin)
        splitChunks: {
            cacheGroups: {
                vendors: {
                    test: /[\\/]node_modules[\\/](react|react-dom|react-router|react-router-dom|axios|history|scheduler|react-is|prop-types|object-assign|mini-create-react-context|hoist-non-react-statics|resolve-pathname|value-equal|tiny-invariant)[\\/]/,
                    name: 'vendors',
                    chunks: 'all',
                    priority: 100,
                },
              },
        }
    },
}

** webpack.config.dev.js**

const path = require('path');
const { merge } = require('webpack-merge');
const common = require('./webpack.config.base');

const devProxy = require('../proxyConfig');

// webpack开发环境配置
module.exports = merge(common, {
    mode: 'development',
    // 优化 
    optimization: {
        moduleIds: 'named',
        chunkIds: 'named',
        minimize: false,  // 开发环境下不启用压缩
    },
    devtool: 'eval-cheap-module-source-map',
    devServer: {  // 开发环境本地启动的服务配置
        static: {
            directory: path.join(__dirname, '../dist'),
        },
        // 端口号
        port: 3000,
        // 自动打开浏览器
        // open: true,
        // 开启HMR(热模块替换)功能, 当修改了webpack配置,新配置要想生效,必须重新webpack服务
        hot: true,
        // 服务器代理 --> 解决开发环境跨域问题
        proxy: devProxy,
        historyApiFallback: true, // 当找不到路径时,默认加载index.html
        client: {  // 不显示[webpack-dev-server]的log
            logging: 'none',
        },
    },
})

** webpack.config.prod.js**

const { resolve } = require('path');
const { merge } = require('webpack-merge');
const common = require('./webpack.config.base');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

// webpack生产环境配置
module.exports = merge(common, {
    mode: 'production',
    // output: {
        // publicPath: '/'  // 所有资源引入时的公共路径前缀
    // },
    module: {
        rules: [
            {
                test: /\.(css|less)$/,
                exclude: /node_modules/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions: {
                                ident: 'postcss',
                                plugins: () => [ // postcss的插件
                                    require('postcss-preset-env')()
                                ]
                            }
                        }
                    },{
                        loader: 'less-loader',
                        options: {
                            lessOptions: {
                                javascriptEnabled: true,
                            }
                        }
                    }
                ],
            },
        ]
    },
    plugins: [
        new CleanWebpackPlugin(),
        new MiniCssExtractPlugin({
            // prod模式下分离css文件, 对输出的css文件进行重命名
            filename: 'css/[name].bundle.[contenthash:8].css',
            chunkFilename: 'css/[name].chunk.[contenthash:8].css',
        }),
        // new BundleAnalyzerPlugin(),
    ],
    // 优化 
    optimization: {
        moduleIds: 'deterministic',
        chunkIds: 'deterministic',
        minimize: true,  // 生产环境开启压缩
    },
})

** webpack.config.utils.js**

const getGlobalConstants = (__ENV__ = 'production') => {
  // console.log(1,__ENV__)
    const _path = `../envConfig/env.${__ENV__}.js`;
    const originalConstants = require(_path);
    const appliedConstants = {};
    Object.keys(originalConstants).forEach(key => {
      appliedConstants[key] = JSON.stringify(originalConstants[key]);
    });
    return appliedConstants;
};

module.exports = {
    getGlobalConstants
}

区分环境

将 webpack.config文件合并成一个,根据环境变量区分

目录结构:
在这里插入图片描述

** webpack.config.js**

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CssMinimizerPlugin =require('css-minimizer-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const copyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { resolve, getGlobalConstants } = require('./webpack.utils');

const devServer = require('../devConfig/devServer');

// webpack公共配置
module.exports = (env) => {
    const isDev = env.__ENV__ === 'dev';
    process.env.NODE_ENV = isDev ? 'development': 'production';
    const styleLoader = isDev ? 'style-loader' : MiniCssExtractPlugin.loader;
    const config = {
        mode: isDev ? 'development': 'production',
        entry: {
            index: resolve('../src/index')
        },
        output: {
            path: resolve('../dist'),
            filename: 'js/[name].bundle.[contenthash:8].js',  // index.js 被打包出来的文件
            chunkFilename: 'js/[name].chunk.[contenthash:8].js',
            // assetModuleFilename: 'assets/[hash][ext]', //引用资源文件打包后的文件名
        },
        module: {
            rules: [
                {
                    test: /\.(js|jsx|ts|tsx|mjs|cjs)$/, // 匹配哪些文件
                    exclude: /(node_modules|bower_components)/,
                    loader: 'babel-loader',
                },
                {
                    test: /\.(css|less)$/,
                    use: [styleLoader, 'css-loader', {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions: {
                                ident: 'postcss',
                                plugins: () => [ // postcss的插件
                                    require('postcss-preset-env')()
                                ]
                            }
                        }
                    }, {
                        loader: 'less-loader',
                        options: {
                            lessOptions: {
                                javascriptEnabled: true,
                            }
                        }
                    }],
                },
                {
                    test: /\.(png|jpg|jpeg|gif)$/,
                    type: 'asset',
                    parser: {
                    dataUrlCondition: {
                        maxSize: 8 * 1024,
                    },
                    },
                    generator: {
                    filename: 'assets/images/[hash][ext]',
                    },
                },
                { test: /\.(woff(2)?|eot|ttf|otf)$/, type: 'asset/fonts' },
            ]
        },
        plugins: [
            new CleanWebpackPlugin(),
            new HtmlWebpackPlugin({
                template: resolve('../src/index.html'),
                favicon: resolve('../src/static/favicon.png'),
                minify: { // 对html压缩
                    collapseWhitespace: true, // 移除空格
                    removeComments: true // 移除注释
                }
            }),
            // 配置全局的常量
            new webpack.DefinePlugin(getGlobalConstants(env.__ENV__)),
            // copy 静态目录的资源
            new copyWebpackPlugin({
                patterns: [{
                    from: './src/static', // 从哪个目录
                    to: '../dist/static',  // copy到哪个目录,默认是output到输出路径
                    globOptions: {
                        ignore: ['.*']
                    }
                }]
            }),
        ],
        // 解析
        resolve: {
            extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.mjs', '.cjs', '.css', '.less'],
            alias: {  // 配置别名
                'src': resolve('../src'),
                'components': resolve('../src/components'),
                'assets': resolve('../src/assets'),
                'pages': resolve('../src/pages'),
                'api': resolve('../src/api'),
            }
        },
        devtool: isDev?'eval-cheap-module-source-map':false,
        // 优化 
        optimization: {
            moduleIds: isDev?'named':'deterministic',
            chunkIds: isDev?'named':'deterministic',
            minimize: isDev?false:true,  // 开发环境下不启用压缩
            runtimeChunk: { name: 'runtime' },
            minimizer: ['...', new CssMinimizerPlugin()],  // '...' 使用默认值(TerserPlugin)
            splitChunks: {
                cacheGroups: {
                    vendors: {
                        test: /[\\/]node_modules[\\/](react|react-dom|react-router|react-router-dom|axios|history|scheduler|react-is|prop-types|object-assign|mini-create-react-context|hoist-non-react-statics|resolve-pathname|value-equal|tiny-invariant)[\\/]/,
                        name: 'vendors',
                        chunks: 'all',
                        priority: 100,
                    },
                },
            }
        },
    }
    // 配置时区分环境
    if(isDev) {
        config.devServer = devServer;
    }else{
        config.plugins.push(
            new MiniCssExtractPlugin({
                // prod模式下分离css文件, 对输出的css文件进行重命名
                filename: 'css/[name].bundle.[contenthash:8].css',
                chunkFilename: 'css/[name].chunk.[contenthash:8].css',
            })
        )
    }
    return config;
}

** webpack.utils.js**

const path = require('path');
const resolve = _path => path.resolve(__dirname, _path);

const getGlobalConstants = (__ENV__ = 'prod') => {
  console.log(1,__ENV__)
    const _path = `../envConfig/env.${__ENV__}.js`;
    const originalConstants = require(_path);
    const appliedConstants = {};
    Object.keys(originalConstants).forEach(key => {
      appliedConstants[key] = JSON.stringify(originalConstants[key]);
    });
    return appliedConstants;
};

module.exports = {
  resolve,
  getGlobalConstants
}

entry 的三种写法
1. string ,单入口 ,打包形成一个chunk,输出一个bundle文件
entry: ‘…/src/index.js’
2. array 多入口, 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件
entry: [’…/src/index.js’, ‘…/src/test.js’ ]
3. object 多入口,有几个入口文件就形成几个chunk,输出几个bundle文件, 此时chunk的名称是 key
entry: {
index: ‘…/src/index.js’,
test: ‘…/src/test.js’
}
(以上方法1、3用的比较多 )
–> 特殊用法
{
// 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
index: [’./src/index.js’, ‘./src/count.js’],
// 形成一个chunk,输出一个bundle文件。
add: ‘./src/add.js’
}

output:
public 用于生产环境,所有资源引入时的公共路径的前缀
例如一个图片路径是 ‘img/a.png’ —> ‘/img/a.png’ 以当前服务器地址去补充,去服务器根目录下找img文件夹,再找img下的a.png
publicPath: ‘/’

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
redux、reactwebpack是目前非常流行的前端技术,它们可以帮助我们快速构建复杂的单页面应用。而热加载是一种非常方便的开发工具,能够在我们修改代码后自动重新编译并刷新页面,极大地提高了开发效率。 然而,IE8是一个老旧的浏览器,在支持新技术的同时也存在很多兼容性问题。要使我们的应用在IE8上运行,我们需要进行一些特殊的处理。 下面是一些可能有用的技巧: 1. 使用babel进行ES6代码转换:redux和react都使用了一些ES6语法,而IE8并不支持这些语法。我们可以使用babel将ES6代码转换为ES5代码,从而在IE8上运行。同时,我们还需要使用babel-polyfill来提供一些ES6新特性的支持。 2. 使用es5-shim来提供一些ES5新特性的支持:IE8对ES5的支持也不完整,我们可以使用es5-shim来提供一些ES5新特性的支持。 3. 使用es6-shim来提供一些ES6新特性的支持:IE8对ES6的支持更加有限,我们可以使用es6-shim来提供一些ES6新特性的支持。 4. 使用webpack的兼容性插件:webpack提供了一些兼容性插件,可以自动处理一些兼容性问题,比如babel-loader、es3ify-webpack-plugin等。 5. 使用react-ie8来提供react在IE8上的支持:react-ie8是一个为react提供在IE8上支持的库,可以很方便地解决一些兼容性问题。 6. 使用redux-ie8来提供redux在IE8上的支持:redux-ie8是一个为redux提供在IE8上支持的库,可以很方便地解决一些兼容性问题。 总之,要使我们的应用在IE8上运行,需要进行一些特殊的处理,包括使用babel进行ES6代码转换、使用es5-shim和es6-shim来提供一些新特性的支持、使用webpack的兼容性插件、使用react-ie8和redux-ie8来提供在IE8上的支持等。这些技巧需要不断地更新和完善,以适应不断变化的前端技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值