记录 Webpack 从版本 4 更新至 5 所遇到的问题

Webpack 从 v4 迁移至 v5 问题记录

最近,出于工作中对项目优化,有打算将用到的 Webpack 从 4.* 升级至最新版本(Webpack@5.3.0);鉴于之前就有 Webpack 相关经验,略看了点文档 Webpack 从 v4 升级到 v5 后,就基于 nicelinks-vue-client 项目开始了升级之旅。因为强行升级,过程也较为曲折,有将遇到的一些问题做下梳理记录,希望对之后欲升级 webpack 的朋友,形成参考。

webpack5 构建 vue 编译报错

TypeError: Cannot read property 'properties' of undefined

重新安装依赖 webpack-cli

yarn remove webpack-cli
yarn add webpack-cli -D

[webpack-cli] Invalid configuration object.

[webpack-cli] Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema. configuration.module.rules[5] should be one of these:
["..." | object { compiler?, dependency?, descriptionData?, enforce?, exclude?, generator?, include?, issuer?, loader?, mimetype?, oneOf?, options?, parser?, realResource?, resolve?, resource?, resourceFragment?, resourceQuery?, rules?, sideEffects?, test?, type?, use? }, ...] -> A rule. Details: * configuration.module.rules[4] has an unknown property 'query'.

Compiling RuleSet failed

ERROR: Compiling RuleSet failed: A Rule must not have a 'options' property when it has a 'use' property

在 Webpack 最新版本中,rules 属性中的配置,可以有 testexcludeuseinclude 等字段,但不允许有 options 了;如果需要,可以写成下面这样:

{
  test: /\.m?js$/,
  exclude: /(node_modules|bower_components)/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: ['@babel/preset-env']
    }
  }
},

TypeError: Cannot read property 'vue' of undefined

Module build failed (from ./node_modules/vue-loader/index.js):
TypeError: Cannot read property 'vue' of undefined

重新安装 vue-loader 插件:

yarn remove vue-loader && yarn add vue-loader -D

Module parse failed: Unexpected character

ERROR in ./src/assets/scss/style.scss 1:0
Module parse failed: Unexpected character
ERROR in ./src/components/markdown/markdown.css 1:0
Module parse failed: Unexpected token
ERROR in ./src/views/manage/Users.vue?vue&type=template&id=85a68250& 2:0
Module parse failed: Unexpected token (2:0)
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.

是因为在 webpack.config.js 中没有添加对 cssscss 等文件进行处理操作,添加对应规则即可:

rules: [
  {
    test: /\.s[ac]ss$/i,
    use: [
      // Creates `style` nodes from JS strings
      'style-loader',
      // Translates CSS into CommonJS
      'css-loader',
      // Compiles Sass to CSS
      'sass-loader',
    ],
  },
  {
    test: /\.css$/i,
    use: [
      // Creates `style` nodes from JS strings
      'style-loader',
      // Translates CSS into CommonJS
      'css-loader'
    ],
  }
]

vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.

Vue-loader在 15.* 之后的版本都是 vue-loader 的使用都是需要伴生 VueLoaderPlugin 的。在webpack.config.js中加入

const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
    // ......
    plugins: [
        // make sure to include the plugin for the magic
        new VueLoaderPlugin()
    ],
}

ERROR in ReferenceError: webpack is not defined

ERROR in Template execution failed: ReferenceError: webpack is not defined
ERROR in ReferenceError: webpack is not defined

HtmlWebpackPlugin 插件的自定义模版中用到了 webpack 变量;而最新版本已经不再支持这个变量,因此去掉下面这些就好:

<% for (var chunk of webpack.chunks) {
	for (var file of chunk.files) {
		if (file.match(/\.(css)$/)) { %>
			<link rel="<%= chunk.initial ? 'preload' : 'prefetch' %>" href="<%= htmlWebpackPlugin.files.publicPath + file %>" as="<%= file.match(/\.css$/)?'style':'script' %>">
<% }}} %>

TypeError: Cannot read property 'babel' of undefined

Module build failed (from ./node_modules/babel-loader/lib/index.js):
TypeError: Cannot read property 'babel' of undefined

所依赖的 babel-loader 不是最新版本,更新下即可:

yarn remove babel-loader && yarn add babel-loader -D
new webpack.DllReferencePlugin({
      context: path.resolve(__dirname, '..'),
      manifest: require('./vendor-manifest.json')
}),

[webpack-cli] TypeError: compiler.plugin is not a function

[webpack-cli] TypeError: compiler.plugin is not a function
at AddAssetHtmlPlugin.apply (/Users/x/y/z/node_modules/add-asset-html-webpack-plugin/lib/index.js:10:14)

是因为所依赖的 add-asset-html-webpack-plugin 插件不匹配新版本,更新至最新版即可:

yarn remove add-asset-html-webpack-plugin && yarn add add-asset-html-webpack-plugin -D

webpack5 构建 vue 运行报错

Cannot access 'WEBPACK_DEFAULT_EXPORT' before initialization

index.js:3 Uncaught ReferenceError: Cannot access 'WEBPACK_DEFAULT_EXPORT' before initialization at Module.default (index.js:3)

是因为在构建配置中,有部分依赖出现重复所致;重新启用 DllReferencePlugin 插件,去除这部分重复的组件即可:

new webpack.DllReferencePlugin({
  context: path.resolve(__dirname, '..'),
  manifest: require('./vendor-manifest.json')
}),

Uncaught TypeError: Cannot read property 'call' of undefined

Uncaught TypeError: Cannot read property 'call' of undefined
at webpack_require (vendor.dll.js:24767)
at eval (index.js?9552:1)

根据控制台错误提示,点击进去发现,在 webpack:///./src/index.js 下存在这样的代码:

import ContentPlaceholder from './ContentPlaceholder.vue'
export default ContentPlaceholder

事实上代码中的写法是:

import ContentPlaceholder from 'vue-content-placeholder'
export default {
	component: {
		ContentPlaceholder
	}
}

有尝试移除对该组件的依赖,就不会存在此问题,所构建出的包,也能在本地正常运行起来;略做了查纠,发现 vue-content-placeholder 该组件库,并未提供源代码,node_modules 中存在的也是构建后的内容,与当前版本 webpack(5) 不匹配所致。后续考虑重新写或者替换一个库来予以解决。

当然,webpack 打包,能引起类似报错的种类繁多,上述这种解决方法,不一定适合其他项目,具体问题需,要具体分析。

Uncaught ReferenceError: vendor_library is not defined

是因为自定义的映射文件没有注入,使用 add-asset-html-webpack-plugin 插件导入即可(vendor.dll.js):

new AddAssetHtmlPlugin({
      filepath: path.resolve(__dirname, 'dist/*.dll.js'),
}),