一次webpack3升级为webpack4的实践

简介: 一次webpack3升级为webpack4的实践

image.png

这次webpack升级提升了不少构建速度:生产打包提升了30%;开发构建提升40%,开发热更新提升70%

之前尝试过一些在webpack3的基础上做的构建优化,例如引入HappyPack优化构建速度,开启loader缓存和优化包查找路径等等,详情可以查看前端webpack构建优化

但是随着时间的推移,这种优化产生的效果越来越弱化,手上的项目体积越来越大,对本地开发热更新速度和生产打包发布速度都有了很大的影响。

webpack3升级到webpack4迫在眉睫,这篇博文将记录一些我在升级过程中遇到的坑。

当你遇到这些坑时,通过搜索引擎找到我这篇文章,如果能够解决了手上的webpack配置问题,然后发自内心的感到 ”Save my day!“,”It helps me!“,”Solved my problem!“,”Works for me!“ ,我会感觉自己的这篇博文很有意义。

  • 升级到 webpack 4
  • 移除 CommonsChunkPlugin,默认使用 SplitChunksPlugin 分割代码
  • 升级 html-webpack-plugin
  • 移除 extract-text-webpack-plugin,引入 mini-css-extract-plugin 并配置 css-loader
  • 配置 mode 属性
  • 升级 vue-loader 到 v14.2.2
  • 更新 HtmlWebpackPlugin 的 chunkSortMode
  • 修复大小限制的报错
  • 重命名 app.js,生成 vendors.js
  • mini-css-extract-plugin 的配置放在 webpack.base.conf.js
  • 调试开发环境可用
  • 引入analyzer分析分析包大小
  • webpack3与webpack4打包对比
  • 文件可以更小一些吗?构建速度可以更快一些吗?
  • 升级vue-loader到v15并且替换happyPack为thread-loader
  • webpack3与webpack4开发依赖对比
  • 总结


升级 webpack 到 4


"webpack": "^3.6.0" -> "webpack": "^4.43.0"


yarn add -D webpack@4.43.0


移除 CommonsChunkPlugin

plugins: [
  // // split vendor js into its own file
  // new webpack.optimize.CommonsChunkPlugin({
  //   name: 'vendor',
  // }),
  // // extract webpack runtime and module manifest to its own file in order to
  // // prevent vendor hash from being updated whenever app bundle is updated
  // new webpack.optimize.CommonsChunkPlugin({
  //   name: 'manifest',
  //   minChunks: Infinity,
  // }),
  // // This instance extracts shared chunks from code splitted chunks and bundles them
  // // in a separate chunk, similar to the vendor chunk
  // // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
  // new webpack.optimize.CommonsChunkPlugin({
  //   name: 'app',
  //   async: 'vendor-async',
  //   children: true,
  //   minChunks: 3,
  // }),
];


升级 html-webpack-plugin


"html-webpack-plugin": "^2.30.1" -> "html-webpack-plugin": "^4.3.0"

// https://stackoverflow.com/questions/49942558/deprecationwarning-tapable-plugin-is-deprecated-use-new-api-on-hooks-instea
// error
Tapable.apply is deprecated. Call apply on the plugin directly instead
yarn add -D html-webpack-plugin@latest

移除 extract-text-webpack-plugin,引入 mini-css-extract-plugin 并配置 css-loader


// const ExtractTextPlugin = require('extract-text-webpack-plugin');
// plugins:[
// extract css into its own file
// new ExtractTextPlugin({
//   filename: utils.assetsPath('css/[name].[contenthash].css'),
//   // Setting the following option to `false` will not extract CSS from codesplit chunks.
//   // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by
//   // webpack. It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit
//   // bundle as well when it's `false`, increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
//   allChunks: true,
// }),
// ]
// extract: true
// if (options.extract) {
//   return ExtractTextPlugin.extract({
//     use: loaders,
//     fallback: 'vue-style-loader',
//   });
// }


yarn add -D mini-css-extract-plugin


// webpack.prod.conf.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
plugins: [
    new MiniCssExtractPlugin(filename: utils.assetsPath('css/[name].[contenthash].css'))
];
// webpack.base.conf.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              hmr: process.env.NODE_ENV === "development",
            },
          },
          "css-loader",
          "postcss-loader",
          "sass-loader",
        ],
      },
    ],
  },
};


配置 mode 属性

The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.


mode: "production";


升级 vue-loader


