webpack3升级到webpack5

参考地址:webpack.docschina.org

依赖调整

包名 处理方式 版本
webpack 升级 5.51.0
webpack-cli 新增 4.10.0
webpack-dev-server 升级 4.7.4
webpack-merge 升级 5.8.0
babel-loader 升级 8.2.5
autoprefixer 升级 10.4.7
babel-core 升级 7.0.0-bridge.0
babel-eslint 升级 9.0.0
babel-jest 升级 23.4.2
compression-webpack-plugin 升级 6.1.0
copy-webpack-plugin 升级 9.1.0
css-loader 升级 5.2.7
css-minimizer-webpack-plugin 新增 3.4.1
eslint 升级 7.32.0
eslint-loader 废弃 --
eslint-plugin-vue 升级 7.20.0
extract-text-webpack-plugin 废弃 --
eslint 升级 7.32.0
eslint-webpack-plugin 新增 3.1.1
file-loader 废弃 --
html-webpack-plugin 升级 5.5.0
happypack 废弃 --
less-loader 升级 8.1.1
less 升级 4.1.2
mini-css-extract-plugin 新增 1.6.2
optimize-css-assets-webpack-plugin 废弃 --
postcss-import 升级 14.1.0
postcss-loader 升级 6.2.1
postcss-url 升级 10.1.3
scss-loader 升级 10.2.0
uglify-webpack-plugin 废弃 --
url-loader 废弃 --
style-loader 新增 3.3.1
terser-webapck-plugin 新增 5.3.3
vue-loader 新增 15.10.0
vue-style-loader 废弃 --

由于涉及到babel相关依赖包比较多,暂时没有详细列出。如果不想手动更新babel相关依赖包的版本,可以使用babel-upgrade自动更新.babelrc配置以及package.json依赖包。

  • 使用babel-upgrade的方法
