前言:由于公司项目是历史项目,因此还是基于vue/cli2搭建的,webpack版本也是3.6.0。听说升级到webpack4之后打包速度贼快,所以早就想体验一下,但是又不敢拿公司项目开刀,因此先找一个以前写过的demo项目练练手。下面就记录一下vue/cli2下的webpack3升级webpack4中遇到的一些问题及解决方案。
目录
1、安装 webpack-cli ,升级webpack
2、升级webpack-dev-server至3.9.0
3、升级html-webpack-plugin
4、升级vue-loader
5、增加VueLoaderPlugin插件代码
6、升级一下 eslint 和 eslint-loader。
7、设置mode
8、配置performance。
9、安装babel-plugin插件转换es6代码。
10、配置optimization.splitChunks,删除optimize.CommonsChunkPlugin
11、去除extract-text-webpack-plugin,安装 mini-css-extract-plugin
1、安装 webpack-cli ,升级webpack
npm i --save-dev webpack webpack-cli
如果发现执行上述命令之后,webpack还是3.x的版本,那很可能是你的package-lock.json中锁定了webpack的最高版本。所以我们直接指定webpack4的最新稳定版进行安装。
npm i --save-dev [email protected]
2、升级webpack-dev-server至3.9.0
此时我们运行npm run dev,会发现报错:Error: Cannot find module ‘webpack/bin/config-yargs’
这是由于webpack-dev-server的版本和webpack4的版本不兼容导致的,webpack3对应的最高webpack-dev-server版本为2.11.5,所以我们要安装2.11.5以上的版本。查阅npm官网,安装目前webpack4对应最新的webpack-dev-server版本为3.9.0
npm i [email protected] --save-dev
3、升级html-webpack-plugin
安装完毕之后,再次执行npm run dev,发现之前的报错消失了。但是又有新的错误提示:compilation.mainTemplate.applyPluginsWaterfall is not a function
这是因为html-webpack-plugin 版本不兼容导致的,所以我们需要升级一下html-webpack-plugin。
npm i [email protected] -D
4、升级vue-loader
安装完毕之后,再次执行npm run dev,发现之前的报错消失了。但是又有新的错误提示:
Module build failed (from ./node_modules/vue-loader/index.js):
TypeError: Cannot read property ‘vue’ of undefined
这是因为vue-loader的版本不兼容webpack4导致的,所以我们升级一下vue-loader,
npm i [email protected] -D
5、增加VueLoaderPlugin插件代码
安装完毕之后,再次执行npm run dev,发现之前的报错消失了。但是又有新的错误提示:
vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.
File was processed with these loaders:
* ./node_modules/vue-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
这里是因为最新版的 vue-loader 需要加一个新的配置 VueLoaderPlugin。
打开webpack.dev.conf.js,增加如下代码,…表示省略部分已有的代码
//webpack.dev.conf.js
...
// 引入
const { VueLoaderPlugin } = require('vue-loader')
...
// 找到plugins数组,增加一个数组项
plugins: [
...
new VueLoaderPlugin()
]
然后打开webpack.prod.conf.js,进行同样的修改:
// webpack.prod.conf.js
...
// 引入
const { VueLoaderPlugin } = require('vue-loader')
...
// 找到plugins数组,增加一个数组项
plugins: [
...
new VueLoaderPlugin()
]
6、升级一下 eslint 和 eslint-loader。
安装完毕之后,再次执行npm run dev,发现之前的报错消失了。但是又有新的错误提示:
cannot read property ‘eslint’ of undefind!
很明显,这里我们需要升级eslint相关插件。
npm i --save-dev eslint-loader@latest eslint@latest
7、设置mode
安装完毕之后,再次执行npm run dev,发现之前的报错消失了。但是又有新的错误提示:
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 enviroment.
分别打开webpack.dev.conf.js和webpack.prod.conf.js 。
在webpackConfig 中增加以下代码:
// webpack.dev.conf.js
const webpackConfig = merge(baseWebpackConfig, {
mode: 'development',
module: {...}
}
// webpack.prod.conf.js
const webpackConfig = merge(baseWebpackConfig, {
mode: 'production',
module: {...}
}
8、配置performance。
安装完毕之后,再次执行npm run dev,发现之前的报错消失了。但是又有新的错误提示:
asset size limit: The following asset(s) exceed the recommended size limit (244 kib).
This can impact web performance.
在webpack.base.conf增加:
// webpack.base.conf
module.exports = {
performance: {
hints: "warning", // 枚举
maxAssetSize: 30000000, // 整数类型(以字节为单位)
maxEntrypointSize: 50000000, // 整数类型(以字节为单位)
assetFilter: function(assetFilename) {
// 提供资源文件名的断言函数
return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
}
},
module: {...}
}
9、安装babel-plugin插件转换es6代码。
安装完毕之后,再次执行npm run dev,发现之前的报错消失了。项目似乎已经可以正常运行了。但,打开项目地址一看,一片空白。F12控制台一样,鲜红的报错:
Uncaught TypeError: Cannot assign to read only property ‘exports’ of object ‘#
点开报错信息,发现直指BaseClient.js,该文件是node_modules下webpack-dev-server中的文件。经过查询得知,出现该种报错信息的原因是因为混用了import和module.exports,一个是es6的用法,一个是es5的用法。
网上的解决办法是建议统一使用es6或者es5的写法 。但是这里的文件并不是我写的,而是npm插件中生成的,所以肯定不能修改写法。于是想到了是不是没有配置babel转换导致的。
安装以下插件,
npm install babel-plugin-transform-es2015-modules-commonjs -D
在项目根目录的.babelrc文件中加入:
"plugins": ["transform-es2015-modules-commonjs"]
安装完后再次运行项目,打开项目地址,ok,终于可以正常运行了!
至此,dev环境下的修改已经全部完成。
简单来说就是编译时,遇到can not find xxx,can not find xxx 此类的错误,就需要升级对应的插件,倒也还好。
10、配置optimization.splitChunks,删除optimize.CommonsChunkPlugin
接下来还得继续配置product环境,执行npm run build ,依然是醒目的报错信息:
Error: webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead.
这是因为webpack4已经弃用了webpack.optimize.CommonsChunkPlugin插件,改用了config.optimization.splitChunks属性来进行更加灵活的配置。
打开webpack.prod.conf.js,找到plugins同级的位置增加以下代码。
// plugins
const webpackConfig = merge(baseWebpackConfig, {
module: {...},
optimization: { // 增加此处代码
// Setting optimization.runtimeChunk to true adds an additonal chunk to each entrypoint containing only the runtime.
// The value single instead creates a runtime file to be shared for all generated chunks.
runtimeChunk: 'single',
minimize: env === 'production' ? true : false, //生产环境下才进行代码压缩。
splitChunks:{
//As it was mentioned before this plugin will affect dynamic imported modules. Setting the optimization.
//splitChunks.chunks option to "all" initial chunks will get affected by it (even the ones not imported dynamically).
//This way chunks can even be shared between entry points and on-demand loading.
//This is the recommended configuration.
//官方推荐使用all.
chunks: 'all',
minSize: 30000, //模块大于30k会被抽离到公共模块
minChunks: 1, //模块出现1次就会被抽离到公共模块
maxAsyncRequests: 5, //异步模块,一次最多只能被加载5个
maxInitialRequests: 3, //入口模块最多只能加载3个
name: true, // 拆分出来块的名字(Chunk Names),默认由块名和hash值自动生成;设置为true则表示根据模块和缓存组秘钥自动生成。
cacheGroups: {
// 详情建议参看官网 http://webpack.html.cn/plugins/split-chunks-plugin.html
default: {
minChunks: 2,
reuseExistingChunk: true,
},
//打包重复出现的代码
vendor: {
// chunks: 'initial',
// 省略test默认选择所有的模块。
chunks: 'all',
minChunks: 2,
name: 'vendor'
},
//打包第三方类库
commons: {
// chunks: "initial",
chunks: "all",
name: "commons",
minChunks: Infinity
}
}
}
},
plugins: [...]
})
同时,注释或者删除plugins中含有CommonsChunkPlugin的代码,应该是3块:
// 去掉 plugins 中的这3部分代码
...
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
}),
...
11、去除extract-text-webpack-plugin,安装 mini-css-extract-plugin
再次执行npm run build,发现之前的报错信息没有了,但是仍然有新的报错提示:
Error: Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead
这里是因为官方已经不推荐使用 extract-text-webpack-plugin 提取 css 样式,可以使用 mini-css-extract-plugin 替代
如果不想改变的话可以升级 extract-text-webpack-plugin到4.0.0-beta.0版本。
但是这里还是推荐使用mini-css-extract-plugin,因为它可以根据页面按需加载,分割css代码,使得以前一大坨CSS彻底成为过去式,强烈建议替换。
首先安装mini-css-extract-plugin,
npm i mini-css-extract-plugin -D
然后卸载extract-text-webpack-plugin,
npm uninstall extract-text-webpack-plugin
然后去除所有和extract-text-webpack-plugin相关的代码。一共有2处位置需要修改:
第一个是utils.js中的:
// utils.js
'use strict'
...
// const ExtractTextPlugin = require('extract-text-webpack-plugin') //删除或注释此段。
// 引入
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
...
// 修改此处的generateLoaders函数。
// generate loader string to be used with extract text plugin
function 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,
// publicPath: '../../',
// fallback: 'vue-style-loader'
// })
/**注释或删除此处代码,结束 */
/**增加此处代码开始 */
return [MiniCssExtractPlugin.loader].concat(loaders)
/**增加此处代码结束 */
} else {
return ['vue-style-loader'].concat(loaders)
}
}
第二处是webpack.prod.conf.js中的:
...
//webpack.prod.conf
/**注释或删除此处代码,开始 */
// const ExtractTextPlugin = require('extract-text-webpack-plugin')
/**注释或删除此处代码,结束 */
/**增加此处代码,开始 */
const MiniCssExtractPlugin = require('mini-css-extract-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,
// }),
/**注释或删除此处代码,结束 */
/**增加此处代码,开始 */
new MiniCssExtractPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
allChunks: true,
}),
/**增加此处代码,结束 */
...
再次运行npm run build,这次终于运行成功了!
至此,vue/cli2下的webpack3升级webpack4工作已经全部完成,希望能够帮助到读者朋友们。
参考文章:
1、Webpack4之SplitChunksPlugin规则
2、webpack4——SplitChunksPlugin使用指南
3、Webpack4 升级全教程
4、vue项目从webpack3 升级webpack4
————————————————
版权声明:本文为CSDN博主「SilenceJude」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/SilenceJude/article/details/103080559