webpack性能优化从两个方向来考虑:优化打包速度和优化打包文件大小。
一, 优化打包速度
1.webpack区分环境配置文件,避免加载多余的插件。
2.对 webpack 的 resolve 参数进行合理配置,减少查找过程。
webpack的resolve常用配置如下。
resolve: {
//extensions表示的是可省略的文件拓展名,参数是一个数组
extensions: ['.js', '.jsx'],
//alias 的意思为 别名,能把原导入路径映射成一个新的导入路径,减少查找过程。将 resolve.alias 设置为 false 将告知 webpack 忽略模块。
alias: {
alias: path.resolve(__dirname, '../src/alias'),
},
//modules的配置是优化配置,告诉webpack去什么目录下查找node_modules文件夹
modules: [
path.resolve(__dirname, 'node_modules'), // 指定当前目录下的 node_modules 优先查找
'node_modules', // 将默认写法放在后面
]
}
3.缩小构建目标,排除 Webpack 不需要解析的模块。即使用 loader 的时候,在尽量少的模块中去使用。我们可以借助 include 和 exclude 这两个参数,规定 loader 只在那些模块应用和在哪些模块不应用。
module: {
rules: [
{
test: /.js|jsx$/,
exclude: /node_modules/, // 排除模块
include: path.resolve(__dirname, '../src'), //要应用的模块
use: ['babel-loader']
},
// ...
]
}
4>利用多线程提升构建速度。多线程常用插件有HappyPack和thread-loader,HappyPack 的作者现在基本上不维护这个插件了,所以我们可以用thread-loader配置多线程。
使用方式如下:
module: {
rules: [{
test: /.jsx?$/,
exclude: /node_modules/,
// include: path.resolve(__dirname, '../src'),
use: [
{
loader: 'thread-loader',
options: {
workers: 3, // 开启几个 worker 进程来处理打包,默认是 os.cpus().length - 1
}
},
'babel-loader'
]
}]
}
5.预先编译资源模块(DllPlugin)。
我们在打包的时候,一般来说第三方模块是不会变化的,所以我们想只要在第一次打包的时候去打包一下第三方模块,并将第三方模块打包到一个特定的文件中,当第二次 webpack 进行打包的时候,就不需要去 node_modules 中去引入第三方模块,而是直接使用我们第一次打包的第三方模块的文件就行。
// build/webpack.dll.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'production', // 环境
entry: {
vendors: ['lodash'], // 将 lodash 打包到 vendors.js 下
},
output: {
filename: '[name].dll.js', // 输出的名字
path: path.resolve(__dirname, '../dll'), // 输出的文件目录
library: '[name]' // 将我们打包出来的文件以全部变量的形式暴露,可以在浏览器变量的名字进行访问
},
plugins: [
// 对生成的库文件进行分析,生成库文件与业务文件的映射关系,将结果放在 mainfest.json 文件中
new webpack.DllPlugin({
name: '[name]', // 和上面的 library 输出的名字要相同
path: path.resolve(__dirname, '../dll/[name].manifest.json'),
})
]
}
6.缓存 Cache 相关
(1)babel-loader开启缓存:
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
}
},
]
(2)在一些性能开销较大的 loader 之前添加 cache-loader,以便将结果缓存到磁盘里
module.exports = {
module: {
rules: [
{
test: /.ext$/,
use: ['cache-loader', ...loaders],
include: path.resolve('src'),
},
],
},
};
7.合理使用 sourceMap
在开发环境,我们的要求是:快,能够定位到源码的出错位置
所以一般来说选择:cheap-module-source-map
线上环境的话,我们是不能让使用者可以看到source-map的,所以使用hidden-source-map,配合前端监控系统
source-map,表示生成map文件
加上inline,表示生成map写在js里
加上cheap,表示生成map写在js里,只映射行,不映射列,并且只映射业务代码,不管引入的第三方模块
加上module,是管第三方模块的映射
加上eval,用eval执行来提示映射关系
开发推荐cheap-module-eval-source-map
线上推荐cheap-module-source-map
注意: webpack4.0+; webpack配置 devtool: "source-map" 生成的map代码没有 sourcesContent,没有sourcesContent的结果是你只能定位要压缩代码的位置,无法定位到源码的位置。正确配置如下:
const buildConfig = {
mode: "production",
output: { path: distPath, filename: "./js/[name].[hash].min.js", publicPath: "./" },
optimization: {
minimize: false// 1. 这个配置必须
},
plugins: [ ].concat(baseConfig.htmlArray),
devtool: "source-map" // 2. 这个配置必须 }
二. 优化文件体积
1. wepack区分环境配置文件 。
建议把webpack.config.js文件拆分为webpack.common.js,webpack.dev.js和webpack.prod.js,这样把类似HMR之类的开发环境用到的包去掉。
2.js压缩
使用terser-webpack-plugin ,也可用 uglifyjs-webpack-plugin,两者的区别是后者对 ES6 的压缩不是很好。
示例:
const TerserPlugin = require('terser-webpack-plugin');
webpack.common.js:
optimization: {
minimizer: [
new TerserPlugin({
parallel: 4, // 开启几个进程来处理压缩,默认是 os.cpus().length - 1
}),
],
},
}
3.css压缩
使用css-minimizer-webpack-plugin
使用方式:const CssMinimizerPlugin=require("css-minimizer-webpack-plugin")
webpack.prod.js:
optimization: {
minimizer: [
new CssMinimizerPlugin()
]
}
4. 拆分代码(code Spliting)减少main.js包的体积
使用splitChunks选项
optimization: {
splitChunks:{//自动实现代码分割
chunks:'all',//异步 ”all" 对同步和异步都有效
minSize: 20000,
minRemainingSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups:{
vendors:{
test:/[\/]node_modules[\/]/,
priority:-10, //优先级 数字越大 ,优先级越高
filename:"vender.js" //对打包的文件名进行重置
},
default:{//所有代码分割快都符合默认值,此时判断priority优先级
minChunks:2,
priority:-20 ,
reuseExistingChunk:true//允许在模块完全匹配时重用现有的块,而不是创建新的块。
}
}
}
}
5.去除不引用的代码(tree-shaking)
注意:tree-shaking 只在mode为production生效
需要手动开启:
webpack.config.js设置:
optimization:{
useExports:true
}
另外:package.json需要设置为false,防止忽略 “import form './index/css” 之类的文件引用
"sideEffects":false,//所有的模块需要监控 .["*.css"] 排除css监控
6. "babel/preset-env"开启按需加载。
babel的polyfill总是比较大,会影响一些性能,而且也会有一些没用的polyfill,useBuiltIns的按需加载:usage,可减少polyfill的体积.
配置中设置useBuiltIns:usage,babel就会自动把所需的polyfill加载进来,不需要手动import polyfill文件。
useBuiltIns参数配置:"usage" | "entry" | false
,默认值是 fals
-
false
:需要在 js 代码第一行主动 import '@babel/polyfill',会将@babel/polyfill 整个包全部导入。(不推荐,能覆盖到所有 API 的转译,但体积最大)
-
entry
:需要在 js 代码第一行主动 import '@babel/polyfill',会将 browserslist 环境不支持的所有垫片都导入。(能够覆盖到‘hello‘.includes(‘h‘)这种句法,足够安全且代码体积不是特别大)
-
usage
:项目里不用主动 import,会自动将代码里已使用到的、且 browserslist 环境不支持的垫片导入。(但是检测不到‘hello‘.includes(‘h‘)这种句法,对这类原型链上的句法问题不会做转译,书写代码需注意)
提取文件:.babelrc
内容:
{
"presets":[
[
"@babel/preset-env",{
//兼容的浏览器版本(向上兼容)
"targets":{
"edge":"17",
"firefox":"60"
// "Android":"6.0
},
//按需加载
"useBuiltIns":"usage",
"corejs":2
}],
//解析react
"@babel/preset-react"
]
}
补充概念:
babel-loader 不是用来 ES6 转译的,它充其量只是一个打通 babel 与 webpack 的一个 桥梁 ,首先两者得建立一个连接。
{} 转为 function () {} ES5函数。
但是 @babel/preset-env 不会转换 新的API,以及一些在全局对象上的方法 都不会进行转码。 比如 Iterator, Generator, Set, Maps, Proxy, Reflect,Symbol,Promise 等全局对象,以及 Object.assign , Array.from 全局对象的方法,都不会进行转码。
polifill作用:
识别es6新增的对象,如promise等
与@babel/polifill对比,@babel/plugin-transform-runtime 不会造成全局污染
但是 @babel/plugin-transform-runtime不会对Array.prototype.include() 进行polifill (原型链上的方法)
其他:配置webpack时需要注意的点
1>提取单独的css文件:css 代码引用默认打包在js里,不会单独抽离,需要mini-css-extract-plugin插件协助处理
用法:const MinCssExtractPlugin=require("mini-css-extract-plugin")
将"style-loader ”替换为MinCssExtractPlugin.loader ,并引入插件
plugins:[
new MinCssExtractPlugin({
filename:'[name].[contenthash].css',
chunkFilename: '[id].css'
})]
2>将file-loader 替换为url-loader,可以对一定范围内的图片进行base64转换,减少http请求
设置:
rules:[
{
test:/.(png|jepg|jpg|gif)$/,
use:{
//url-loader 可以限定模块的体积,根据体积判断是否需要转换base64,减少http请求
loader:'url-loader',//把模块放到另一个目录里
options:{
limit:10000, /*limit: 10000,限制 图片大小 10000b(10k),小于限制会将图片转换为 base64格式*/
//name 是打包前的文件名称。hash是hash值,防止缓存。ext是打包前的文件格式
name:"[name].[hash].[ext]",
outputPath:'imgs/'
}}
}]
3>使用最新版本的webpack,打包速度会更快。