"vue-loader": "^13.3.0" -> "vue-loader": "14.2.2"

TypeError: Cannot read property ' vueOptions' of undefined


yarn add -D vue-loader@latest


vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config


// https://github.com/symfony/webpack-encore/issues/311
You probably use vue-loader v15 which was released yesterday and introduces a lot of changes compared to v14. One of these changes is that you have to use an extra plugin: VueLoaderPlugin (that's not handled yet by Encore).
In the meantime could you try removing your version of the vue-loader/VueLoaderPlugin and adding vue-loader@^14.2.2 instead?


yarn add -D vue-loader@14.2.2
(1:1) Unknown word
> 1 | // extracted by mini-css-extract-plugin


移除 postcss-loader。


// postcss: generateLoaders()


更新 HtmlWebpackPlugin 的 chunkSortMode


// https://www.cnblogs.com/wyliunan/p/10238717.html
Unhandled rejection Error: "dependency" is not a valid chunk sort mode


设置为 HtmlWebpackPlugin 的 chunkSortMode 为"auto": https://github.com/jantimon/h...


修复大小限制的报错


AssetsOverSizeLimitWarning: asset size limit: The following asset(s) exceed the recommended size limit (244 KiB 250000Byte).This can impact web performance.


// webpack.config.js
module.exports = {
  performance: {
    hints: "warning",
    maxEntrypointSize: 5000 * 1024,
    maxAssetSize: 5000 * 1024,
  },
};

生成 manifest.js,生成 vendors.js


// https://webpack.js.org/configuration/optimization/#optimizationsplitchunks
// 生成manifest.js
optimization: {
    runtimeChunk: {
        name:'manifest'
    }
},


// https://webpack.js.org/plugins/split-chunks-plugin/#split-chunks-example-1
// 生成 vendors.js
optimization: {
  splitChunks: {
    cacheGroups: {
      commons: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendors',
        chunks: 'all',
      },
    },
  },
}
  output: {
-   chunkFilename: utils.assetsPath('js/[id].[chunkhash].js'),
+   chunkFilename: utils.assetsPath('js/[name].[chunkhash].js'),
  },


调试开发环境可用

Error: Cannot find module 'webpack/bin/config-yargs'
https://github.com/mzgoddard/jest-webpack/issues/27
"webpack-cli": "^2.1.3",
"webpack-dev-server": "^3.1.4"
mode: 'development',
// webpack Error: Callback was already called.
// https://github.com/webpack-contrib/mini-css-extract-plugin/issues/493
// webpack.dev.js
plugins:[
    new MiniCssExtractPlugin(),
]
// https://segmentfault.com/q/1010000012054980
// BaseClient.js:12 Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'

安装transform-es2015-modules-commonjs并且在.babelrc中配置。


yarn add -D transform-es2015-modules-commonjs
// .babelrc
"plugins": [
    "transform-es2015-modules-commonjs"
]


引入analyzer分析分析包大小

// package.json
scripts:{
  "build:analyse": "NODE_ENV=production source_map=false npm_config_report=true node build/build.js"
}
// webpack.prod.conf.js
if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  webpackConfig.plugins.push(new BundleAnalyzerPlugin());
}


webpack3与webpack4打包对比


版本 文件大小(Parsed) 文件大小(Gzipped) chunk数 生产构建时间 开发构建时间 开发热更新体感
webpack3.6.0 6.09MB 1.76MB 73 52196ms 70103ms 慢(12079ms)
webpack4.43.0 7.07MB 1.98MB 88 40727ms 45448ms 快(3394ms)

机器参数:

MacBook Pro (15-inch, 2019)

处理器 2.3 GHz Intel Core i9

内存 16 GB 2400 MHz DDR4


文件可以更小一些吗?构建速度可以更快一些吗?


  • 未使用TerserPlugin而是用UglifyjsPlugin
  • OptimizeCSSPlugin位置放错
  • 如果手动配置splitChunks的话,一定要把没有配置的参数也配置上
  • devtool由最慢的“source-map”改为false

引入TerserPlugin的话,需要首先升级node到v10.17.0+。

sudo n v10.17.0
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
      // Compress extracted CSS. We are using this plugin so that possible
      // duplicated CSS from different presentation can be deduped.
      new OptimizeCSSPlugin({
        cssProcessorOptions: config.build.productionSourceMap ? { safe: true, map: { inline: false } } : { safe: true },
      }),
      new TerserPlugin({
        cache: true,
        parallel: true,
        sourceMap: Boolean(config.build.productionSourceMap),
      }),
    ],
  }
}

