对于Webpack的优化方案,可以从以下几个方面入手
- 提升体验
- 提升打包构建速度
- 减小包体积
- 优化代码运行性能
提升体验
-
Source Map
我们通过webpack打包后的文件一般都会经过一些处理,比如代码压缩,文件合并等,使得最终打包产生的文件与实际开发时有很大区别,在这种情况下,如果代码报错,根据浏览器提示的报错信息是无法知道出错的具体位置的。
Source Map就是为了解决这一问题
Source Map就是一个信息文件,存储着代码转换前的位置信息,浏览器通过这个文件,可以直接找到代码的原始位置,而不是转换后的代码,为开发带来很大的方便
const path = require('path');
module.exports = (env) => {
return {
mode: 'development',
entry: {
main: './src/main.js',
},
output: {
path: path.resolve(__dirname, './dist'),
clean: true,
},
// source-map配置
devtool: 'source-map',
}
}
Source Map常用配置项
- cheap-module-source-map : 只包含映射行,不包含列,打包速度块,适合生产模式
- source-map:包含行/列映射,打包速度慢,适合开发模式
提升打包构建速度
- HotModuleReplacement(热更新):开发时只编译打包发生变化的代码,对于不变的代码使用缓存,提高更新速度。
只需要在webpack-dev-server中将hot设置true即可,webpack4.0以上默认配置。 - OneOf: 在进行资源模块处理时,通过OneOf配置资源被某loader处理就不会继续遍历了。
module.exports = { //解析资源模块 module: { rules: [ //... { //oneOf: 只使用第一个匹配的规则 oneOf: [ { test: [/\.css$/], use: getStyleLoaders() }, { test: [/\.s[ac]ss$/], use: getStyleLoaders("sass-loader") }] } ] } }
- Include/extend:只包含或排除某文件,使处理文件更少,提高打包速度
比如这里通过include配置只针对当前Src文件下的js/ts/tsx文件做进行babel代码转换module: { { test: [/\.(tsx?|js)$/], // babel- loader:转换js代码 include: paths.appSrc, loader: require.resolve('babel-loader'), options: { //对指定目录的转换做缓存 cacheDirectory: true, //... }, }, }
- Cache:对babel和eslint处理结果缓存,提高打包速度
- runtimeChunk:生成一个runtime文件用来存储所有文件地址,当一个文件发生变化时,只更新变化文件和runtime文件,依赖这个文件的其他文件不受影响。
output: { path: paths.appBuild, filename: isEnvProduction ? 'static/js/[name].[contenthash:8].js' : isEnvDevelopment && 'static/js/[name].js', clean: true, //懒加载包名称(用到时加载) chunkFilename: isEnvProduction ? 'static/js/[name].[contenthash:8].chunk.js' : isEnvDevelopment && 'static/js/[name].chunk.js', assetModuleFilename: 'static/media/[name].[contenthash:8][ext][query]', }, //....
首先在设置输出的文件名时,通过contenthash保证文件名根据文件内容进行变化来设置缓存。
同时配置runtimeChunk,他会将文件之间的依赖关系(文件地址 hash值)提取成单独的一个runtime文件,保证A文件变化时, 只有runtime文件和A文件发生变化,其他文件不变。optimization: { runtimeChunk: isEnvProduction && { // 保存引入文件的模块的依赖文件,当引入模块更新时,只需更新当前文件和runtime即可, 无需更新其他依赖文件 name: (entrypoint) => `runtime~${entrypoint.name}` //entrypoint 入口文件 } }
- Thead:多进程处理eslint和babel,提高速度(需根据实际文件大小进行衡量)
减小包体积
- tree-shaking:移除JS中多余的代码
module.exports = { optimization: { usedExports: true, }, };
- babel/plugin-transform-runtime: babel在编译每个文件时,都会注入一些辅助代码,使得文件体积过大,通过这一插件,在处理babel时,通过引入辅助代码的方式,而不是让每个文件都生成辅助代码,以减少文件体积。
{ test: [/\.js$/], exclude: path.resolve(__dirname, '../node_modules'), loader: require.resolve('babel-loader'), options: { //对指定目录的转换做缓存 cacheDirectory: true, //会使用 Gzip 压缩每个 Babel transform 输出 cacheCompression: false, plugins: [ //引入辅助代码的方式 "@babel/plugin-transform-runtime", ] } }
-
图片压缩
-
代码压缩
optimization: { //... minimizer: [ //js代码压缩 new TerserPlugin({ terserOptions: { compress: { //是否输出警告 warnings: false, // 是否删除代码中所有的console语句,默认为不删除,开启后,会删除所有的console语句 drop_console: true, }, output: { //是否保留注释 comments: false, } } } ), //CSS压缩 new CssMinimizerPlugin(), // 压缩图片 new ImageMinimizerPlugin({ minimizer: { implementation: ImageMinimizerPlugin.imageminGenerate, options: { plugins: [ ["gifsicle", { interlaced: true }], ["jpegtran", { progressive: true }], ["optipng", { optimizationLevel: 5 }], [ "svgo", { plugins: [ "preset-default", "prefixIds", { name: "sortAttrs", params: { xmlnsOrder: "alphabetical", }, }, ], }, ], ], }, }, }), ], }
优化代码运行性能
- code split : 对代码进行分块打包,使单文件体积更小,并行加载速度更快
optimization: { splitChunks: { chunks: 'all',//所有模块进行分割 cacheGroups: {//打包模块组 react: {//组名 //react react-dom test: /[\\/]node_modules[\\/]react(.*)?[\\/]/, name: 'chunk-react', priority: 50, }, antd: { test: /[\\/]node_modules[\\/]antd[\\/]/, name: 'chunk-antd', priority: 30, }, lid: { test: /[\\/]node_modules[\\/]/, name: 'chunk-libs', priority: 1, // reuseExistingChunk: true, }, }, }, }
这里分别针对了react/antd模块进行单独打包,然后对其他模块统一打包
- 按需加载:通过import动态导入进行按需加载
document.getElementById("btn").onclick = function () { import('./count') .then((res) => { console.log("模块加载成功" + res); }) .catch(err => { console.log("模块加载失败" + err); }) }
这样只有在点击btn按钮时才会加载count文件,而不是页面一加载就加载count文件。
再比如vue中的路由懒加载也是通过这一方法实现。 - preload/prefetch
preload:告诉浏览器立即加载资源,只加载当前页面资源
prefetch:空闲时加载资源,也可以加载下一页面使用的资源new PreloadWebpackPlugin({ rel:'preload', as:'script' })
new PreloadWebpackPlugin({ rel:'prefetch', })
他们都是只加载,不执行,当使用相关资源时,会运行加载之前的缓存 - core-js:js兼容处理,能够在低版本浏览器中运行。(处理babel不能处理不了的,包括async函数、promise对象、数组的一些方法等)