webpack3升级为webpack4的实践

时间: 2020-04-19阅读: 69标签: webpack
这次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.06.09MB1.76MB7352196ms70103ms慢(12079ms)
webpack4.43.07.07MB1.98MB8840727ms45448ms快(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.06.09MB1.76MB7352196ms70103ms慢(12079ms)
webpack4.43.0(优化前)7.07MB1.98MB8840727ms45448ms快(3394ms)
webpack4.43.0(优化后)7.02MB1.98MB8834585ms45448ms快(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.06.09MB1.76MB7352196ms70103ms慢(12079ms)
webpack4.43.0(优化前)7.07MB1.98MB8840727ms45448ms快(3394ms)
webpack4.43.0(第一次优化)7.02MB1.98MB8834585ms45448ms快(3394ms)
webpack4.43.0(第二次优化)6.7MB1.91MB8834585ms41657ms快(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

反思

  • 执行力是第一生产力
  • 这次webpack升级提升了不少构建速度:生产打包提升了30%;开发构建提升40%,开发热更新提升70%
  • 这次webpack升级没有减小包大小,有尝试使用tree shaking,但是没有成功,有待在tree shaking上继续做实践
期待和大家交流,共同进步,欢迎大家加入我创建的与前端开发密切相关的技术讨论小组:

站长推荐

1.阿里云: 本站目前使用的是阿里云主机,安全/可靠/稳定。点击领取2000元代金券、了解最新阿里云产品的各种优惠活动点击进入

2.腾讯云: 提供云服务器、云数据库、云存储、视频与CDN、域名等服务。腾讯云各类产品的最新活动,优惠券领取点击进入

3.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/9073

让IDE识别webpack的别名alias

许多项目脚手架默认就会把src目录添加一个@别名,项目中实际引入时,虽然可以精简路径,但也带来一个很麻烦的问题:IDE无法识别这些别名,因此导致无法自动完成路径、无法识别引用资源的输出、出现不必要的告警等情况。

最全的webpack.base.config.js文件中文注释

引入utils文件,此处主要用到了assetsPath()方法,用来根据开发模式或生产模式来使用config文件下的相应配置,引入config文件夹下index.js,使用打包或开发配置

Webpack中如何编写一个自定义的loader

Webpack中loader是一个CommonJs风格的函数,接收输入的源码,通过同步或异步的方式替换源码后进行输出。需要注意的是,该导出函数必须使用function,不能使用箭头函数,因为loader编写过程中会经常使用到this访问选项和其他方法。

webpack 中 import css 文件报错: Module build failed...

项目中引入了css文件,配置了 rules 中:在执行 webpack 命令时,发生错误,报错信息:Module build failed (from ./node_modules/css-loader/dist/cjs.js):CssSyntaxError,经查询,是loader顺序不正确导致此问题,修改后如下:

webpack 阶段回顾 之 webpack-dev-server

webpack-dev-server是一个让我们可以模拟线上环境进行项目调试的工具,主要功能有:路径重定向、浏览器中显示编译错误、接口代理 如解决跨域、热更新

《深入浅出webpack》有感

nodejs的出现对于构建工具具有重要的意义,在没有nodejs之前,js只能执行在浏览器环境下,所以意味着对发布前的js文件要进行处理,十分局限,没有打包工具,只能用PHP脚本来处理文件

webpack是什么?

webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle

webpack的optimization.namedModules

optimization.namedModules 这个配置对于调试起来很方便,比如原本生产的一段代码是长这样的:很多奇怪的数字,比如最后几行的 [0,0] 是表示了什么意思?如果配置上namedModules 为true呢

为什么要用webpack?

现今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包。模块化,让我们可以把复杂的程序细化为小的文件;

webpack 从入门到放弃

随着前端项目复杂程度越来越高,依赖也越来越多,为了提高项目中代码的可复用性,前端开始提出模块化开发的思路,前端模块化会有以下几个痛点:命名冲突,文件依赖,代码复用

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!

文章投稿关于web前端网站点搜索站长推荐网站地图站长QQ:522607023

小程序专栏: 土味情话心理测试脑筋急转弯幽默笑话段子句子语录成语大全运营推广