现在,我们只能手工的来调整 mode选项,实现生产环境和开发环境的切换,且很多配置在生产环境和开发环境中存在不一致的情况,比如开发环境没有必要设置缓存,生产环境还需要设置公共路径等等

本章介绍拆分开发环境和生产环境,让打包更灵活

1. 公共路径

publicPath配置选项在各种场景中都非常有用,可以通过它来指定应用程序中所有资源的基础路径

① 不使用公共路径

在使用公共路径之前,打包后的 index.html :

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Webpack5</title>
  8. <link href="/css/bb19a26be8ae85c3b88a.css" rel="stylesheet"></head>
  9. <body>
  10. <script defer src="/js/vendors.6f0475eed10a5b2f3992.js">
  11. </script><script defer src="/js/index.9c58ba4a38a06ada3563.js"></script>
  12. </body>
  13. </html>

发现 script 、link 标签上引用的都是相对路径

② 使用公共路径

然后我们使用公共路径:配置output.publicPath

  1. output: {
  2. filename: 'js/[name].[contenthash].js',
  3. path: path.resolve(__dirname, './dist'),
  4. // 清除上一次打包,但这一次打包不需要的文件
  5. clean: true,
  6. // 公共路径:用于指定所有资源的基础路径
  7. publicPath: 'http://localhost:8080/'
  8. },

查看打包后的 index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Webpack5</title>
  8. <link href="http://localhost:8080/css/bb19a26be8ae85c3b88a.css" rel="stylesheet"></head>
  9. <body>
  10. <script defer src="http://localhost:8080/js/vendors.6f0475eed10a5b2f3992.js"></script>
  11. <script defer src="http://localhost:8080/js/index.9c58ba4a38a06ada3563.js"></script>
  12. </body>
  13. </html>

发现 script 、link 标签上的引用路径都带有我们设置的公共路径publicPath

③ 基于环境设置公共路径

在开发环境中,我们通常有一个 assets/ 文件夹,它与索引页面位于同一级别。这没太大问题,但是,如果我们将所有静态资源托管至 CDN,然后想在生产环境中使用呢?
想要解决这个问题,可以直接使用一个环境变量(environment variable),而且我们还可以结合 webpack 的内置插件webpack.DefinePlugin帮助我们在代码中安全的使用环境变量

  1. const webpack = require('webpack')
  2. // 根据环境的不同,修改文件的路径地址
  3. process.env.ASSET_PATH = 'http://localhost:8080/'
  4. // 尝试使用环境变量,否则使用根路径
  5. const ASSET_PATH = process.env.ASSET_PATH || '/';
  6. module.exports = {
  7. output: {
  8. filename: 'js/[name].[contenthash].js',
  9. path: path.resolve(__dirname, './dist'),
  10. clean: true,
  11. // 公共路径:用于指定所有资源的基础路径
  12. publicPath: ASSET_PATH
  13. },
  14. plugins: [
  15. // 这可以帮助我们在代码中安全地使用环境变量
  16. new webpack.DefinePlugin({
  17. 'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH)
  18. })
  19. ]
  20. }

④ Automatic publicPath

有可能你事先不知道publicPath是什么,webpack 会自动根据import.meta.urldocument.currentScripscript.src或者self.location变量设置publicPath
你需要做的是将output.publicPath设为'auto'

  1. module.exports = {
  2. output: {
  3. publicPath: 'auto'
  4. }
  5. }

请注意在某些情况下不支持document.currentScript,例如:IE 浏览器,你不得不引入一个 polyfill,例如currentScript Polyfill

2. 环境变量

想要消除webpack.config.js在 开发环境 和 生产环境 之间的差异,需要环境变量

在使用环境变量之前,对于我们的webpack配置,有一个必须要修改之处。通常,module.exports指向配置对象。要使用env变量,你必须将module.exports转换成一个函数:

  1. const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
  2. // 尝试使用环境变量,否则使用根路径
  3. process.env.ASSET_PATH = 'http://localhost:8080/'
  4. const ASSET_PATH = process.env.ASSET_PATH || '/'
  5. module.exports = (env) => {
  6. return {
  7. entry: {
  8. index: './src/js/index.js',
  9. },
  10. output: {
  11. filename: 'js/[name].[contenthash].js',
  12. path: path.resolve(__dirname, './dist'),
  13. clean: true,
  14. publicPath: ASSET_PATH
  15. },
  16. mode: 'development',
  17. optimization: {
  18. minimizer: [
  19. new CssMinimizerWebpackPlugin()
  20. ]
  21. }
  22. }
  23. }

