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优化配置

相关推荐
垣宇14 小时前
Vite 和 Webpack 的区别和选择
前端·webpack·node.js
小纯洁w1 天前
Webpack 的 require.context 和 Vite 的 import.meta.glob 的详细介绍和使用
前端·webpack·node.js
海盗强2 天前
Webpack打包优化
前端·webpack·node.js
祈澈菇凉2 天前
如何优化 Webpack 的构建速度?
前端·webpack·node.js
懒羊羊我小弟2 天前
常用 Webpack Plugin 汇总
前端·webpack·npm·node.js·yarn
祈澈菇凉3 天前
Webpack的持久化缓存机制具体是如何实现的?
前端·webpack·gulp
懒羊羊我小弟4 天前
Webpack 基础入门
前端·webpack·rust·node.js·es6
刽子手发艺4 天前
Selenium+OpenCV处理滑块验证问题
opencv·selenium·webpack
懒羊羊我小弟4 天前
常用Webpack Loader汇总介绍
前端·webpack·node.js
真的很上进6 天前
【1.8w字深入解析】从依赖地狱到依赖天堂:pnpm 如何革新前端包管理?
java·前端·vue.js·python·webpack·node.js·reactjs