js 复制代码
// 1. 安装 babel-upgrade
npm install babel-upgrade
// 2. 运行babel-upgrade的更新命令
npx babel-upgrade --write
// 3. 输出的日志详情
updating .babelrc config at .babelrc
==========================================
{
    "presets": [
        [
            "env",
 +          "@babel/preset-env",
            {
                "modules": false,
                "targets": {
                    "browsers": ["> 1%", "last 15 versions", "not ie <= 8"]
                }
            }
-       ],
-       "stage-1",
 +      ]
    ],
    "plugins": [
        "transfrom-vue-js",
-       "transfrom-runtime",
-       "syntax-decorators-legacy",
-       "transfrom-class-properties",
+       [
+           "@babel/plugin-transfrom-runtime",
+           {
+               "corejs": 2
+           }
+       ],
+       "@babel/plugin-syntax-dynamic-import"
+       [
+           "@babel/plugin-proposal-decorators",
+           {
+               "legacy": true
+           }
+       ],
+       "@babel/plugin-proposal-function-sent",
+       "@babel/plugin-proposal-export-namespace-from",
+       "@babel/plugin-proposal-numeric-separator",
+       "@babel/plugin-proposal-throw-expressions",
+       "@babel/plugin-proposal-export-default-from",
+       "@babel/plugin-proposal-optional-chaining",
+       [
+           "@babel/plugin-proposal-pipeline-operator",
+           {
+               "proposal": "minimal"
+           }
+       ],
+       "@babel/plugin-proposal-mullish-coalescing-operator",
+       "@babel/plugin-proposal-do-expressions"
    ],
    "env": {
        "test": {
            "presets": [
-               "env",
-               "stage-1"
+               "@babel/preset-env"
            ],
            "plugins": [
                "transform-vue-js",
-               "transform-es2015-modules-commonjs",
+               "@babel/plugin-transform-modules-commonjs",
                "dynamic-import-node",
-               "istanbul"
-               "istanbul",
+               "@babel/plugin-syntax-dynamic-import",
+               "@babel/plugin-syntax-import-meta",
+               "@babel/plugin-proposal-class-properties",
+               "@babel/plugin-proposal-json-strings",
+               [
+                   "@babel/plugin-proposal-descorator",
+                   {
                        "legacy": true
+                   }
+               ],
+               "@babel/plugin-proposal-function-sent",
+               "@babel/plugin-proposal-export-namesapce-from",
+               "@babel/plugin-proposal-numeric-separator",
+               "@babel/plugin-proposal-throw-expressions",
+               "@babel/plugin-proposal-export-default-from",
+               "@babel/plugin-proposal-logical-assignment-operators",
+               "@babel/plugin-proposal-optional-chaining",
+               [
+                   "@babel/plugin-proposal-pipeline-operator",
+                   {
+                       "proposal": "minimal"
+                   }
+               ],
+               "@babel/plugin-proposal-mullish-coalescing-operator",
+               "@babel/plugin-proposal-do-expressions"
            ]
        }
    }
}
// updating closest package.json dependencies
"devDependencies": {
+   "@babel/core": "^7.0.0",
+   "@babel/plguin-proposal-class-properties": "^7.0.0",
+   "@babel/plugin-proposal-decorators": "^7.0.0",
+   "@babel/plugin-proposal-do-expressions": "^7.0.0",
+   "@babel/plugin-proposal-export-default-from": "^7.0.0",
+   "@babel/plugin-proposal-export-namespace-from": "^7.0.0",
+   "@bable/plugin-proposal-function-sent": "^7.0.0",
+   "@babel/plugin-proposal-json-strings": "^7.0.0",
+   "@babel/plugin-proposal-logical-assignment-operators": "^7.0.0",
+   "@babel/plugin-proposal-nullish-coalescing-opertaor": "^7.0.0",
+   "@babel/plugin-proposal-numeric-separator": "^7.0.0",
+   "@babel/plugin-proposal-optional-chaining": "^7.0.0",
+   "@babel/plugin-proposal-pipeline-operator": "^7.0.0",
+   "@babel/plugin-proposal-throw-expressions": "^7.0.0",
+   "@babel/plugin-syntax-dynamic-import": "^7.0.0",
+   "@babel/plugin-syntax-import-meta": "^7.0.0",
+   "@babel/plugin-transform-modules-commonjs": "^7.0.0",
+   "@babel/plugin-transform-runtime": "^7.0.0",
+   "@babel/preset-env": "^7.0.0",
+   "@babel/register":  "^7.0.0",
    "autoperfixer": "^10.4.7",
-   "babel-core": "^6.22.1",
-   "babel-esint": "^8.2.1",
+   "babel-core": "^7.0.0-bridge.0",
+   "babel-eslint": "^9.0.0",
    "babel-helper-vue-jsx-merge-props": "^2.0.3",
-   "babel-jest": "^21.0.2",
+   "babel-jest": "^23.4.2",
    "babel-loader": "^8.2.5",
    "babel-plugin-dynamic-import-node": "^1.2.0",
    "babel-plugin-istanbul": "^4.1.5",
-   "babel-plugin-syntax-dynamic-import": "^6.18.0",
-   "babel-plugin-syntax-jsx": "^6.18.0",
-   "babel-plugin-transform-class-properties": "^6.24.1",
-   "babel-plugin-transform-decorators-legacy": "^1.3.4",
-   "babel-plugin-es2015-modules-commonjs": "^6.26.0",
-   "babel-plugin-transform-runtime": "^6.22.0",
    "babel-plugin-transform-vue-jsx": "^3.5.0",
-   "babel-preset-env": "^1.3.2",
-   "babel-preset-stage-1": "^6.24.1",
-   "babel-preset-stage-2": "^6.22.0",
-   "babel-register": "^6.22.0",
    "chalk": "^2.0.1",
}

webpack 配置

plugin 以及配置项调整

  • webpack3 升级到webpack5,webpack-dev-server版本也对应进行了升级,npm run dev的命令需要改为webpack serve
  • extract-text-webpack-plugin和optimize-css-assets-webpack-plugin已经过时,需要更新为mini-css-extract-plugin和css-minimizer-webpack-plugin\
  • UglifyJsPlugin已经过时,可以使用webpack5开箱的TerserPlugin
  • webpack5已支持资源模块替换url-loader, file-loader
  • webpack-merge更新版本之后,需要解构出merge,const { merge } = require('webpack-merge')
  • NamedModulesPlugin已移除,改为使用optimization.moduleIds: 'named'
  • NoEmitOnErrorsPlugig已移除,改为使用optimization.emitOnErrors: true
  • HashedModuleIdsPlugin已移除,改为使用optimization.moduleIds: 'hashed'
  • CommonsChunkPlugin已移除,改为使用optimization.splitChunks
  • devtool的值需要进行调整
  • 由于vue-loader升级之后,原来css使用的/deep/需要调整为::deep
  • 配置项添加模式

配置项具体调整

添加模式

js 复制代码
module.exports = {
  // production: 生产模式  development: 开发模式
  mode: 'production'
}

插件

  • mini-css-extracct-plugin 配置
js 复制代码
plugins: [
    new MiniCssExtractPlugin({
        filename: utils.assetsPath('css[name].[contentHash].css')
    })
]

同时utils.js也需要做相应的调整