增加下面的配置:

optimization: {
  splitChunks: {
    chunks: 'async',
    minSize: 30000,
    maxSize: 0,
    minChunks: 1,
    maxAsyncRequests: 5,
    maxInitialRequests: 3,
    automaticNameDelimiter: '~',
    automaticNameMaxLength: 30,
  }
}

现在的webpack3和webpack4打包分析:


版本 文件大小(Parsed) 文件大小(Gzipped) chunk数 生产构建时间 开发构建时间 开发热更新体感
webpack3.6.0 6.09MB 1.76MB 73 52196ms 70103ms 慢(12079ms)
webpack4.43.0(优化前) 7.07MB 1.98MB 88 40727ms 45448ms 快(3394ms)
webpack4.43.0(优化后) 7.02MB 1.98MB 88 34585ms 45448ms 快(3394ms)

通过对比发现,提升了大概5秒的打包速度。


升级vue-loader到v15并且引入thread-loader加速vue-loader

warning No parser and no filepath given, using 'babel' the parser now but this will throw an error in the future. Please specify a parser or a filepath so one can be inferred

为什么引入thread-loader加速vue-loader?

因为HappyPack无法加速vue-loader15。

https://github.com/vuejs/vue-...

yyx990803:vue-loader 15 does not support HappyPack. Use thread-loader instead.

顺便升级eslint-loader到4。

"eslint-loader": "^1.7.1"->"eslint-loader": "^4.0.2"


  // plugins: [
  //   new HappyPack({
  //     id: 'happy-eslint-loader',
  //     threadPool: happyThreadPool,
  //     loaders: ['eslint-loader?cacheDirectory=true'],
  //   }),
  //   new HappyPack({
  //     id: 'happy-vue-loader',
  //     threadPool: happyThreadPool,
  //     loaders: ['vue-loader?cacheDirectory=true'],
  //   }),
  //   new HappyPack({
  //     id: 'happy-babel-loader',
  //     threadPool: happyThreadPool,
  //     loaders: ['babel-loader?cacheDirectory=true'],
  //   }),
  // ]


rules: [
  {
    test: /\.(js|vue)$/,
    use: [
      { loader: 'thread-loader' },
      {
        loader: 'eslint-loader',
        options: {
          formatter: require('eslint-friendly-formatter'),
          emitWarning: !config.dev.showEslintErrorsInOverlay,
        },
      },
    ],
    enforce: 'pre',
    include: [resolve('src'), resolve('test')],
  },
  {
    test: /\.vue$/,
    use: ['thread-loader', 'vue-loader'],
    exclude: (file) => /node_modules/.test(file) && !/\.vue\.js/.test(file),
  },
  {
    test: /\.js$/,
    use: ['thread-loader', 'babel-loader'],
    include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')],
  },
  {
    test: /\.(sa|sc|c)ss$/,
    use: [
      {
        loader: process.env.NODE_ENV === 'development' ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
        options: {
          hmr: process.env.NODE_ENV === 'development',
        },
      },
      'css-loader',
      'postcss-loader',
      'sass-loader',
    ],
  },
]

error:despite it was not able to fulfill desired ordering with these modules:


new MiniCssExtractPlugin({
  ignoreOrder: true,
}),


现在的webpack3和webpack4打包分析:

版本 文件大小(Parsed) 文件大小(Gzipped) chunk数 生产构建时间 开发构建时间 开发热更新体感
webpack3.6.0 6.09MB 1.76MB 73 52196ms 70103ms 慢(12079ms)
webpack4.43.0(优化前) 7.07MB 1.98MB 88 40727ms 45448ms 快(3394ms)
webpack4.43.0(第一次优化) 7.02MB 1.98MB 88 34585ms 45448ms 快(3394ms)
webpack4.43.0(第二次优化) 6.7MB 1.91MB 88 34585ms 41657ms 快(3394ms)


webpack3与webpack4开发依赖对比

// webpack3
"webpack": "^3.6.0"
"webpack-dev-server": "^2.9.1"
"eslint-loader": "^1.7.1"
"vue-loader": "^13.3.0"
"happypack": "^5.0.0"
"html-webpack-plugin": "^2.30.1"
"extract-text-webpack-plugin": "^3.0.0"
"uglifyjs-webpack-plugin": "^1.1.1"
// webpack4
"webpack": "^4.43.0"
"webpack-cli": "^3.3.11"
"webpack-dev-server": "^3.7.2"
"thread-loader": "^2.1.3"
"eslint-loader": "^4.0.2"
"vue-loader": "^15.9.2"
"html-webpack-plugin": "^4.3.0"
"mini-css-extract-plugin": "^0.9.0"
"terser-webpack-plugin": "^3.0.1"
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2"