webpack命令行环境配置的--env参数,可以允许你传入任意数量的环境变量。而在webpack.config.js中可以访问到这些环境变量。例如,--env production--env goal=local
修改mode:

  1. // 根据命令行参数 env 来设置不同环境的 mode
  2. mode: env.production ? 'production' : 'development',

npx webpack --env production
然后控制台就会发出警告提醒你是在开发环境下打包的。接着我们观察 dist/js 文件夹下的 js 文件,发现它们并没有被压缩:
第十章  拆分开发环境和生产环境配置 - 图1
这里webpack有个开箱即用的插件terser可以使 js 文件中的代码压缩,但是这里为什么webpack这个开箱即用的功能没有生效呢?原因是我们配置了optimization.minimizer,做了一个 CSS 压缩。如果我们做了这个配置以后,那么我们需要单独配置一下terser
npm i terser-webpack-plugin

  1. const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
  2. // 1.引用
  3. const TerserWebpackPlugin = require('terser-webpack-plugin')
  4. module.exports = (env) => {
  5. return {
  6. optimization: {
  7. minimizer: [
  8. new CssMinimizerWebpackPlugin(),
  9. // 2.使用
  10. new TerserWebpackPlugin()
  11. ]
  12. }
  13. }
  14. }

执行npx webpack --env production后查看 dist/js 中的 js 文件:
image.png
发现生产环境下代码已压缩

再执行npx webpack --env development后查看 dist/js 中的 js 文件:
image.png
发现开发环境下代码不会被压缩

这就是我们使用环境变量的意义

3. 拆分配置文件

目前,生产环境和开发环境使用的是一个配置文件,mode: env.production ? 'production' : 'development'是通过三元运算符来区分生产环境和开发环境的,那么我们所有的属性都通过三元运算符来区分生产环境和开发环境,那未免也太麻烦了。所以我们需要拆分一下webpack.config.js文件

在项目根目录下创建 config 文件夹,在其中创建两个文件:webpack.config.dev.js(开发环境的配置文件) 和webpack.config.prod.js(生产环境的配置文件)

