vue/cli2下的webpack3升级webpack4记录【转】

前言:由于公司项目是历史项目,因此还是基于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

你可能感兴趣的:(vue/cli2下的webpack3升级webpack4记录【转】)