总结


webpack3到webpack4的升级,主要做了以下这些事情

  • 升级依赖:升级webpack major version为webpack4,升级vue-loader,升级html-webpack-plugin等等
  • 代码分割:移除了CommonsChunkPlugin;引入SplitChunksPlugin,设置mode属性为production,optimization拆分出webpack3中的manifest和vendors
  • 压缩css:移除extract-text-webpack-plugin;引入mini-css-extract-plugin,使用mini-css-extract-plugin的loader重新配置sass-loader,postcss-loader和css-loader,它会为每个包含css的js文件单独构建一个js文件
  • 代码热更新:升级webpack-cli和webpack-dev-server,并且设置mode为development
  • 构建加速:替换happypack为thread-loader,多线程式本地构建和生产构建
    反思
  • 执行力是第一生产力
  • 这次webpack升级提升了不少构建速度:生产打包提升了30%;开发构建提升40%,开发热更新提升70%
  • 这次webpack升级没有减小包大小,有尝试使用tree shaking,但是没有成功,有待在tree shaking上继续做实践
相关文章
|
7月前
|
前端开发
构建工具对比:Webpack与Rollup的前端工程化实践
在现代前端开发中,前端工程化变得愈发重要。本文将对两个常用的构建工具——Webpack和Rollup进行比较,探讨它们在前端工程化实践中的特点、优势和适用场景。无论是大型应用的打包优化还是轻量级库的构建,选择适合的构建工具都能提高开发效率和项目性能。
144 1
|
缓存 资源调度 编译器
原来是这样啊!浅谈webpack4和webpack5的区别
相对于webpack4,webpack5内置了很多plugin插件,比如、打包、压缩、缓存
772 1
|
JavaScript
webpack4主流程源码阅读,以及动手实现一个简单的webpack(二)
webpack4主流程源码阅读,以及动手实现一个简单的webpack
132 0
|
1月前
|
前端开发 JavaScript
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
|
1月前
|
缓存 前端开发 JavaScript
Webpack 4 和 Webpack 5 区别?
【10月更文挑战第23天】随着时间的推移,Webpack 可能会继续发展和演进,未来的版本可能会带来更多的新特性和改进。保持对技术发展的关注和学习,将有助于我们更好地应对不断变化的前端开发环境。
|
2月前
|
前端开发 JavaScript 开发者
构建工具对比:Webpack与Rollup的前端工程化实践
【10月更文挑战第11天】本文对比了前端构建工具Webpack和Rollup,探讨了它们在模块打包、资源配置、构建速度等方面的异同。通过具体示例,展示了两者的基本配置和使用方法,帮助开发者根据项目需求选择合适的工具。
28 3
|
5月前
|
缓存 JSON 前端开发
Webpack打包优化实践
【7月更文挑战第17天】Webpack的打包优化是一个持续的过程,需要开发者根据项目的实际情况选择合适的优化策略。通过拆分代码、压缩代码、使用Tree Shaking、优化加载器配置、利用缓存以及进行性能分析,我们可以有效提升Webpack的打包效率和应用的加载
|
7月前
|
缓存 资源调度 监控
Webpack 5新特性详解与性能优化实践
Webpack 5通过确定性的Chunk ID、模块ID和导出ID实现了长期缓存,这意味着相同的输入将始终产生相同的输出。这样,当你的用户再次访问更新后的网站时,浏览器可以重用旧的缓存,而不是重新下载所有资源。
93 2
|
移动开发 弹性计算 前端开发
Html5和Webpack3:Webpack5的常见用法
本实验将介绍Webpack5的打包工具的一些常见用法
|
7月前
|
前端开发 UED
探索前端工程化之路:Webpack、Rollup等构建工具对比与实践
在现代前端开发中,工程化成为不可或缺的一环。本文将深入探讨常用的前端构建工具Webpack和Rollup,并比较它们在实践中的优劣势。通过对功能、性能、插件生态等方面的评估,帮助读者选择适合自己项目需求的构建工具。
98 1
下一篇
DataWorks