webpack4 也发布3个月了,一直想体验一下。最近项目不忙,也感觉项目编译和打包的速度略慢,索性就把一个由 vue-cli 生成的项目从 webpack3 升级到 webpack4,期间遇到的问题也记录一下。
安装依赖
npm i webpack@latest webpack-cli --save-dev
出现报错信息:
根据报错信息,逐个升级它们:
npm install extract-text-webpack-plugin@latest html-webpack-plugin@latest inject-loader@latest webpack-dev-middleware@latest webpack-dev-server@latest
顺便把其它 loaders
及 plugins
都升级到最新版本
npm install webpack-bundle-analyzer@latest vue-template-compiler@latest webpack-merge@latest friendly-errors-webpack-plugin@latest copy-webpack-plugin@latest optimize-css-assets-webpack-plugin@latest
npm install css-loader@latest file-loader@latest url-loader@latest less-loader@latest postcss-loader@latest vue-loader@latest vue-style-loader@latest
升级的版本信息如下:
-
webpack@4.8.3
-
webpack-cli@2.1.4
-
html-webpack-plugin@3.2.0
-
extract-text-webpack-plugin@4.0.0-beta.0
-
webpack-dev-server@3.1.4
-
webpack-dev-middleware@3.1.3
-
friendly-errors-webpack-plugin@1.7.0
-
webpack-bundle-analyzer@2.13.1
-
webpack-merge@4.1.2
-
optimize-css-assets-webpack-plugin@4.0.1
-
copy-webpack-plugin@4.5.1
-
vue-template-compiler@2.5.16
-
postcss-loader@2.1.5
-
inject-loader@4.0.1
-
less-loader@4.1.0
-
css-loader@0.28.11
-
vue-style-loader@4.1.0
-
file-loader@1.1.11
-
vue-loader@15.2.0
-
url-loader@1.0.1
运行 npm run dev
,又出现 eslint
的报错信息
npm i eslint@latest eslint-config-standard@latest eslint-friendly-formatter@latest eslint-loader@latest eslint-plugin-import@latest eslint-plugin-node@latest eslint-plugin-promise@latest eslint-plugin-standard@latest eslint-plugin-vue@latest
- eslint-plugin-standard@3.1.0
- eslint-plugin-vue@4.5.0
- eslint-plugin-promise@3.7.0
- eslint-plugin-import@2.12.0
- eslint-config-standard@11.0.0
- eslint-loader@2.0.0
- eslint-plugin-node@6.0.1
- eslint@4.19.1
- eslint-friendly-formatter@4.0.1
配置
相比于 webpack 3,webpack 4 的配置部分改变,具体如下:
在 dev 环境中,添加 mode: 'development'
,去掉 webpack.NamedModulesPlugin 及 webpack.NoEmitOnErrorsPlugin 插件,因为 webpack4 开发模式已经内置。
// webpack.dev.conf.jsmodule.exports = { // ... mode: 'development', // ... plugins: { // new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. // new webpack.NoEmitOnErrorsPlugin(), }}复制代码
在 prod 环境中添加 mode 配置,用 optimization 代替以前的 webpack.optimize.CommonsChunkPlugin 、 uglifyjs-webpack-plugin 、 webpack.optimize.ModuleConcatenationPlugin 相关配置及引用
// webpack.production.prod.js// const UglifyJsPlugin = require('uglifyjs-webpack-plugin')const webpackConfig = merge(baseWebpackConfig, { // ... mode: 'production', // webpack4 内置 optimization: { splitChunks: { cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, chunks: 'initial', name: 'vendors', }, 'async-vendors': { test: /[\\/]node_modules[\\/]/, minChunks: 2, chunks: 'async', name: 'async-vendors' } } }, runtimeChunk: { name: 'runtime' } }, // ... pluins: { // new UglifyJsPlugin({ // uglifyOptions: { // beautify: false, // comments: false, // compress: { // warnings: false, // drop_console: true // } // }, // sourceMap: config.build.productionSourceMap, // parallel: true // }), // ... // enable scope hoisting // new webpack.optimize.ModuleConcatenationPlugin(), // split vendor js into its own file // new webpack.optimize.CommonsChunkPlugin({ // name: 'vendor', // minChunks (module) { // // any required modules inside node_modules are extracted to vendor // return ( // module.resource && // /\.js$/.test(module.resource) && // module.resource.indexOf( // path.join(__dirname, '../node_modules') // ) === 0 // ) // } // }), // 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 // }), }}复制代码
运行 npm run dev
,又出现 vue-loader
的报错信息:
vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.
为了解决上面的 vue-loader
的报错,在 webpack.base.conf.js 中添加配置
// webpack.base.conf.jsconst { VueLoaderPlugin } = require('vue-loader')//...plugins: [ new VueLoaderPlugin()]复制代码
运行 npm run dev
,报错:
虽然有报错信息,但是没有显示错误的具体位置。在网上查找了很久,发现有人说他在template file
和 routing file
出现了循环引用。 根据这个,我突然发现在 main.js
和 向后台请求的api文件中都引用了路由文件,api.js
中引用是为了写 axios
拦截器,出现 401、404 或 500 时,跳转相应页面。 注释掉 api.js
中对 router
的引用,确实可以成功运行了,但是 401、404、500 跳页的问题就没法解决了。 但是后来我又找到了另一种方法,在 HtmlWebpackPlugin
插件中添加或者修改 chunksSortMode: none
。(不过不确定这方式会不会对性能优化等等有影响)
new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', inject: true, chunksSortMode: 'none'}),复制代码
运行 npm run dev
,没问题了。
接下来试试生产环境,运行 npm run build
。1分钟过去了……5分钟过去了……中午吃饭1个小时都过去了……居然没反应,也没有报错。又尝试了好几次,依旧如此。在网上也没有找到相关问题。
于是我只好在 build.js
中逐步打断点,最后发现是 extract-text-webpack-plugin
插件的问题。 查找资料了解到 extract-text-webpack-plugin
其实是适配 webpack3 的,有个 extract-text-webpack-plugin@4.0.0-beta.0
版本可以适配 webpack4,但是我用的就是这个版本。
有人推荐用 mini-css-extract-plugin
来替代它,我就根据 尝试配置一下。
需要在 webpack.prod.conf.js
和 utils.js
两个文件中配置。
// webpack.prod.conf.js// const ExtractTextPlugin = require('extract-text-webpack-plugin')const MiniCssExtractPlugin = require('mini-css-extract-plugin')// ...// extract css into its own file// new ExtractTextPlugin({ // ...// })// 升级 webpack4, 由 ExtractTextPlugin 改用 MiniCssExtractPluginnew MiniCssExtractPlugin({ filename: utils.assetsPath('css/[name].[contenthash].css'), allChunks: true,}),复制代码
// utils.js// const ExtractTextPlugin = require('extract-text-webpack-plugin')const MiniCssExtractPlugin = require('mini-css-extract-plugin')// ...// generate loader string to be used with extract text pluginfunction generateLoaders (loader, loaderOptions) { const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] if (loader) { loaders.push({ loader: loader + '-loader', options: Object.assign({}, loaderOptions, { sourceMap: options.sourceMap }) }) } // Extract CSS when that option is specified // (which is the case during production build) // if (options.extract) { // return ExtractTextPlugin.extract({ // use: loaders, // fallback: 'vue-style-loader' // }) // } else { // return ['vue-style-loader'].concat(loaders) // } // 升级 webpack4, 由 ExtractTextPlugin 改用 MiniCssExtractPlugin return [ options.extract ? MiniCssExtractPlugin.loader : 'vue-style-loader', ].concat(loaders)}复制代码
运行 npm run build
,终于成功了。
附上 webpack3 和 webpack4 的打包对比图,可以看到,体积少了一点(后期我会在边学习 webpack 边进行优化)但是打包的时间减少了一半,也不枉费我这么辛苦的升级。
参考
- https://github.com/jantimon/html-webpack-plugin/issues/870
- https://webpack.js.org/plugins/mini-css-extract-plugin/
- https://vue-loader.vuejs.org/guide/extract-css.html#webpack-4