翼度科技»论坛 编程开发 JavaScript 查看内容

从0到1:React项目中的Webpack配置实战

3

主题

3

帖子

9

积分

新手上路

Rank: 1

积分
9
公司项目一般都是使用集团封装好的脚手架,脚手架内部实现咱看不到也摸不着,好不容易组内推行新的UI框架,需要自行定义 webpack 配置,这可是个绝佳的好机会,我对配置过程进行了梳理,把商业项目的成熟配置小跑着送上。
初始化

首先新建一个空文件夹,执行 npm init  初始化生成 package.json 文件。
创建 src 文件夹,项目的业务代码都放在这里,再创建 index.js,这是项目的入口文件,待会使用 webpack 来打包项目,就从 index.js 文件开始。
大家都知道,webpack 在开发和生产时部分配置是不同的,比如在开发时,我们希望代码能够即改即生效,能够立马在页面中看到效果,需要 devServer 来帮助我们实现,而在开发中,我们希望能对 node_modules 资源进行分包,需要借助 splitChunks 属性,而还有一些属性是开发和生产共用的,比如 js 、css 资源的处理。
创建 webpack-config 文件夹,里面包含 webpack.dev.js,webpack.prod.js,webpack.base.js,分别定义 webpack 的开发环境、生产环境、公共的配置。
公共配置

开发和生产的配置都需要依赖公共配置,所以我们首先来定义 webpack.base.js。
项目入口/出口

执行 npm install webpack webpack-cli 安装 webpack 基础依赖,再定义 webpack 配置文件的基础框架,entry 定义项目入口文件,output 定义编译后的出口,我们把编译后文件放在项目根目录的 dist 文件夹下。
  1. const path = require("path");
  2. module.exports = {
  3.   entry: "./src/index.js",
  4.   output: {
  5.     path: path.join(__dirname, "../dist"),
  6.     filename: "[name].[contenthash:8].js",
  7.   },
  8.   module: {},
  9.   plugins: [],
  10. };
复制代码
样式资源

module 中放置各种文件的 loader 处理规则,首先处理样式资源,我们项目中通常使用 scss  来作为项目的预处理器,所以这里指定 css 及 scss 处理方式(less 或其他预处理器同理)。
执行 npm install sass sass-loader postcss postcss-loader postcss-preset-env css-loader mini-css-extract-plugin --save 指令,安装所需要的 loader 和 plugin。
css 和 scss 文件的区别仅在于 scss 需要先通过 sass-loader 处理为 css 资源,其他的配置是一致的,这里提取到 commonStyleLoader。
  1. const MiniCssExtractPlugin = require("mini-css-extract-plugin");
  2. module.exports = {
  3.   module: {
  4.     rules: [
  5.       {
  6.    
  7.     test: /\.css$/,
  8.    
  9.     exclude: /node_modules/,
  10.    
  11.     use: commonStyleLoader,
  12.       },
  13.       {
  14.    
  15.     test: /\.scss$/,
  16.    
  17.     exclude: /node_modules/,
  18.    
  19.     use: [...commonStyleLoader, "sass-loader"],
  20.       },
  21.     ],
  22.   },
  23.   plugins: [
  24.     new MiniCssExtractPlugin({
  25.       filename: "style/[name].[contenthash:8].css",
  26.     }),
  27.   ],
  28. };
复制代码
再来看处理 sass 和 css 资源的公共 loader,首先使用 postcss-loader 进行兼容性处理,然后通过 css-loader 解析 css 语法,最后通过 mini-css-extract-plugin 的将 css 从 js 中分离出来并生成新的 css 文件,也就是下面代码的 MiniCssExtractPlugin.loader 和上面代码的 new MiniCssExtractPlugin 操作 。
  1. const commonStyleLoader = [
  2.   MiniCssExtractPlugin.loader,
  3.   "css-loader",
  4.   {
  5.     loader: "postcss-loader",
  6.     options: {
  7.       postcssOptions: {
  8.    
  9.     plugins: ["postcss-preset-env"],
  10.       },
  11.     },
  12.   },
  13. ];