webpack.config.js的配置如下:

  1. const path = require('path')
  2. const HtmlWebpackPlugin = require('html-webpack-plugin')
  3. const MiniCssExtractPlugin = require('mini-css-extract-plugin')
  4. const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
  5. const TerserWebpackPlugin = require('terser-webpack-plugin')
  6. const svgToMiniDataURI = require('mini-svg-data-uri')
  7. const toml = require('toml')
  8. const yaml = require('yaml')
  9. const json5 = require('json5')
  10. const webpack = require('webpack')
  11. // 尝试使用环境变量,否则使用根路径
  12. process.env.ASSET_PATH = 'http://localhost:8080/'
  13. const ASSET_PATH = process.env.ASSET_PATH || '/';
  14. module.exports = (env) => {
  15. return {
  16. // 设置项目打包的入口文件
  17. entry: {
  18. index: './src/js/index.js',
  19. // anthor: './src/js/another-module.js'
  20. },
  21. // 设置项目打包的出口文件
  22. output: {
  23. // 指定输出文件的文件名
  24. filename: 'js/[name].[contenthash].js',
  25. // 指定文件的输出路径(只能是绝对路径)
  26. path: path.resolve(__dirname, './dist'),
  27. // 清除上一次打包,但这一次打包不需要的文件
  28. clean: true,
  29. // 定义输出文件的文件名(优先级小于generator)
  30. // assetModuleFilename: 'img/[contenthash][ext][query]',
  31. // 公共路径:用于指定所有资源的基础路径
  32. publicPath: ASSET_PATH
  33. },
  34. // 根据命令行参数 env 来设置不同环境的 mode
  35. mode: env.production ? 'production' : 'development',
  36. // 能够准确的捕获代码方式错误的位置
  37. // inline-source-map:在开发模式下追踪代码(mode: 'development',)
  38. devtool: 'inline-source-map',
  39. // 插件
  40. plugins: [
  41. new HtmlWebpackPlugin({
  42. // 指定哪个html文件为模板
  43. template: './src/index.html',
  44. // 指定输出文件的文件名
  45. filename: 'index.html',
  46. // 指定生成的 script 标签在 head 中,还是在 body 中
  47. inject: 'body'
  48. }),
  49. new MiniCssExtractPlugin({
  50. filename: 'css/[contenthash].css'
  51. }),
  52. // 这可以帮助我们在代码中安全地使用环境变量
  53. new webpack.DefinePlugin({
  54. 'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH)
  55. })
  56. ],
  57. // 修改代码后实现浏览器热更新(结合 npx webpack-dev-server 命令开启本地服务器一起使用)
  58. devServer: {
  59. static: './dist'
  60. },
  61. // 配置资源文件
  62. module: {
  63. rules: [
  64. {
  65. test: /\.js$/,
  66. // node_modules 中的代码无需编译
  67. exclude: /node_modules/,
  68. use: {
  69. loader: 'babel-loader',
  70. options: {
  71. // 通过 presets 设置预设
  72. presets: ['@babel/preset-env'],
  73. plugins: [
  74. ['@babel/plugin-transform-runtime']
  75. ]
  76. }
  77. },
  78. },
  79. {
  80. // 校验文件格式
  81. test: /\.jpg$/,
  82. // 资源模块类型
  83. type: 'asset/resource',
  84. // 定义输出文件的文件名
  85. generator: {
  86. filename: 'img/[contenthash][ext][query]'
  87. }
  88. },
  89. {
  90. test: /\.svg$/,
  91. // 注:inline资源模块类型不会导出文件
  92. type: 'asset/inline',
  93. // 自定义data url
  94. generator: {
  95. dataUrl: context => {
  96. return svgToMiniDataURI(context.toString())
  97. }
  98. }
  99. },
  100. {
  101. test: /\.txt$/,
  102. // 注:source资源模块类型不会导出文件
  103. type: 'asset/source'
  104. },
  105. {
  106. test: /\.(woff|woff2|eot|ttf|otf)$/,
  107. type: 'asset/resource'
  108. },
  109. {
  110. test: /\.png$/,
  111. type: 'asset',
  112. generator: {
  113. filename: 'img/[contenthash][ext][query]'
  114. },
  115. parser: {
  116. dataUrlCondition: {
  117. // 默认4*1024(4kb)
  118. maxSize: 4*1024
  119. }
  120. }
  121. },
  122. {
  123. test: /\.(css|less)$/,
  124. // 抽离 css 使用插件 MiniCssExtractPlugin 自身的 loader
  125. use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
  126. },
  127. {
  128. test: /\.(tsv|csv)$/,
  129. use: 'csv-loader'
  130. },
  131. {
  132. test: /\.xml$/,
  133. use: 'xml-loader'
  134. },
  135. {
  136. test: /\.toml$/,
  137. type: 'json',
  138. parser: {
  139. parse: toml.parse
  140. }
  141. },
  142. {
  143. test: /\.yaml$/,
  144. type: 'json',
  145. parser: {
  146. parse: yaml.parse
  147. }
  148. },
  149. {
  150. test: /\.json5$/,
  151. type: 'json',
  152. parser: {
  153. parse: json5.parse
  154. }
  155. }
  156. ]
  157. },
  158. // 优化配置
  159. optimization: {
  160. minimizer: [
  161. new CssMinimizerWebpackPlugin(),
  162. new TerserWebpackPlugin()
  163. ],
  164. splitChunks: {
  165. // 缓存组
  166. cacheGroups: {
  167. vendor: {
  168. // 第三方库的文件都在 node_modules 文件夹里
  169. test: /[\\/]node_modules[\\/]/,
  170. name: 'vendors',
  171. chunks: 'all'
  172. }
  173. }
  174. }
  175. }
  176. }
  177. }

