夜听城嚣 夜听城嚣
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏

dwfrost

前端界的小学生
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏
  • 抓包工具

  • 知识管理

  • 编辑器工具

  • 工程部署

  • 团队规范

    • VSCode使用Eslint
    • husky+lint-staged+commitlint规范代码提交
    • uniapp小程序模板搭建
    • webpack3老项目升级为vite实践
    • webpack3项目升级webpack4实践
      • 梳理
        • build
        • client
        • config
        • mock
        • template
        • 零散文件
      • 改造过程
        • 开发优化
        • 构建优化
        • 其他问题
        • 总结
    • webpack4项目升级webpack5实践
    • 提交代码时修改commit消息
  • 效率工具
  • 团队规范
frost
2021-11-30

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"
  },
1
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
1
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
1
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
1
2
3
4
5
6
7

# config

config
├── dev.env.js
├── index.js
├── pre.env.js
├── prod.env.js
└── test.env.js
1
2
3
4
5
6

# mock

mock
├── data
│   ├── ^datalist.js
│   └── ^login.js
└── mockServer.js
1
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
1
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
1

报错

webpack-dev-server.png

# 升级webpack-dev-server

cnpm i webpack-dev-server@3 -D
1

依然报错

Error: Cannot find module 'webpack-cli/bin/config-yargs'
1

需要降级webpack-cli.

# 降级webpack-cli

参考 (opens new window)

cnpm i webpack-cli@3.3.12 -D
1

继续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
1
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
1

继续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)
1
2
3
4
5

# 升级vue-loader

cnpm i vue-loader vue-template-compiler -D
1

继续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;
1
2
3
4
5
6
7
8
9
10
11

此时vue-loader版本如下

    "vue-loader": "^15.9.8",
	"vue-template-compiler": "^2.6.14",
1
2

因为HappyPack无法加速vue-loader15,见issue (opens new window)。

# 去除happypack,安装thread-loader

cnpm i thread-loader -D
1

继续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;
1
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(),
        ]
1
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  

1
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'
                    ]
                },
1
2
3
4
5
6
7

如果是生产模式,可以引入MiniCssExtractPlugin插件,安装并使用

cnpm i mini-css-extract-plugin@1 -D
1
// webpack.dev.config.js
plugins: [
    		new MiniCssExtractPlugin({
                filename: 'css/[name].css',
                chunkFilename: 'css/[name].css',
                ignoreOrder: false
            }),
]
1
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 |
1
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',
                    ],
                },
1
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
}
1
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
1

运行,报错

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
1
2
3
4
5

是版本太高,需要降级,见issue (opens new window)。

cnpm i terser-webpack-plugin@4 -D
1

发现打包js的同时,也会打包LICENSE.

		  goodsCenter/js/0.ff46af08c1d4ad39b430.js     69 KiB       0  [emitted] [immutable]
          goodsCenter/js/0.ff46af08c1d4ad39b430.js.LICENSE.txt  158 bytes          [emitted]
1
2

配置terser,处理之。

        optimization: {
            minimizer: [
                new TerserPlugin({
                    cache: true, // 开启缓存
                    parallel: true, // 是否并行压缩
                    extractComments: false, // 是否把注释提取到关联的LICENSE文件
                }),
            ],
        },
1
2
3
4
5
6
7
8
9

# 升级copy-webpack-plugin

cnpm i copy-webpack-plugin -D
1
"copy-webpack-plugin": "^10.0.0",
1

运行报错

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:
1
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: ['.*'],
                },
            ]),
1
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`),
                    },
                ],
            }),
1
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
1
2
3
4
5

参考issue (opens new window),copy-webpack-plugin@7及以上只支持webpack5,所以降级到6。

cnpm i copy-webpack-plugin@6 -D
1

运行成功。

# 升级css-loader

cnpm i css-loader -D
1
    "css-loader": "^6.5.1",
1

但安装时报webpack版本低

peerDependencies WARNING css-loader@latest requires a peer of webpack@^5.0.0 but webpack@4.46.0 was installed
1

降级处理

cnpm i css-loader@5 -D
1

# 是否使用hard-source-webpack-plugin(不使用)

cnpm i hard-source-webpack-plugin -D
1
    "hard-source-webpack-plugin": "^0.13.1",
1
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')

...
        plugins: [
            new HardSourceWebpackPlugin(),
		]
1
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)
1
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/
                }
            ])
1
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%左右。

注:上述数据只是随机统计,并未使用平均数来统计。

上次更新: 2021/12/07, 19:00:32
webpack3老项目升级为vite实践
webpack4项目升级webpack5实践

← webpack3老项目升级为vite实践 webpack4项目升级webpack5实践→

最近更新
01
提交代码时修改commit消息
04-09
02
如何快速定位bug
02-20
03
云端web项目开发踩坑
08-25
更多文章>
Theme by Vdoing | Copyright © 2021-2025 dwfrost | 粤ICP备2021118995号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×