复制代码
另外,postcss-loader 在进行兼容性处理的时候,需要配置 browserslist 来告知兼容的要求,可以直接定义在 package.json 中,开发环境兼容要求为近一个版本的chrome/safari/firefox浏览器,生产环境兼容市场份额大于 0.2% 且活跃的浏览器。
  1. "browserslist": {
  2.     "development": [
  3.       "last 1 chrome version",
  4.       "last 1 safari version",
  5.       "last 1 firefox version"
  6.     ],
  7.     "production": [
  8.       ">0.2%",
  9.       "not dead"
  10.     ]
  11.   }
复制代码
js/jsx

js 和 react 需要使用的 jsx 资源的处理方式相同,都使用 babel-loader,所以可以一起来定义 loader 匹配规则。
执行 npm install babel @babel/core babel-loader @babel/preset-env core-js@3 @babel/preset-react --save 指令来安装相关资源。
  1. module.exports = {
  2.   module: {
  3.     resolve: {
  4.       extensions: [".jsx", ".js", ".json"],
  5.     },
  6.     rules: [
  7.       {
  8.    
  9.     test: /\.(js|jsx)$/,
  10.    
  11.     exclude: /node_modules/,
  12.    
  13.     use: ["babel-loader"],
  14.       },
  15.     ],
  16.   },
  17. };
复制代码
extensions 中配置上常用文件后缀名,这样引入jsx文件时,就可省略后缀。
babel-loader 使用 babel 工具 来处理资源,需要配置处理规则,babel 中有很多 pluginspresets,presets 包括很多 plugins,所以直接配置 presets 会方便很多。
新增 babel.config.js 文件,使用 @babel/preset-react 处理 react 资源,通过 @babel/preset-env 处理 js 资源,再添加 corejs 的配置增加 js 兼容处理 polyfills
  1. module.exports = {
  2.   presets: [
  3.     [
  4.       "@babel/preset-env",
  5.       {
  6.    
  7.     useBuiltIns: "usage",
  8.    
  9.     corejs: 3,
  10.       },
  11.     ],
  12.     "@babel/preset-react",
  13.   ],
  14. };
复制代码
图片

在 webpack5 之前,处理字体图片等资源需要通过 url-loaderfile-loader,webpack5 新增 asset module type,通过4种新的模块类型替代以前的loader。
  1. module.exports = {
  2.   module: {
  3.     rules: [
  4.       {
  5.    
  6.     test: /\.(jp(e)g|png|gif)$/,
  7.    
  8.     type: "asset",
  9.    
  10.     parser: {
  11.    
  12.       dataUrlCondition: {
  13.    
  14.         maxSize: 8 * 1024,
  15.    
  16.       },
  17.    
  18.     },
  19.    
  20.     generator: {
  21.    
  22.       filename: "img/[name].[contenthash:8].[ext]",
  23.    
  24.     },
  25.       },
  26.     ],
  27.   },
  28. };
复制代码
其中模块类型 asset 替代 url-loader,可以自行定义根据图片大小导出资源为 Data URI 内联到 js 文件中,还是生成一个单独的文件,这里定义以资源大小 8kb 为分界线。
html

html 文件处理非常的简单,通过 html-webpack-plugin 生成 HTML 文件并注入 webpack 输出的 javaScript 文件和 css 文件即可。
  1. const HtmlWebpackPlugin = require("html-webpack-plugin");
  2. module.exports = {
  3.   plugins: [
  4.     new HtmlWebpackPlugin({
  5.       template: "./public/index.html",
  6.       inject: "body",
  7.       minify: {
  8.    
  9.     collapseWhitespace: true,
  10.       },
  11.     }),
  12.   ],
  13. };
复制代码
可以在 plugin 中配置模板、引入 webpack 输出 js 资源的地址、压缩方式。
运行测试

以上就完成了公共配置部分,现在我们来测试一下配置是否生效。
在 public 目录下创建 index.html 文件,初始化之后在 body 标签中增加 react 需要的挂载容器。
  1. [/code]再执行 npm install react react-dom --save 指令,在 src/index.js 文件中定义渲染逻辑
  2. [code]import React from 'react';
  3. import ReactDom from 'react-dom';
  4. import App from './App';
  5. ReactDom.render(<App/>, document.getElementById('container'))
复制代码
然后在 src 目录下新建 App.js,里面随意编写 jsx 代码。
再通过命令行工具执行 npx webpack -c webpack-config/webpack.base.js,运行结果没有报错就代表配置正确啦~ 有 warning ⚠ The 'mode' option has not been set 是没关系的,后续结合开发环境和生产环境的配置一起执行时,mode 属性会被配置上。
开发环境配置