webpack.config.dev.js的配置如下:

  1. const path = require('path')
  2. const HtmlWebpackPlugin = require('html-webpack-plugin')
  3. const MiniCssExtractPlugin = require('mini-css-extract-plugin')
  4. const svgToMiniDataURI = require('mini-svg-data-uri')
  5. const toml = require('toml')
  6. const yaml = require('yaml')
  7. const json5 = require('json5')
  8. module.exports = {
  9. entry: {
  10. index: './src/js/index.js',
  11. },
  12. output: {
  13. // 开发环境中不需要缓存,所以不用[name].[contexthash].js
  14. filename: 'js/[name].js',
  15. // 目录路径修改为上一级 ../dist,否则打包的dist目录会在config文件夹下而不是项目根目录下
  16. path: path.resolve(__dirname, '../dist'),
  17. clean: true,
  18. assetModuleFilename: 'other/[contenthash][ext][query]',
  19. // 开发环境不需要publicPath
  20. },
  21. mode: 'development',
  22. devtool: 'inline-source-map',
  23. devServer: {
  24. static: path.resolve(__dirname, '../dist')
  25. },
  26. plugins: [
  27. new HtmlWebpackPlugin({
  28. template: './src/index.html',
  29. filename: 'index.html',
  30. inject: 'head'
  31. }),
  32. new MiniCssExtractPlugin({
  33. filename: 'css/[contenthash].css'
  34. })
  35. ],
  36. module: {
  37. rules: [
  38. {
  39. test: /\.js$/,
  40. exclude: /node_modules/,
  41. use: {
  42. loader: 'babel-loader',
  43. options: {
  44. presets: ['@babel/preset-env'],
  45. plugins: [
  46. ['@babel/plugin-transform-runtime']
  47. ]
  48. }
  49. },
  50. },
  51. {
  52. test: /\.jpg$/,
  53. type: 'asset/resource',
  54. generator: {
  55. filename: 'img/[contenthash][ext][query]'
  56. }
  57. },
  58. {
  59. test: /\.svg$/,
  60. type: 'asset/inline',
  61. generator: {
  62. dataUrl: context => {
  63. return svgToMiniDataURI(context.toString())
  64. }
  65. }
  66. },
  67. {
  68. test: /\.txt$/,
  69. type: 'asset/source'
  70. },
  71. {
  72. test: /\.(woff|woff2|eot|ttf|otf)$/,
  73. type: 'asset/resource'
  74. },
  75. {
  76. test: /\.png$/,
  77. type: 'asset',
  78. generator: {
  79. filename: 'img/[contenthash][ext][query]'
  80. },
  81. parser: {
  82. dataUrlCondition: {
  83. maxSize: 4*1024
  84. }
  85. }
  86. },
  87. {
  88. test: /\.(css|less)$/,
  89. use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
  90. },
  91. {
  92. test: /\.(tsv|csv)$/,
  93. use: 'csv-loader'
  94. },
  95. {
  96. test: /\.xml$/,
  97. use: 'xml-loader'
  98. },
  99. {
  100. test: /\.toml$/,
  101. type: 'json',
  102. parser: {
  103. parse: toml.parse
  104. }
  105. },
  106. {
  107. test: /\.yaml$/,
  108. type: 'json',
  109. parser: {
  110. parse: yaml.parse
  111. }
  112. },
  113. {
  114. test: /\.json5$/,
  115. type: 'json',
  116. parser: {
  117. parse: json5.parse
  118. }
  119. }
  120. ]
  121. },
  122. optimization: {
  123. // 开发环境不需要压缩,所以不需要minimizer
  124. splitChunks: {
  125. cacheGroups: {
  126. vendor: {
  127. test: /[\\/]node_modules[\\/]/,
  128. name: 'vendors',
  129. chunks: 'all'
  130. }
  131. }
  132. }
  133. }
  134. }