js 复制代码
loader: [MiniCssExtractPlugin.loader,...loader]
  • css-minimizer-webpack-plugin 配置
js 复制代码
optimization: {
    minimizer: [
        new CssMinimizerPlugin()   
    ]
}
plugins: [
    new CssMinimizerPlugin()   
]

如果想在开发环境中启用css优化,请设置optimization.minimize 设置为 true

yaml 复制代码
optimization: {
    minimize: true,
    minimizer: [
        new CssMinimizerPlugin()    
    ]
}
  • terser-webpack-plugin 配置
js 复制代码
optimization: {
    minimizer: {
        new TerserPlugin({
            terserOptions: {
                sourceMap: config.build.productionSourceMap,
                format: {
                    beautify: false,
                    comments: false
                },
                compress: {
                    drop_debugger: true,
                    drop_console: true
                }
            },
            extractComments: false,
            arallel: true
        })
    }
}
  • copy-webpack-plugin 配置
js 复制代码
// 调整前
new CopyWebpackPlugin([
    {
        from: path.resolve(__dirname, '../static'),
        to: config.dev.assetsSubDiractory,
        ignore: ['.*']
    }
])
// 调整后
new CopyWebpackPlugin({
    patterns: [
        {
            from: path.resolve(__dirname, '../static'),
            to: config.dev.assetsSubDiractory,
            globOptions: {
                ignore: ['.*']
            }
        }
    ]
})
  • eslint-webpack-plugin

eslint-loader已经被废弃,需要替换为eslint-webpack-plugin

js 复制代码
// webpack.base.conf.js
module.expots = {
    plugin: [
        new EslintPlugin({
            extensions: ['js', 'vue'],
            context: path.resolve(__dirname, '../'),
            files: [resolve('src'), resolve('test')]
            exclude: [resolve('src/components'), resolve('src/assets'), resolve('src/libs')],
            formatter: require('eslint-friendly-formater'),
            emitWarning: !config.dev.showEslintErrorsInOverlay
        })
    ]
}

devServer 配置

js 复制代码
// 调整前
devServer: {
    clientLogLevel: 'warning',
    overlay: config.dev.errorOverlay ? { warnings: false, errors: true } :false
}
// 调整后
devServer: {
    client: {
        logging: 'warn',
        overlay: config.dev.errorOverlay ? { warnings: false, errors: true } :false
    }
}
js 复制代码
// 调整前
devServer: {
    publicPath: config.dev.assetsPublicPath
}
// 调整后
devServer: {
    static: {
        directory: path.join(__dirname, './public'),
        publicPath: config.dev.assetsPublicPath
    }
}
js 复制代码
// 调整前
devServer: {
    watchOptions: {
        poll: config.dev.poll
    }
}
// 调整后
{
    watch: true,
    watch  Options: {
        aggregateTimeout: 200,
        ignored: ['**/node_modules/', '**/test/'],
        stdin: true,
        poll: config.dev.poll
    }
}
js 复制代码
// 去掉的配置
contentBase: false,
quiet: false

资源配置

  • 解析图片
js 复制代码
// 调整前
module: {
    rules: [
        ...,
        {
            test: /\.(png|jpe?g|gif|svg)(\?.*)?$,
            loader: "url-loader",
            options: {
                limit: 10000,
                name: utils.assetsPath('img/[name].[hash:7].[ext]')
            }
        },
        {
            test: /\.(mp4|webm|ogg|mp3|wav|flac|acc)(\?.*)?$/,
            loader: "url-loader",
            options: {
                limit: 10000,
                name: utils.assetsPath('img/[name].[hash:7].[ext]')
            }
        },
        {
            test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
            loader: "url-loader",
            options: {
                limit: 10000,
                name: utils.assetsPath('img/[name].[hash:7].[ext]')
            }
        }
    ]
}
// 调整后
module: {
    rules: [
        ...,
        {
            test: /\.(png|jpe?g|gif|svg)(\?.*)?$,
            type: 'asset',
            generator: {
                filename: utils.assetsPath('img/[name].[hash:7].[ext]')
            },
            parser: {
                dataUrlCondition: {
                    maxSize: 8 * 1024
                }
            }
        },
        {
            test: /\.(mp4|webm|ogg|mp3|wav|flac|acc)(\?.*)?$/,
            type: 'asset',
            generator: {
                filename: utils.assetsPath('media/[name].[hash:7].[ext]')
            },
            parser: {
                dataUrlCondition: {
                    maxSize: 8 * 1024
                }
            }
        },
        {
            test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,            
            type: 'asset',
            generator: {
                filename: utils.assetsPath('fonts/[name].[hash:7].[ext]')
            },
            parser: {
                dataUrlCondition: {
                    maxSize: 8 * 1024
                }
            }
        }
    ]
}
  • 压缩资源调整