开发环境的配置很简单,只要使用 webpack-dev-server 开启本地服务就行。
执行 npm install webpack-dev-server webpack-merge -D 指令安装依赖,在 config.dev.js 中定义开发时所需要的配置。
使用 webpack-merge 工具来合并 webpack.base.js 中的公共配置,将 dev 配置和公共配置合并导出。
  1. const { merge } = require("webpack-merge");
  2. const baseConfig = require("./webpack.base");
  3. const devConfig = {
  4.   mode: "development",
  5.   devServer: {
  6.     open: true,
  7.     port: 8001,
  8.     compress: true,
  9.     hot: true
  10.   },
  11. };
  12. module.exports = merge(devConfig, baseConfig);
复制代码
运行命令 npx webpack serve -c ./webpack-config/webpack.dev.js,如果能在浏览器自动打开 http://localhost:8001 页面就表示开发环境配置成功。
我们通常会把指令配置到 package.json 文件的 script 属性中,执行起来更为方便。
  1. "scripts": {
  2.     "dev": "webpack serve -c ./webpack-config/webpack.dev.js"
  3. }
复制代码
这样,在命令行工具中,我们只需要执行 npm run dev 即可。
生产环境配置

生产环境配置的框架和开发环境类似,都是使用 webpack-merge 合并公共配置
  1. const { merge } = require("webpack-merge");
  2. const baseConfig = require("./webpack.base");
  3. const prodConfig = {
  4.   mode: "production",
  5. };
  6. module.exports = merge(prodConfig, baseConfig);
复制代码
当 mode 定义为 production 时,webpack 会自动给我们做一些代码压缩和 tree shaking 等操作,我们可以自己再对编译出来的 js 和 css 文件进行压缩。
js/css压缩

执行 npm install css-minimizer-webpack-plugin terser-webpack-plugin clean-webpack-plugin --save  指令安装依赖。
使用 css-minimizer-webpack-plugin 压缩 css 资源,通过 terser-webpack-plugin 开启多进程并发运行以提高构建速度。
  1. const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
  2. const TerserWebpackPlugin = require("terser-webpack-plugin");
  3. const prodConfig = {
  4.   optimization: {
  5.     minimizer: [
  6.       new CssMinimizerWebpackPlugin(),
  7.       new TerserWebpackPlugin({
  8.    
  9.     parallel: true,
  10.       }),
  11.     ],
  12.   },
  13. };
复制代码
splitChunks

目前除了异步加载引入的模块,其他的内容都打包在了一个入口文件中,其中包含 node_modules 中使用到的依赖,如 React、RecatDom 等,这些依赖通常是不会变化的,打包到一个公共的文件中,可以利用浏览器的缓存策略,不需要每次都重新拉取资源。
  1. const prodConfig = {
  2.   optimization: {
  3.     splitChunks: {
  4.       chunks: "all",
  5.       cacheGroups: {
  6.    
  7.     vendor: {
  8.    
  9.       test: /[\\/]node_modules[\\/]/,
  10.    
  11.       filename: '[name].bundle.js'
  12.    
  13.     },
  14.       },
  15.     },
  16.   },
  17. };
复制代码
这样 node_modules中使用到的资源文件会被打包成一个以 bundle.js 结尾的文件
clean-webpack-plugin

当我们执行 npm run build 后,编译后文件会放到 dist 文件夹下,为了避免 dist 文件夹内容越来越来多不便查看,每次我们都要手动删除文件夹,非常不方便。clean-webpack-plugin 可以帮助我们解决这个问题。
  1. const { CleanWebpackPlugin } = require('clean-webpack-plugin')
  2. const prodConfig = {
  3.   plugins: [
  4.     new CleanWebpackPlugin()
  5.   ]
  6. };
复制代码
externals

有些资源,我们会使用稳定可靠的 cdn 资源,提升页面加载速度,减少打包出来的 bundle 体积,这时候就要配合 externals 使用。
如在 index.html 页面引入 react、react-dom
  1.    
  2.    
复制代码
在 webpack.prod.js 中定义 externals,告诉 webpack 当遇到 react、react-dom 时,不要将其打包,期望在全局环境中找到 React、ReactDOM 的配置。
  1. const prodConfig = {
  2.   externals: {
  3.      react: "React",
  4.      "react-dom": "ReactDOM",
  5.    },
  6. };