webpack.config.prod.js的配置如下:

  1. const path = require('path')
  2. const HtmlWebpackPlugin = require('html-webpack-plugin')
  3. const MiniCssExtractPlugin = require('mini-css-extract-plugin')
  4. const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
  5. const TerserWebpackPlugin = require('terser-webpack-plugin')
  6. const svgToMiniDataURI = require('mini-svg-data-uri')
  7. const toml = require('toml')
  8. const yaml = require('yaml')
  9. const json5 = require('json5')
  10. module.exports = {
  11. entry: {
  12. index: './src/js/index.js',
  13. },
  14. output: {
  15. // 生产环境需要缓存 [contexthash]
  16. filename: 'js/[name].[contenthash].js',
  17. // 修改成 ../dist
  18. path: path.resolve(__dirname, '../dist'),
  19. clean: true,
  20. assetModuleFilename: 'other/[contenthash][ext][query]',
  21. // 生产环境需要publicPath
  22. publicPath: 'http://localhost:8080/'
  23. },
  24. mode: 'production',
  25. // 开发环境不需要 devtool 和 devServer
  26. plugins: [
  27. new HtmlWebpackPlugin({
  28. template: './src/index.html',
  29. filename: 'index.html',
  30. inject: 'head'
  31. }),
  32. new MiniCssExtractPlugin({
  33. filename: 'css/[contenthash].css'
  34. })
  35. ],
  36. module: {
  37. rules: [
  38. {
  39. test: /\.js$/,
  40. exclude: /node_modules/,
  41. use: {
  42. loader: 'babel-loader',
  43. options: {
  44. presets: ['@babel/preset-env'],
  45. plugins: [
  46. ['@babel/plugin-transform-runtime']
  47. ]
  48. }
  49. },
  50. },
  51. {
  52. test: /\.jpg$/,
  53. type: 'asset/resource',
  54. generator: {
  55. filename: 'img/[contenthash][ext][query]'
  56. }
  57. },
  58. {
  59. test: /\.svg$/,
  60. type: 'asset/inline',
  61. generator: {
  62. dataUrl: context => {
  63. return svgToMiniDataURI(context.toString())
  64. }
  65. }
  66. },
  67. {
  68. test: /\.txt$/,
  69. type: 'asset/source'
  70. },
  71. {
  72. test: /\.(woff|woff2|eot|ttf|otf)$/,
  73. type: 'asset/resource'
  74. },
  75. {
  76. test: /\.png$/,
  77. type: 'asset',
  78. generator: {
  79. filename: 'img/[contenthash][ext][query]'
  80. },
  81. parser: {
  82. dataUrlCondition: {
  83. maxSize: 4*1024
  84. }
  85. }
  86. },
  87. {
  88. test: /\.(css|less)$/,
  89. use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
  90. },
  91. {
  92. test: /\.(tsv|csv)$/,
  93. use: 'csv-loader'
  94. },
  95. {
  96. test: /\.xml$/,
  97. use: 'xml-loader'
  98. },
  99. {
  100. test: /\.toml$/,
  101. type: 'json',
  102. parser: {
  103. parse: toml.parse
  104. }
  105. },
  106. {
  107. test: /\.yaml$/,
  108. type: 'json',
  109. parser: {
  110. parse: yaml.parse
  111. }
  112. },
  113. {
  114. test: /\.json5$/,
  115. type: 'json',
  116. parser: {
  117. parse: json5.parse
  118. }
  119. }
  120. ]
  121. },
  122. optimization: {
  123. // 开发环境需要压缩 CSS 和 JS
  124. minimizer: [
  125. new CssMinimizerWebpackPlugin(),
  126. new TerserWebpackPlugin()
  127. ],
  128. splitChunks: {
  129. cacheGroups: {
  130. vendor: {
  131. test: /[\\/]node_modules[\\/]/,
  132. name: 'vendors',
  133. chunks: 'all'
  134. }
  135. }
  136. }
  137. }
  138. }

拆分成两个配置文件后,分别运行这两个文件:

  1. 开发环境:

npx webpack -c ./config/webpack.config.dev.js
观察 dist ,发现文件名上没有 hash 字符串,以及 index.html 中的引用也没有了缓存,CSS 文件夹中的代码也没有压缩
image.png

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Webpack5</title>
  8. <script defer src="js/vendors.js"></script>
  9. <script defer src="js/index.js"></script>
  10. <link href="css/a4f4120816239f5386dd.css" rel="stylesheet">
  11. </head>
  12. <body>
  13. </body>
  14. </html>

image.png
可以在开发环境中启动服务:
npx webpack serve -c ./config/webpack.config.dev.js

  1. 生产环境

npx webpack -c ./config/webpack.config.prod.js
观察 dist ,发现文件名上有 hash 字符串,以及 index.html 中的引用也有了缓存,CSS 文件夹中的代码也压缩了
image.png

4. 提取公共配置

在此之前,我们已经将webpack.config.js抽离成webpack.config.dev.jswebpack.config.prod.js,但是这两个文件中含有很多重复代码,我们可以手动的将这些重复的代码单独提取到一个文件里

