webpack3到4的迁移
webpack4大幅度减少配置成本。为了优化打包流程和打包效率,决定升级新版本,拥抱新技术
计划步骤:
- 从现有的一个项目,找到每个配置的作用是什么,以及新旧版本的区别,然后制定修改方案,同时埋好日志输出,便于排查错误
- 打包测试,修正问题
找到每个配置的作用是什么,以及新旧版本的区别,然后制定修改方案,同时埋好日志输出,便于排查错误
为防止不同npm包版本兼容有问题,我们把所有涉及到的包都升级到最新
先把"webpack": "3.6.0",升级到4.39.3
打开项目根目录下package.json
从scripts
下的dev
开始分析
webpack-dev-server --config build/webpack.dev.conf.js // 通过webpack-dev-server启动的本地服务。看到最新版本webpack-dev-server,需要webpack4,索性把webpack-dev-server升级到最新版,把原来的 "webpack-dev-server": "2.9.1", 改成 "3.8.0"
分析build/webpack.dev.conf.js
文件
引入的插件
const merge = require('webpack-merge') // "webpack-merge": "4.1.0" 升级到 "4.2.2"
const HtmlWebpackPlugin = require('html-webpack-plugin') // "html-webpack-plugin": "2.30.1" 升级到 "3.2.0"
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') // 最新版不用动
const portfinder = require('portfinder') // "portfinder": "1.0.13" 升级 "1.0.23"
引入其他文件const utils = require('./utils')
webpack4,ExtractTextPlugin废弃了,需要换成MiniCssExtractPlugin
const ExtractTextPlugin = require('extract-text-webpack-plugin') // 用于提取css文件 webpack4 不需要用这个插件, 换成 mini-css-extract-plugin
// 在工程里搜索 extract-text-webpack-plugin 发现可能有用到的文件或者包有:
1. /build/utils.js
// const ExtractTextPlugin = require('extract-text-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
if (options.extract) {
// return ExtractTextPlugin.extract({
// use: loaders,
// fallback: 'vue-style-loader'
// })
return [
{
loader: MiniCssExtractPlugin.loader
},
'css-loader'
]
}
2. /build/webpack.prod.conf.js
// new ExtractTextPlugin({
new MiniCssExtractPlugin({
3. 包 avg // 未在package.json中出现,不做处理
4. 包 async // 未在package.json中出现,不做处理
5. 包 css-loader // "css-loader": "0.28.0", 升级到 "3.2.0"
6. 包 element-ui // "element-ui": "2.3.2", 升级到2.11.1
7. 包 html-webpack-plugin // 已经升级过
8. 包 loader-utils // 未在package.json中出现,不做处理
9. 包 optimize-css-assets-webpack-plugin // "3.2.0",升级到5.0.3
10. 包 postcss-loader // "postcss-loader": "2.0.8", 升级到3.0.0
11. 包 sass-loader // 6.0.6,升级到7.3.1
12. 包 schema-utils // 未在package.json中出现,不做处理
13. 包 svg-sprite-loader // 3.7.3 升级到4.1.6
14. 包 tabel // 未在package.json中出现,不做处理
15. 包 ajv // 未在package.json中出现,不做处理
16. 包 ve-calendar // 没有新版本暂时不处理
17. 包 vue-loader // 13.3.0 升级到15.7.1
18. 包 vue-resize-directive // 未在package.json中出现,不做处理
19. 包 webpack-sources // 未在package.json中出现,不做处理
引入其他文件const config = require('../config')
根据不同环境:develop或者production等采取不同的配置,可能一些配置和webpack4不一致,暂定不需修改。
引入其他文件const baseWebpackConfig = require('./webpack.base.conf')
引入文件 vue-loader.conf,用于vue-loader需要的配置,暂定不需修改。
分析build/webpack.dev.conf.js
文件配置的参数
暂定不需修改
打包测试,修正问题
安装npm包
删掉node_modules目录
执行npm i
npm WARN element-ui@2.11.1 requires a peer of vue@^2.5.17 but none is installed. You must install peer dependencies yourself.
提示element-ui
需要vue@2.5.17,将vue升级到2.6.10。重新npm i
npm WARN babel-loader@7.1.1 requires a peer of webpack@2 || 3 but none is installed. You must install peer dependencies yourself.
npm WARN file-loader@1.1.4 requires a peer of webpack@^2.0.0 || ^3.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN uglifyjs-webpack-plugin@1.1.1 requires a peer of webpack@^2.0.0 || ^3.0.0 but none is installed. You must install peer dependencies yourself.
升级babel-loader到8.0.6,升级file-loader到4.2.0,webpack4.0已经不用uglifyjs-webpack-plugin,直接删掉。重新npm i
npm WARN babel-loader@8.0.6 requires a peer of @babel/core@^7.0.0 but none is installed. You must install peer dependencies yourself.
升级babel-core
到@babel/core] 7.5.5
重新npm i
测试
npm run dev
The CLI moved into a separate package: webpack-cli
Please install 'webpack-cli' in addition to webpack itself to use the CLI
# 安装webpack-cli
npm install webpack-cli --save-dev
重新npm run dev
ERROR Failed to compile with 1 errors
Module build failed (from ./node_modules/eslint-loader/index.js):
TypeError: Cannot read property 'eslint' of undefined at Object.module.exports (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint-loader/index.js:155:18)
eslint-loader
1.7.1升级到3.0.0, 重新npm i
npm WARN eslint-loader@3.0.0 requires a peer of eslint@^5.0.0 || ^6.0.0 but none is installed. You must install peer dependencies yourself.
eslint
3.19.0升级到6.2.2, 重新npm i
npm WARN eslint-plugin-import@2.7.0 requires a peer of eslint@2.x - 4.x but none is installed. You must install peer dependencies yourself.
npm WARN acorn-jsx@5.0.2 requires a peer of acorn@^6.0.0 || ^7.0.0 but none is installed. You must install peer dependencies yourself.
eslint-plugin-import
2.7.0升级到2.18.2。
通过命令npm ls
查看整个包依赖树,
webpack-bundle-analyzer
引用了acorn@5.7.3
,所以把webpack-bundle-analyzer
2.9.0升级到3.4.1
另外一个引用jest@21.2.0
-> jest-cli
-> jest-environment-jsdom
-> jsdom
-> acorn@4.0.13
所有把jest@21.2.0升级到24.9.0
重新npm i
还是有警告
npm WARN acorn-jsx@5.0.2 requires a peer of acorn@^6.0.0 || ^7.0.0 but none is installed. You must install peer dependencies yourself.
依赖树中看到
│ │ ├── UNMET PEER DEPENDENCY acorn@7.0.0
│ │ ├── acorn-jsx@5.0.2
只好手动安装acorn最新版本
npm install acorn --save-dev
重新npm i
没有警告
npm run dev
Module build failed (from ./node_modules/eslint-loader/dist/cjs.js):
TypeError: Cannot read property 'forEach' of undefined
at Linter.parseResults (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint-loader/dist/Linter.js:121:13)
at Linter.printOutput (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint-loader/dist/Linter.js:85:26)
at Object.loader (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint-loader/dist/index.js:26:10)
表面上看不出这个错误导致是怎么导致的。在/node_modules/eslint-loader/dist/Linter.js的lint(content)
函数中输出捕获的错误.
Failed to load plugin 'html' declared in '.eslintrc.js': eslint-plugin-html error: It seems that eslint is not loaded. If you think it is a bug, please file a report at https://github.com/BenoitZugmeyer/eslint-plugin-html/issues
at iterateESLintModules (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint-plugin-html/src/index.js:79:11)
at Object.<anonymous> (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint-plugin-html/src/index.js:21:1)
at Module._compile (internal/modules/cjs/loader.js:738:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:749:10)
at Module.load (internal/modules/cjs/loader.js:630:32)
at tryModuleLoad (internal/modules/cjs/loader.js:570:12)
at Function.Module._load (internal/modules/cjs/loader.js:562:3)
at Module.require (internal/modules/cjs/loader.js:667:17)
at require (internal/modules/cjs/helpers.js:20:18)
at ConfigArrayFactory._loadPlugin (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint/lib/cli-engine/config-array-factory.js:861:49)
把eslint-plugin-html
从3.0.0升级到6.0.0
npm i
成功升级eslint-plugin-html
npm run dev
Cannot find module 'escope'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:613:15)
at monkeypatch (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/babel-eslint/index.js:53:26)
at Object.exports.parse (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/babel-eslint/index.js:358:5)
at parse (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint/lib/linter/linter.js:625:29)
at Linter._verifyWithoutProcessors (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint/lib/linter/linter.js:1067:33)
at Linter.(anonymous function) (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint-plugin-html/src/index.js:151:21)
at Linter._verifyWithConfigArray (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint/lib/linter/linter.js:1210:21)
at Linter.verify (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint/lib/linter/linter.js:1165:25)
at Linter.verifyAndFix (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint/lib/linter/linter.js:1355:29)
at verifyText (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/eslint/lib/cli-engine/cli-engine.js:231:48)
babel-eslint
版本也比较老,7.1.1换成 10.0.3
npm i
成功升级babel-eslint
npm run dev
Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: .plugins[1][1] must be an object, false, or undefined
at assertPluginItem (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/validation/option-assertions.js:244:15)
at arr.forEach (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/validation/option-assertions.js:222:30)
at Array.forEach (<anonymous>)
at assertPluginList (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/validation/option-assertions.js:222:9)
at Object.keys.forEach.key (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/validation/options.js:107:5)
at Array.forEach (<anonymous>)
at validateNested (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/validation/options.js:83:21)
at validate (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/validation/options.js:74:10)
at file (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/config-chain.js:174:34)
at cachedFunction (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/caching.js:33:19)
在/node_modules/@babel/core/lib/config/validation/options.js
添加日志输出
console.log('optLoc--------', optLoc)
console.log('opts--------', opts)
console.log('key-----', key)
validator(optLoc, opts[key]);
// 报错的前的输出是
optLoc-------- { type: 'option',
name: 'plugins',
parent: { type: 'root', source: 'babelrcfile' } }
opts-------- { presets: [ [ 'env', [Object] ], 'stage-2' ],
plugins: [ 'transform-runtime', [ 'component', [Array] ] ],
env: { test: { presets: [Array], plugins: [Array] } } }
key----- plugins
// 是babelr配置文件出问题了,这里应该是babel修改配置规则了,plugins里的元素不能是数组了
修改.babelrc文件
"plugins": ["transform-runtime", ["component", [
{
"libraryName": "element-ui"
}
]]]
改为
"plugins": ["transform-runtime", "component": [
{
"libraryName": "element-ui"
}
]]
npm run dev
Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: Plugin/Preset files are not allowed to export objects, only functions. In /Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/babel-preset-stage-2/lib/index.js
at createDescriptor (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/config-descriptors.js:178:11)
at items.map (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/config-descriptors.js:109:50)
at Array.map (<anonymous>)
at createDescriptors (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/config-descriptors.js:109:29)
at createPresetDescriptors (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/config-descriptors.js:101:10)
at presets (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/config-descriptors.js:47:19)
at mergeChainOpts (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/config-chain.js:320:26)
at /Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/config-chain.js:283:7
at buildRootChain (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/config-chain.js:120:22)
at loadPrivatePartialConfig (/Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/@babel/core/lib/config/partial.js:85:55)
先将babel-preset-stage-2
从6.22.0升级到6.24.1
npm i
成功升级babel-preset-stage-2
npm run dev
一样的错误
/@babel/core/lib/config/config-chain.js
283行添加打印日志
console.log('chain--', chain)
console.log('op--', op)
mergeChainOpts(chain, op);
// 报错前输出
chain-- { options: [], presets: [], plugins: [] }
op-- { options:
{ presets: [ [Array], 'stage-2' ],
plugins: [ 'transform-runtime', [Object] ],
env: { test: [Object] } },
plugins: [Function: plugins],
presets: [Function: presets] }
babel-plugin-transform-runtime@6.22.0
更新为@babel/plugin-transform-runtime@7.5.5
.babelrc文件中
"plugins": ["transform-runtime", {
改为
"plugins": ["@babel/plugin-transform-runtime", {
npm i
这里一直报babel的错,把babel的相关包都做下升级
"babel-plugin-dynamic-import-node": "1.2.0", => 2.3.0
"babel-plugin-transform-es2015-modules-commonjs": "6.26.0", => 6.26.2
"babel-preset-env": "1.3.2", => 1.7.0
"babel-register": "6.22.0", => 6.26.0
"babel-plugin-component": "0.10.1", => 1.1.1
npm i
npm run dev 一样的错,在网上找到有将babel-preset-env换成 @babel/preset-env,试下
"babel-preset-env": "1.7.0", => "@babel/preset-env" : "7.5.5",
"babel-register": "6.26.0", => "@babel/register" : "7.5.5", // 一起改掉
"babel-polyfill": "6.26.0", => "@babel/polyfill" : "7.4.4", // 一起改掉
npm i
npm run dev 依然报错
临时把.babelrc文件简化
{
"presets": ["@babel/preset-env"]
}
npm run dev
报其他错误。
先做下相关npm包升级
"vue-template-compiler": "2.5.2", => "2.6.10",
"vue-style-loader": "3.0.1", => "@babel/register" : "4.1.2",
依然报错
有个老兄说 webpack4 不兼容vue-loader 15.x.x版本https://www.cnblogs.com/tugenhua0707/p/9695179.html
我也把vue-loader 降低成^14.2.2
之前错误没了,只有eslint的错误
暂时将/config/index.js中的useEslint设置成false,
npm run dev
终于跑起来了
将/config/index.js中的useEslint设置成true
将eslint
相关的包升级为最新
"eslint-config-standard": "10.2.1", => "14.1.0",
"eslint-friendly-formatter": "3.0.0", => "4.0.1",
"eslint-plugin-node": "5.2.0", => "10.0.0",
"eslint-plugin-promise": "3.5.0", => "4.2.1",
"eslint-plugin-standard": "3.0.1", => "4.0.1",
npm i
npm run dev 依然报错,猜测可能和简化.babelrc
有关系
把babelrc中的配置一点点加回来
{
"presets": [["@babel/preset-env", {"modules": false}], "stage-2"]
}
npm run dev 依然报错
查到babel7 stage-2
的文章 https://babeljs.io/docs/en/babel-preset-stage-2 发现babel-preset-stage-2插件需要更换
"babel-preset-stage-2": "6.24.1", => "@babel/preset-stage-2": "7.0.0",
npm i
babelrc中的配置修改
{
"presets": [["@babel/preset-env", {"modules": false}], "@babel/preset-stage-2"]
}
npm run dev , 提示As of v7.0.0-beta.55, we've removed Babel's Stage presets.
发现babel7
已经移除了stage preset
https://babeljs.io/blog/2018/07/27/removing-babels-stage-presets
babelrc
中去掉@babel/preset-stage-2
并添加pluginx
component
的结构做了点调整
{
"presets": [["@babel/preset-env", {"modules": false}]],
"plugins": [
"@babel/plugin-transform-runtime",
[
"component",
{
"libraryName": "element-ui"
}
]
]
}
npm run dev
# 提示错误信息
These dependencies were not found:
* @babel/runtime/helpers/defineProperty in ./src/router/index.js
...
添加@babel/runtime
包
"@babel/runtime": "^7.6.0"
npm i
npm run dev
# 提示错误信息
These dependencies were not found:
* element-ui/lib/alert/style.css in ./src/element-ui/index.js
* element-ui/lib/aside/style.css in ./src/element-ui/index.js
...
element包导入有问题,babelrc
需要添加styleLibraryName
用来加载样式文件
{
"presets": [["@babel/preset-env", {"modules": false}]],
"plugins": [
"@babel/plugin-transform-runtime",
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
npm run lint(lint配置在package中)
还是报错
http://eslint.org/docs/rules/dot-notation ["children"] is better written in dot notation
src/utils/index.js:36:31
...
1 | <template>
2 | <div class="mod-user">
> 3 | <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
| ^
4 | <el-form-item>
5 | <el-input v-model="dataForm.userName" placeholder="用户名" clearable></el-input>
6 | </el-form-item>
首先先 将 --fix 添加到package中的lint脚步中
"scripts": {
...
"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs --fix",
...
},
npm run lint
只剩下vue lint 相关的错误
看到.eslintrc.js中,plugins:[ 'html'] 上有注释// required to lint *.vue files
查了下eslint-plugin-html插件
发现
Linting VUE files
Initially, eslint-plugin-vue was using eslint-plugin-html to lint code inside script tags. Since v3, eslint-plugin-vue is using its own parser, so it is incompatible with eslint-plugin-html. You should use eslint-plugin-vue exclusively and remove eslint-plugin-html from your dependencies if you still have it.
原来用 eslint-plugin-html
插件就可以检测vue文件,现在更换最新的eslint-plging-vue
// 删除
"eslint-plugin-html"
// 添加
"eslint-plugin-vue": "^5.2.3",
参考https://vuejs.github.io/eslint-plugin-vue/user-guide/#faq
.eslintrc.js修改plugin
"plugins": [
"vue",
- "html"
]
.eslintrc.js修改extends
extends: ['plugin:vue/essential', 'standard'],
.eslintrc.js修改parserOptions 添加 parser: 'babel-eslint'
这个是为了给eslint-plugin-vue提供解析 .vue文件中<script>标签中的代码,因为eslint-plugin-vue
的解析器只解析<template>标签里的内容
去掉原来的parser
- "parser": "babel-eslint",
parserOptions: {
sourceType: 'module',
parser: 'babel-eslint',
},
npm run lint
仅剩下一些defined but never used
的错误,逐个更正
npm run lint 成功
.babelrc中添加完剩下的信息
之前的evn配置
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-es2015-modules-commonjs", "dynamic-import-node"]
}
}
但是并没有地方使用。
babel会 从按这个顺序找到一个环境变量值process.env.BABEL_ENV || process.env.NODE_ENV || "development"
然后用这个值做为key,在上面的env
配置中找到信息,合并到根对象中指定的选项之上。
但是项目中并没有设置 process.env.BABEL_ENV || process.env.NODE_ENV 的地方,所以不会解析test
里的内容,除非程序运行所在电脑上配置BABEL_ENV 环境变量为test
如果改成新版的形式: 因为不需要指定 stage的preset 所以去掉preset。 plugins里的 transform-es2015-modules-commonjs 改成 @babel/plugin-transform-modules-commonjs,dynamic-import-node不需要改。但是暂时不用test这些,整个env
配置都先去掉,后面要在test环境测试一些插件,再添加上。
开发环境修改完毕,测试下生产环境
npm run build
Error: Cannot find module 'del'
在package.lock文件中搜索这个模块,发现是webpack-dev-server的依赖,删除package.lock从新安装webpack-dev-server
npm run build
Error: webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead.
按官方文档添加splitChunks
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
automaticNameMaxLength: 30,
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
};
npm run build
.scss文件中一些sass的写法,报错
比如 双斜线注释//
会报错 需改成 /* */
; @import 下划线开头的scss文件,需要严格写文件名包括后缀,否则报错
npm run build
报错
⠧ building for production.../Users/treePro/develop/X/good_things/common-manage-platform-vue/node_modules/copy-webpack-plugin/dist/index.js:127
compilation.contextDependencies.push(context);
^
TypeError: compilation.contextDependencies.push is not a function
更新copy-webpack-plugin
"copy-webpack-plugin": "4.0.1", => "copy-webpack-plugin": "5.0.4",
npm i
npm run build 打包成功
本地启动nginx服务,制定dist目录位置静态服务目录
浏览器访问 http://localhost/index.html
// 报错
index.js?t=1568122675619:86 GET http://localhost/1909100108/static/js/manifest.js net::ERR_ABORTED 404 (Not Found)
发现 manifest.js 和 vendor.js 没有导出
查了一通
// 去掉 output 里的 chunkFilename: utils.assetsPath('js/[id].js')
// optimization 添加 runtimeChunk
optimization: {
runtimeChunk: {
name: "manifest" // 所有生成 chunk 之间共享的运行时文件打包到 manifest
},
npm run build 正常h输出manifest.js 和 vendor.js
刷新页面发现样式错乱,调试发现 .vue文件里的scss没有提取出来。猜测是不是mini-css-extract-plugin的问题
发现/build/utils.js 中有问题
return [
{
loader: MiniCssExtractPlugin.loader
},
'css-loader'
]
// 改成
return [MiniCssExtractPlugin.loader].concat(loaders)
npm run build 刷新页面,正确