复制代码
同样,将生产环境的编译指令配置到 package.json 文件的 script 属性中。
  1. "scripts": {
  2.     "build": "webpack -c ./webpack-config/webpack.prod.js"
  3. }
复制代码
这样,在命令行工具中,我们只需要执行 npm run build 即可查看编译产物。
总结

在真实的开发场景,还有生产模式使用 CopyWebpackPlugin 在构建过程复制文件到输出目录,不经过 webpack 打包的情况,也有根据项目部署域名来定义 publicPath 的情况。这些都比较定制化,不同项目使用的配置也不相同。
另外还有前端工程化的配置大家也可以加上,如 eslint、prettier、stylelint、git hooks,为react项目添加开发/提交规范中有详细的讲述,可点击查看。
以上就是开发和生产环境的基础配置,大家可以在这个基础上增加自己项目的不同配置。更多有关 前端、工程化 的内容可以参考我其它的博文,持续更新中~
附上完整配置代码。
webpack.base.js 文件
  1. const path = require("path");const MiniCssExtractPlugin = require("mini-css-extract-plugin");const HtmlWebpackPlugin = require("html-webpack-plugin");const commonStyleLoader = [
  2.   MiniCssExtractPlugin.loader,
  3.   "css-loader",
  4.   {
  5.     loader: "postcss-loader",
  6.     options: {
  7.       postcssOptions: {
  8.    
  9.     plugins: ["postcss-preset-env"],
  10.       },
  11.     },
  12.   },
  13. ];module.exports = {  entry: "./src/index.js",  output: {    path: path.join(__dirname, "../dist"),    filename: "[name].[contenthash:8].js",  },  resolve: {    extensions: [".jsx", ".js", ".json"],  },  module: {    rules: [      {   
  14.     test: /\.css$/,   
  15.     exclude: /node_modules/,   
  16.     use: commonStyleLoader,      },      {   
  17.     test: /\.scss$/,   
  18.     exclude: /node_modules/,   
  19.     use: [...commonStyleLoader, "sass-loader"],      },      {   
  20.     test: /\.(js|jsx)$/,   
  21.     exclude: /node_modules/,   
  22.     use: ["babel-loader"],      },      {   
  23.     test: /\.(jp(e)g|png|gif)$/,   
  24.     type: "asset",   
  25.     parser: {   
  26.       dataUrlCondition: {   
  27.         maxSize: 8 * 1024,   
  28.       },   
  29.     },   
  30.     generator: {   
  31.       filename: "img/[name].[contenthash:8].[ext]",   
  32.     },      },    ],  },  plugins: [    new MiniCssExtractPlugin({      filename: "style/[name].[contenthash:8].css",    }),    new HtmlWebpackPlugin({      template: "./public/index.html",      inject: 'body',      minify: {   
  33.     collapseWhitespace: true,      }    }),  ],};
复制代码
webpack.dev.js 文件
  1. const { merge } = require("webpack-merge");
  2. const baseConfig = require("./webpack.base");
  3. const devConfig = {
  4.   mode: "development",
  5.   devServer: {
  6.     open: true,
  7.     port: 8001,
  8.     compress: true,
  9.     hot: true
  10.   },
  11. };
  12. module.exports = merge(devConfig, baseConfig);
复制代码
webpack.prod.js 文件
  1. const { merge } = require("webpack-merge");const baseConfig = require("./webpack.base");const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");const TerserWebpackPlugin = require("terser-webpack-plugin");const { CleanWebpackPlugin } = require("clean-webpack-plugin");const prodConfig = {  mode: "production",  optimization: {    minimizer: [      new CssMinimizerWebpackPlugin(),      new TerserWebpackPlugin({   
  2.     parallel: true,      }),    ],    splitChunks: {      chunks: "all",      cacheGroups: {   
  3.     vendor: {   
  4.       test: /[\\/]node_modules[\\/]/,   
  5.       filename: "[name].bundle.js",   
  6.     },      },    },  },  externals: {    react: "React",    "react-dom": "ReactDOM",  },  plugins: [new CleanWebpackPlugin()],};module.exports = merge(prodConfig, baseConfig);
复制代码
来源:https://www.cnblogs.com/vigourice/p/18377974
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具