在 config 文件夹下创建webpack.config.common.js,用来存放重复代码:

  1. const path = require('path')
  2. const HtmlWebpackPlugin = require('html-webpack-plugin')
  3. const MiniCssExtractPlugin = require('mini-css-extract-plugin')
  4. const svgToMiniDataURI = require('mini-svg-data-uri')
  5. const toml = require('toml')
  6. const yaml = require('yaml')
  7. const json5 = require('json5')
  8. module.exports = {
  9. entry: {
  10. index: './src/js/index.js',
  11. },
  12. output: {
  13. path: path.resolve(__dirname, './dist'),
  14. clean: true,
  15. assetModuleFilename: 'other/[contenthash][ext][query]',
  16. },
  17. plugins: [
  18. new HtmlWebpackPlugin({
  19. template: './src/index.html',
  20. filename: 'index.html',
  21. inject: 'head'
  22. }),
  23. new MiniCssExtractPlugin({
  24. filename: 'css/[contenthash].css'
  25. })
  26. ],
  27. module: {
  28. rules: [
  29. {
  30. test: /\.js$/,
  31. exclude: /node_modules/,
  32. use: {
  33. loader: 'babel-loader',
  34. options: {
  35. presets: ['@babel/preset-env'],
  36. plugins: [
  37. ['@babel/plugin-transform-runtime']
  38. ]
  39. }
  40. },
  41. },
  42. {
  43. test: /\.jpg$/,
  44. type: 'asset/resource',
  45. generator: {
  46. filename: 'img/[contenthash][ext][query]'
  47. }
  48. },
  49. {
  50. test: /\.svg$/,
  51. type: 'asset/inline',
  52. generator: {
  53. dataUrl: context => {
  54. return svgToMiniDataURI(context.toString())
  55. }
  56. }
  57. },
  58. {
  59. test: /\.txt$/,
  60. type: 'asset/source'
  61. },
  62. {
  63. test: /\.(woff|woff2|eot|ttf|otf)$/,
  64. type: 'asset/resource'
  65. },
  66. {
  67. test: /\.png$/,
  68. type: 'asset',
  69. generator: {
  70. filename: 'img/[contenthash][ext][query]'
  71. },
  72. parser: {
  73. dataUrlCondition: {
  74. maxSize: 4*1024
  75. }
  76. }
  77. },
  78. {
  79. test: /\.(css|less)$/,
  80. use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
  81. },
  82. {
  83. test: /\.(tsv|csv)$/,
  84. use: 'csv-loader'
  85. },
  86. {
  87. test: /\.xml$/,
  88. use: 'xml-loader'
  89. },
  90. {
  91. test: /\.toml$/,
  92. type: 'json',
  93. parser: {
  94. parse: toml.parse
  95. }
  96. },
  97. {
  98. test: /\.yaml$/,
  99. type: 'json',
  100. parser: {
  101. parse: yaml.parse
  102. }
  103. },
  104. {
  105. test: /\.json5$/,
  106. type: 'json',
  107. parser: {
  108. parse: json5.parse
  109. }
  110. }
  111. ]
  112. },
  113. optimization: {
  114. splitChunks: {
  115. cacheGroups: {
  116. vendor: {
  117. test: /[\\/]node_modules[\\/]/,
  118. name: 'vendors',
  119. chunks: 'all'
  120. }
  121. }
  122. }
  123. }
  124. }

修改webpack.config.dev.js

  1. module.exports = {
  2. output: {
  3. filename: 'js/[name].js',
  4. },
  5. mode: 'development',
  6. devtool: 'inline-source-map',
  7. devServer: {
  8. static: '../dist'
  9. }
  10. }

修改webpack.config.prod.js

  1. const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
  2. const TerserWebpackPlugin = require('terser-webpack-plugin')
  3. module.exports = {
  4. output: {
  5. filename: 'js/[name].[contenthash].js',
  6. publicPath: 'http://localhost:8080/'
  7. },
  8. mode: 'production',
  9. optimization: {
  10. minimizer: [
  11. new CssMinimizerWebpackPlugin(),
  12. new TerserWebpackPlugin()
  13. ]
  14. }
  15. }

代码抽离完后需要进行三个文件的合并,而且是需要一个所谓对象的深合并(merge),我们可以使用webpack-merge来完成这个功能

5. 合并配置文件

配置文件拆分好后,新的问题来了,如何保证配置合并没用问题呢?webpack-merge工具可以完美解决这个问题

可以先去 npm 官网查看这个工具的用法:https://www.npmjs.com/package/webpack-merge

① 安装依赖
npm i webpack-merge
② 在 config 文件夹下新建文件webpack.config.js,合并代码:

  1. const { merge } = require('webpack-merge')
  2. const commonConfig = require('./webpack.config.common.js')
  3. const productionConfig = require('./webpack.config.prod.js')
  4. const developmentConfig = require('./webpack.config.dev')
  5. module.exports = (env) => {
  6. switch(true) {
  7. case env.development: return merge(commonConfig, developmentConfig);break;
  8. case env.production: return merge(commonConfig, productionConfig);break;
  9. default: throw new Error('No matching configuration was found!');
  10. }
  11. }

至此,项目文件目录如下:
image.png

6. npm 脚本

配置package.json中的script,配置 npm 脚本来简化命令行的输入,这时可以省略 npx :

  "scripts": {
    "serve": "webpack serve -c ./config/webpack.config.js --env development ",
    "build": "webpack -c ./config/webpack.config.js --env production"
  }

现在就可以直接在控制台执行命令了:

npm run serve
npm run build