webpack3项目升级webpack4实践
# webpack3项目升级webpack4实践
先贴package.json中的命令
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js --env.pageName",
"start": "npm run dev",
"lint": "eslint --ext .js,.vue src",
"build": "node build/build-all.js --buildTarget=prod && npm run build:vendor prod",
"build:one": "node build/build.js --buildTarget=prod --pageName",
"build:vendor": "webpack --config build/build-vendor/build.js --env.target ",
"static-server": "http-server ./dist",
"express": "node build/bin/server.js",
"hooks": "node build/bin/hooks.js",
"mock": "node mock/mockServer.js",
"dev:mock": "concurrently \"npm run dev\" \"npm run mock\"",
"deva": "node build/dev-server.js",
"builda": "node build/build-new.js"
},
"devDependencies": {
"@babel/core": "^7.7.5",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/polyfill": "^7.7.0",
"@babel/preset-env": "^7.7.6",
"@babel/runtime": "^7.7.6",
"autoprefixer": "^7.1.2",
"babel-eslint": "^10.0.3",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^8.0.6",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-vue-jsx": "^3.7.0",
"body-parser": "^1.19.0",
"chalk": "^2.0.1",
"chokidar": "^3.2.2",
"concurrently": "^5.0.0",
"copy-webpack-plugin": "^4.0.1",
"core-js": "^2.6.11",
"css-loader": "^0.28.0",
"el-tree-transfer": "^2.3.0",
"eslint": "^4.15.0",
"eslint-config-standard": "^10.2.1",
"eslint-friendly-formatter": "^3.0.0",
"eslint-loader": "^1.7.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-node": "^5.2.0",
"eslint-plugin-promise": "^3.4.0",
"eslint-plugin-standard": "^3.0.1",
"eslint-plugin-vue": "^4.0.0",
"execa": "^5.0.0",
"express": "^4.17.1",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"happypack": "^5.0.1",
"html-webpack-plugin": "^2.30.1",
"http-proxy-middleware": "^0.20.0",
"http-server": "^0.11.1",
"inquirer": "^7.3.3",
"node-notifier": "^5.1.2",
"node-sass": "^4.12.0",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"os": "^0.1.1",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"sass-loader": "^7.3.1",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"socket.io": "^3.0.4",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
原项目工程包含命令比较丰富,包括开发、部署、本地起mock服务等,本地改造只针对开发和部署。
# 梳理
梳理原项目框架,一级目录如下:
project
├── build
├── build-protos.js
├── client
├── config
├── dist
├── index.html
├── mock
├── node_modules
├── package-lock.json
├── package.json
├── README.md
├── src
├── template
└── yarn.lock
2
3
4
5
6
7
8
9
10
11
12
13
14
15
其中涉及到框架部分如下:
# build
build
├── bin
│ ├── build-route.js
│ ├── hookPreCommit.js
│ ├── hooks.js
│ ├── index.js
│ ├── messageTemplate.js
│ ├── server.js
│ └── template
│ ├── gitmessage.text
│ └── pre-commit
├── build-all.js
├── build-new.js
├── build-vendor
│ ├── build.js
│ ├── element-ui.2.14.js
│ ├── vendor.js
│ └── vue.js
├── build.js
├── check-versions.js
├── dev-server.js
├── helper.js
├── logo.png
├── utils.js
├── vue-loader.conf.js
├── webpack.base.conf.js
├── webpack.dev.conf.js
└── webpack.prod.conf.js
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# client
client
├── element-ui
│ ├── fonts
│ │ ├── element-icons.535877f.woff
│ │ └── element-icons.732389d.ttf
│ └── index.css
└── vue-package.js
2
3
4
5
6
7
# config
config
├── dev.env.js
├── index.js
├── pre.env.js
├── prod.env.js
└── test.env.js
2
3
4
5
6
# mock
mock
├── data
│ ├── ^datalist.js
│ └── ^login.js
└── mockServer.js
2
3
4
5
# template
template
├── temp
│ ├── App.vue
│ ├── index.html
│ ├── main.js
│ ├── router
│ │ └── index.js
│ ├── store
│ │ └── index.js
│ └── views
│ ├── view1.vue
│ └── view2.vue
└── template.js
2
3
4
5
6
7
8
9
10
11
12
13
# 零散文件
# build-protos.js
# index.html
# 改造过程
主要参考记录一次webpack3升级到webpack4过程 (opens new window),并结合自己的实践,过程如下。
# 开发优化
# 升级webpack@4
cnpm i webpack@4 webpack-cli -D
报错
webpack-dev-server.png
# 升级webpack-dev-server
cnpm i webpack-dev-server@3 -D
依然报错
Error: Cannot find module 'webpack-cli/bin/config-yargs'
需要降级webpack-cli.
# 降级webpack-cli
cnpm i webpack-cli@3.3.12 -D
继续npm run dev
,又报错
Happy[vue]: All set; signaling webpack to proceed.
10% building 1/1 modules 0 active(node:8320) DeprecationWarning: Tapable.apply is deprecated. Call apply on the plugin directly instead
11% building 15/18 modules 3 active F:\h5-webpack4\node_modules\_events@3.3.0@events\events.jsF:\h5-webpack4\node_modules\_html-webpack-plugin@2.30.1@html-webpack-plugin\lib\compiler.js:81
var outputName = compilation.mainTemplate.applyPluginsWaterfall('asset-path', outputOptions.filename, {
^
TypeError: compilation.mainTemplate.applyPluginsWaterfall is not a function
2
3
4
5
6
7
原因是html-webpack-plugin需要升级,stackoverflow (opens new window)
# 升级html-webpack-plugin
cnpm i html-webpack-plugin@4.4.0 -D
继续npm run dev
,又报错
error in ./src/common/components/rp-upload.vue
Module build failed (from ./node_modules/_vue-loader@13.7.3@vue-loader/lib/template-compiler/index.js):
TypeError: Cannot read property '__vueOptions__' of undefined
at Object.module.exports (F:\h5-webpack4\node_modules\_vue-loader@13.7.3@vue-loader\lib\template-compiler\index.js:14:35)
2
3
4
5
# 升级vue-loader
cnpm i vue-loader vue-template-compiler -D
继续npm run dev
,又报错
error in ./src/pages/goodsCenter/views/videoSpace/video-view.vue?vue&type=style&index=0&id=273b7afc&lang=scss&scoped=true&
Module parse failed: Unexpected token (96:0)
File was processed with these loaders:
* ./node_modules/_happypack@5.0.1@happypack/loader.js
You may need an additional loader to handle the result of these loaders.
|
|
> .title-h {
| padding-bottom: 15px;
| margin-bottom: 20px;
2
3
4
5
6
7
8
9
10
11
此时vue-loader
版本如下
"vue-loader": "^15.9.8",
"vue-template-compiler": "^2.6.14",
2
因为HappyPack无法加速vue-loader15,见issue (opens new window)。
# 去除happypack,安装thread-loader
cnpm i thread-loader -D
继续npm run dev
,又报错
error in ./src/pages/goodsCenter/views/goodsSpecs/add.vue?vue&type=style&index=0&id=1d241506&lang=scss&scoped=true&
Module parse failed: Unexpected token (96:0)
File was processed with these loaders:
* ./node_modules/_thread-loader@2.1.3@thread-loader/dist/cjs.js
* ./node_modules/_vue-loader@15.9.8@vue-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
|
|
> .title-h {
| padding-bottom: 15px;
| margin-bottom: 20px;
2
3
4
5
6
7
8
9
10
11
12
提示thread-loader
不生效,这里定位了很久,以为是thread-loader
版本或者vue-loader
版本不兼容的问题,查阅官网 (opens new window)才发现,vue-loader@15
的写法不一样了。
// webpack.dev.config.js
const { VueLoaderPlugin } = require('vue-loader');
...
plugins: [
new VueLoaderPlugin(),
]
2
3
4
5
6
7
继续npm run dev
,又报错
error in ./node_modules/_quill@1.3.7@quill/dist/quill.snow.css
Module parse failed: Unexpected token (7:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| * Copyright (c) 2013, salesforce.com
| */
> .ql-container {
| box-sizing: border-box;
| font-family: Helvetica, Arial, sans-serif;
@ ./node_modules/_babel-loader@8.2.3@babel-loader/lib!./node_modules/_vue-loader@14.2.4@vue-loader/lib/selector.js?type=script&index=0!./src/pages/goodsCenter/components/editor/index.vue 28:0-35
2
3
4
5
6
7
8
9
10
11
12
原因是业务中的富文本编辑器vue-quill-editor
直接引入了css,需要有css-loader
# 解析sass、css
在webpack.dev.config.js中加入css-loader
{
test: /.css$/,
use: [
'vue-style-loader',
'css-loader'
]
},
2
3
4
5
6
7
如果是生产模式,可以引入MiniCssExtractPlugin插件,安装并使用
cnpm i mini-css-extract-plugin@1 -D
// webpack.dev.config.js
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].css',
chunkFilename: 'css/[name].css',
ignoreOrder: false
}),
]
2
3
4
5
6
7
8
全局打包时npm run dev
,又报错
error in ./src/pages/firmCenter/views/onlineSeek/seekChat/components/chat/index.scss
Module build failed (from ./node_modules/_mini-css-extract-plugin@1.6.2@mini-css-extract-plugin/dist/loader.js):
ModuleBuildError: Module build failed (from ./node_modules/_css-loader@0.28.11@css-loader/index.js):
Unknown word (209:2)
207 | // left : 2px;
208 | // padding-left: 5px;
> 209 | // }
210 | }
211 |
2
3
4
5
6
7
8
9
10
11
原因是没有处理scss文件,其实sass-loader和node-sass已经安装,但这里需要解析下。
{
test: /.(c|sc|sa)ss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader',
],
},
2
3
4
5
6
7
8
即可,重新运行。
运行成功!
# 对比开发页面的速度
webpack3 | webpack4 | |
---|---|---|
单个项目第一次构建 | 16548ms | 12176ms |
单个项目第二次构建 | 11625ms、9656ms | 9237ms、8918ms |
全局项目第一次构建 | 61911ms | 36287ms |
全局项目第二次构建 | 48798ms、51237ms | 26865ms、25872ms |
以上是随机统计,可以看到在大型项目上,开发效率有明显提升,提速在70%-98%之间。
# 构建优化
先打包一个项目: npm run build:one goodsCenter
F:\h5-webpack4\node_modules\_webpack@4.46.0@webpack\lib\webpack.js:189
throw new RemovedPluginError(errorMessage);
^
RemovedPluginError: webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead.
at Object.get [as CommonsChunkPlugin] (F:\h5-webpack4\node_modules\_webpack@4.46.0@webpack\lib\webpack.js:189:10)
at module.exports (F:\h5-webpack4\build\webpack.prod.conf.js:221:34)
at F:\h5-webpack4\build\build.js:20:11
at next (F:\h5-webpack4\node_modules\_rimraf@2.7.1@rimraf\rimraf.js:83:7)
at CB (F:\h5-webpack4\node_modules\_rimraf@2.7.1@rimraf\rimraf.js:119:9)
at FSReqCallback.oncomplete (fs.js:180:23) {
details: undefined,
missing: undefined,
origin: undefined,
dependencies: undefined,
module: undefined
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CommonsChunkPlugin插件在webpack4已移除,使用optimization.splitChunks替代。
# 移除CommonsChunkPlugin
注释相关代码即可
# 使用optimization.splitChunks
参考官方文档split-chunks-plugin (opens new window)
# 使用terser-webpack-plugin
cnpm i terser-webpack-plugin -D
运行,报错
F:\h5-webpack4\node_modules\_terser-webpack-plugin@5.2.5@terser-webpack-plugin\dist\index.js:753
const hooks = compiler.webpack.javascript.JavascriptModulesPlugin.getCompilationHooks(compilation);
^
TypeError: Cannot read property 'javascript' of undefined
2
3
4
5
是版本太高,需要降级,见issue (opens new window)。
cnpm i terser-webpack-plugin@4 -D
发现打包js的同时,也会打包LICENSE.
goodsCenter/js/0.ff46af08c1d4ad39b430.js 69 KiB 0 [emitted] [immutable]
goodsCenter/js/0.ff46af08c1d4ad39b430.js.LICENSE.txt 158 bytes [emitted]
2
配置terser,处理之。
optimization: {
minimizer: [
new TerserPlugin({
cache: true, // 开启缓存
parallel: true, // 是否并行压缩
extractComments: false, // 是否把注释提取到关联的LICENSE文件
}),
],
},
2
3
4
5
6
7
8
9
# 升级copy-webpack-plugin
cnpm i copy-webpack-plugin -D
"copy-webpack-plugin": "^10.0.0",
运行报错
F:\h5-webpack4\node_modules\_schema-utils@4.0.0@schema-utils\dist\validate.js:115
throw new _ValidationError.default(errors, schema, configuration);
^
ValidationError: Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options[0] has an unknown property 'ignore'. These properties are valid:
2
3
4
5
6
是升级后写法有变,参考文档 (opens new window)修改。
// 原写法
// 复制全局静态资源static文件加下的内容到页面文件夹中
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../src/static'),
to: config.build.assetsSubDirectory,
// to: pageName,
ignore: ['.*'],
},
]),
// 复制页面自己的静态资源static文件加下的内容到页面文件夹中
new CopyWebpackPlugin([
{
from: path.resolve(process.cwd(), `./src/pages/${pageName}/public`),
// to: config.build.assetsSubDirectory,
to: path.resolve(__dirname, `../dist/${pageName}/public`),
ignore: ['.*'],
},
]),
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
改为
new CopyWebpackPlugin({
patterns: [
{ from: path.resolve(__dirname, '../src/static'), to: config.build.assetsSubDirectory },
{
from: path.resolve(process.cwd(), `./src/pages/${pageName}/public`),
to: path.resolve(__dirname, `../dist/${pageName}/public`),
},
],
}),
2
3
4
5
6
7
8
9
运行,报错
F:\h5-webpack4\node_modules\_copy-webpack-plugin@10.0.0@copy-webpack-plugin\dist\index.js:473
const cache = compilation.getCache("CopyWebpackPlugin");
^
TypeError: compilation.getCache is not a function
2
3
4
5
参考issue (opens new window),copy-webpack-plugin@7及以上只支持webpack5,所以降级到6。
cnpm i copy-webpack-plugin@6 -D
运行成功。
# 升级css-loader
cnpm i css-loader -D
"css-loader": "^6.5.1",
但安装时报webpack版本低
peerDependencies WARNING css-loader@latest requires a peer of webpack@^5.0.0 but webpack@4.46.0 was installed
降级处理
cnpm i css-loader@5 -D
# 是否使用hard-source-webpack-plugin(不使用)
cnpm i hard-source-webpack-plugin -D
"hard-source-webpack-plugin": "^0.13.1",
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
...
plugins: [
new HardSourceWebpackPlugin(),
]
2
3
4
5
6
第一次运行,没有使用到缓存,正常编译。
第二次运行,报错。
building for production...internal/crypto/hash.js:84
throw new ERR_INVALID_ARG_TYPE(
^
TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received an instance of Object
at Hash.update (internal/crypto/hash.js:84:11)
at BulkUpdateDecorator.update (F:\h5-webpack4\node_modules\_webpack@4.46.0@webpack\lib\util\createHash.js:61:14)
at CssModule._computeHash (F:\h5-webpack4\node_modules\_mini-css-extract-plugin@1.6.2@mini-css-extract-plugin\dist\index.js:145:14)
2
3
4
5
6
7
8
堆栈报错信息提示与MiniCssExtractPlugin协作有问题,将其注释,可正常运行,从官方文档找到了解决方案 (opens new window),将其排除即可。
...
// 新增下面代码即可
new HardSourceWebpackPlugin.ExcludeModulePlugin([
{
// @see https://github.com/mzgoddard/hard-source-webpack-plugin#excludemoduleplugin
test: /mini-css-extract-plugin[\\/]dist[\\/]loader/
}
])
2
3
4
5
6
7
8
重新打包
第一次:1min27s
第二次:1min2s
对比使用HardSourceWebpackPlugin前后速度(不使用时是1min左右),决定不使用该插件。可能因为在寻找以及排除相关loader(mini-css-extract-plugin)过程中,编译速度反而变慢了。
# 其他问题
# package-lock.json未更新
这个是在另一个目录测试的时候发现的,重新克隆项目之后,发现安装的依赖还是之前的,一看package-lock.json
,发现丝毫未动。
于是在先前的webpack4分支进行调整。
试了许多办法,期间发现无法修改和生成package-lock.json,后来删除package-lock.json,然后重新npm install
,就突然好了。下面是可能可行的操作:
删除package-lock.json,重新
npm install
升级node为最新稳定版本(LTS)
使用命令
npm install --package-lock
安装使用命令
npm install --package-lock-only
由于之前使用
cnpm install
安装,导致npm uninstall
无法卸载依赖。于是新增.npmrc
文件来切换镜像registry=https://registry.npm.taobao.org sass_binary_site=https://npm.taobao.org/mirrors/node-sass
1
2
之后直接使用npm install即可。
# 总结
升级webpack3至webpack4应该在19年就可以完成的,但历史包袱留到了现在。可以看到升级之后,性能有了很大提升,对业务的影响暂时还未发现。提效如下:
webpack3 | webpack4 | |
---|---|---|
dev单个项目第一次构建 | 16548ms | 12176ms |
dev单个项目第二次构建 | 11625ms、9656ms | 9237ms、8918ms |
dev全局项目第一次构建 | 61911ms | 36287ms |
dev全局项目第二次构建 | 48798ms、51237ms | 26865ms、25872ms |
build单个项目第一次构建 | 22.39s | 17.78s |
build单个项目第二次构建 | 20.12s | 17.42s |
build全局项目第一次构建 | 1min44s | 1min17s |
build全局项目第二次构建 | 1min43s | 1min03s |
升级之后,开发构建效率提升在70%-98%左右,打包构建效率提升在35%-63%左右。
注:上述数据只是随机统计,并未使用平均数来统计。