js 复制代码
// 调整前
plugins: [
    new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
        minChunks(module) {
            return (
                module.resource &&
                /\.js$/.test(module.resource) &&
                module.resource.indexOf(
                    path.join(__dirname, '../node_modules')
                ) === 0
            )
        }
    }),
    new webpack.optimize.CommonsChunkPlugin({
        name: 'mainfest',
        minChunk: Infinity
    }),
    new webpack.optimize.CommonsChunkPlugin({
        name: 'app',
        async: 'vendor-async',
        children: true,
        minChunks: 3
    })
]
// 调整后
optimization: {
    splitChunks: {
        chunks: 'all',
        minSize: 30000,
        minChunks: 1,
        cacheGroups: {
            vendors: {
                name: 'vendors',
                test(module) {
                    return (
                        module.resource &&
                        /\.js$/.test(module.resource) &&
                        module.resource.indexOf(
                            path.join(__dirname, '../node_modules')
                        ) === 0
                    )
                }
            },
            styles: {
                name: 'styles',
                type: 'css/mini-extract',
                test: /\.(sc|c)ss$/,
                enforce: true
            },
            manifest: {
                name: 'manifest',
                minChunks: Infinity
            },
            app: {
                name: 'app',
                minChunks: 3
            }
        }
    }
}

devtool 调整

js 复制代码
'cheap-module-eval-source-map' -> 'eval-cheap-module-source-map'

命令行 API

js 复制代码
// 调整前
"dev": "webpack-webpack-server --line --progress --config build/webpack.dev.conf.js --host 0.0.0.0"
// 调整后
"dev": "webpack serve --progress --config build/webpack.dev.conf.js --host 0.0.0.0"

node

js 复制代码
// 调整前
node: {
    setImmediate: false,
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
}
// 调整后
node: {
    global: true
}

在之前的配置文件中使用了类似于node.fs: 'empty',需要改为resolve.fallback.fs: false

js 复制代码
resolve: {
    fallback: {
        fs: false
    }
}

loader

  • vue-loader

升级vue-loader@15.x版本时,需要在配置文件中new HtmlWebpackPlugin()后面添加new VueLoaderPlugin()

javascript 复制代码
const { VueLoaderPlugin } = require('vue-loader')
plugin: [
    new HtmlWebpackPlugin(),
    new VueLoaderPlugin()
]
  • sass-loader

在utils.js文件进行调整

js 复制代码
// 调整前
return {
    ...,
    sass: generateLoaders('sass', {
        indentedSyntax: true,
        data: '@import "common/css/_variable";',
        includePaths: [
            path.join(__dirname, '../src')
        ]
    }),
    scss: generateLoaders('scss' {
        indentedSyntax: true,
        data: '@import "common/css/_variable";',
        includePaths: [
            path.join(__dirname, '../src')
        ]
    })
}
// 调整后
return {
    ...,
    sass: generateLoaders('sass', {
        additionalData: '@import "common/css/_variable";',
        sassOptions: {
            indentWidth: 4,
            includePaths: [
                Path.join(__dirname, '../src')
            ]
        }
    }),
    scss: generateLoadrs('scss', {
        additionalData: '@import "common/css/_variable";',
        sassOptions: {
            indentWidth: 4,
            includePaths: [
                Path.join(__dirname, '../src')
            ]
        }
    })
}

性能优化

参考文章: webpack5 - 性能优化

缓存

cache

通过配置cache: 'filesystem'对构建过程中的模块进行缓存,二次构建从缓存中拉取模块,缩短打包时间。

配置方式:

js 复制代码
// webpack.base.conf.js
module.exports = {
    cache: {
        type: 'filesystem'
    }
}

cache-loader

cache-loader已经被webapck5的cache或取代,不需要引入cache-loader

多进程

thread-loader

thread-loader的配置方式:

js 复制代码
// webpack.base.conf.js
module.export = {
    module: {
        rules [
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                include: [
                    resolve('src'),
                    resolve('test'),
                    resolve('node_modules/webpack-dev-server/client')
                ],
                use: [
                    {
                        loader: 'thread-loader',
                        options: {
                            works: os.cpus().length - 1
                        }
                    },
                    {
                        loader: 'babel-loader',
                        options: {
                            cacheDirectory: true
                        }
                    }
                ]
            }
        ]
    }
}

happypack

在webpack5中不推荐使用,请使用thread-loader

升级过程中遇到的报错

Happypack插件报错

