vue-cli項目升級webpack4踩坑

webpack4 也發佈3個月了,一直想體驗一下。最近項目不忙,也感受項目編譯和打包的速度略慢,索性就把一個由 vue-cli 生成的項目從 webpack3 升級到 webpack4,期間遇到的問題也記錄一下。javascript

安裝依賴

npm i webpack@latest webpack-cli --save-devcss

出現報錯信息:html

報錯信息

根據報錯信息,逐個升級它們:vue

npm install extract-text-webpack-plugin@latest html-webpack-plugin@latest inject-loader@latest webpack-dev-middleware@latest webpack-dev-server@latestjava

順便把其它 loadersplugins 都升級到最新版本node

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@latestwebpack

npm install css-loader@latest file-loader@latest url-loader@latest less-loader@latest postcss-loader@latest vue-loader@latest vue-style-loader@latestios

升級的版本信息以下:git

  • webpack@4.8.3github

  • 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 的報錯信息

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.js
module.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.js
const { VueLoaderPlugin } = require('vue-loader')

//...
plugins: [
  new VueLoaderPlugin()
]
複製代碼

運行 npm run dev,報錯:

cyclic dependency報錯信息

雖然有報錯信息,可是沒有顯示錯誤的具體位置。在網上查找了好久,發現有人說他在template filerouting file 出現了循環引用。 根據這個,我忽然發如今 main.js 和 向後臺請求的api文件中都引用了路由文件,api.js 中引用是爲了寫 axios攔截器,出現 40一、404 或 500 時,跳轉相應頁面。 註釋掉 api.js 中對 router 的引用,確實能夠成功運行了,可是 40一、40四、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.jsutils.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 改用 MiniCssExtractPlugin
new 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 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,
  // 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 邊進行優化)可是打包的時間減小了一半,也不枉費我這麼辛苦的升級。

webpack3

webpack4

參考

  1. vue-cli#webpack3.0 升級到webpack4.6
  2. Webpack4 新特性 及 Vue-cli項目升級
  3. https://github.com/jantimon/html-webpack-plugin/issues/870
  4. https://webpack.js.org/plugins/mini-css-extract-plugin/
  5. https://vue-loader.vuejs.org/guide/extract-css.html#webpack-4
相關文章
相關標籤/搜索