js 复制代码
[webpack-cli] compiler.plugin is not a function 
at HappyPlugin.apply(..\node_modules\happypack\lib\HappyPlugin.js:95:12)

解决方法:

在webpack.base.conf.js文件中去掉Happypack插件

Postcss 依赖报错

js 复制代码
error: true is not a postcss plugin
at processor.normalize

解决办法:

安装postcss@8.1.10

core-js 依赖报错

js 复制代码
module not found: error: can't resolve '@babel/runtime-corejs2/core-js/object/keys' in ....

解决办法:

安装@babel/runtime-corejs2@7.18.9

参考文章:bael的兼容性方案

autoprefixer 双行注释报错

js 复制代码
warning: autoprefixer: second autoprefixer control comment was ignored. autoprefixer applies control comment to whole block, not to next rules.

解决方法:

将autoprefixer的两条注释中后面的一条注释去掉,改为一条注释

js 复制代码
// 修改前
/*! autoprefixer: off */
...
/*! autoprefixer: on */
// 修改后
/* autoprefixer: ignore next */
...

引用.css样式报错

js 复制代码
warning: in ./node_modules/handsontable/dist/handsontable.full.css
warning: postcss-import: @charset must precede al other statements

解决方法:

修改引用.css样式的位置,把引用放在样式表的第一行,

魔法注释报错

js 复制代码
ChunkLoaderError: loading chunk /dashboard faild. (error: http://dashboard.js/)
at Object.__webpack_require__.f.j

解决方法:

去取魔法注释中webpackChunkName内容

compression-weback-plugin 打包配置报错

asset配置报错

js 复制代码
webpack-socket-error validationError: Invalid options object. Compression Plugin has been initialized using an options that does not match the API scheema. 
- options has an unknow property 'asset'. These properties are valid: object { test?, include?, exclude?, algorithm?, compressionOptions?, threshold?, minRatio?, delteOriginalAssets?, filename?, cache? }

解决方法:

修改compression-webapck-plugin配置

js 复制代码
// 修改前
new CompressionWebpackPlugin({
    asset: '[path].gz[query]',
    algorithm: 'gzip',
    test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
    ),
    threshold: 10240,
    minRatio: 0.8
})
// 修改后
new CompressionWebpackPlugin({
    filename: '[path].gz[query]',
    algorithm: 'gzip',
    test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
    ),
    threshold: 10240,
    minRatio: 0.8
})

文件路径重复报错

js 复制代码
error in conflict: Mutiple assets emit different content to the same filename static/js/.gz

修改compression-webpack-plugin配置

js 复制代码
// 修改前
new CompressionWebpackPlugin({
    filename: '[path].gz[query]',
    algorithm: 'gzip',
    test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
    ),
    threshold: 10240,
    minRatio: 0.8
})
// 修改后
new CompressionWebpackPlugin({
    filename: '[path][base].gz',
    algorithm: 'gzip',
    test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
    ),
    threshold: 10240,
    minRatio: 0.8
})

html-webpack-plugin 打包配置报错

js 复制代码
(node:956) UnhandledPromiseRejectionWarning: Error: 'dependency' is not a valid chunk sort mode
at sortEntryChunks

修改方法:

修改htmlwebpack-plugin配置参数chunksSortMode的值改为auto,或者去掉chunksSortMode参数

参考文章:html-webpack-plugin

optimization.moduleIds 配置的值错误

js 复制代码
warning in configuration
The value 'hashed' for option 'optimization.moduleIds' is deprecated. Use 'deterministic' instead

解决方法:

修改optimization.moduleIds的值为deterministic

参考文章:webpack优化配置

相关推荐
q***718521 小时前
Webpack、Vite区别知多少?
前端·webpack·node.js
谢尔登1 天前
简单聊聊webpack摇树的原理
运维·前端·webpack
谢尔登1 天前
原来Webpack在大厂中这样进行性能优化!
前端·webpack·性能优化
醉方休3 天前
Webpack loader 的执行机制
前端·webpack·rust
带只拖鞋去流浪3 天前
迎接2026,重新认识Vue CLI (v5.x)
前端·vue.js·webpack
小奶包他干奶奶3 天前
Webpack学习——Loader(文件转换器)
前端·学习·webpack
小奶包他干奶奶3 天前
Webpack学习——原理理解
学习·webpack·devops
|晴 天|4 天前
Vite 为何能取代 Webpack?新一代构建工具的崛起
前端·webpack·node.js
带只拖鞋去流浪4 天前
迎接2026,重新认识Webpack5
前端·webpack
我也爱吃馄饨4 天前
写的webpack插件如何适配CommonJs项目和EsModule项目
java·前端·webpack