下面会以这些方面来说明如何 优化构建速度:
- 优化
babel-loader
:- 使用
include
或exclude
来缩小查找范围 - 使用
cacheDirectory
来设置缓存
- 使用
- 避免引入无用的模块:
webpack.IgnorePlugin
- 避免重复打包:
module.noParse
- 开启多进程打包:
happypack
- 多进程压缩
js
:webpack-parallel-uglify-plugin
- 自动更新和热更新(仅适合开发环境):
watch
(devServer自动开启)、HotModuleReplacementPlugin
优化babel-loader
在遇到浏览器不识别的es6代码时,我们往往借助babel-loader
去解译成ES5
, 当有多个模块引用时,这个解析时间是比较长的,所以可以采用以下方式来减少解析的时间:
- 开启缓存,在第2次编译时,直接使用缓存,不用重新编译,缓存一般只适用于开发环境
- 使用
include
或exclude
适当缩小loader
的适用范围,让其更快找到要解析的文件,开发和生产环境皆适用
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader?cacheDirectory'], // 1. 开启缓存
include: srcPath, // 2. 缩小适用范围(使用include或exclude)
exclude: /node_modules/
}
]
}
IgnorePlugin 避免引入无用的模块
IgnorePlugin
可以用来忽略某些库中我们不需要引入的部分,比如moment
包中的多国语言包,下面以moment
库为例来说明如何使用
- 安装:
npm i momnet -D
- 在
index.js
入口文件中,引入moment
,可以看到它的体积很大,有290K
,压缩后也要72k
(计算包大小,可安装vscode
的import cost
插件查看)
// index.js
import moment from 'moment'
console.log(moment.each);
moment.locale('zh-cn'); // 设置为中文
const date = moment().format('LL'); // 2021年6月29日
console.log(date);
moment
库会默认引入所有语言的代码,所以会导致代码很大,在实际开发中,其实我们一般只需要中文,或者中英两个语言的代码,这时就可以使用IgnorePlugin
来优化,避免引入其他无用语言的代码
-
在不设置
IgnorePlugin
时,index
打包大小295k
设置
IgnorePlugin
// webpack.prod.js或webpack.common.js都可以
const webpack = require('webpack')
const prodConfig = {
plugins: [
// webpack4写法
// new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
// webpack5写法
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/, // 忽略content设置的库中的某个文件夹
contextRegExp: /moment$/, // 要被忽略某部分内容的库
})
]
}
- 因为忽略了
moment
的所有语言包,所以在index.js
中要手动引入想要的语言包(这点比较麻烦,但相较性能来说值得做),比如中文
import moment from 'moment'
// 动态引入中文语言包
import 'moment/locale/zh-cn'
console.log(moment.each);
moment.locale('zh-cn'); // 设置语言为中文
const date = moment().format('LL'); // 2021年6月29日
console.log(date);
- 此时再打包,可以看到
index
打包后的体积变为64k
,小了230k
,所以性能是提升了很多的
noParse
noParse
是用来过滤不需要解析的模块,比如jquery
,lodash
之类的,这些库一般不会再引入其它库,所以不需要webpack
去解析其依赖,也不用打包,只是直接引用即可
noParse
是module
的一个配置:
module: {
noParse: /jquery|lodash/,
}
IgnorePlugin和noParse的区别:
- IgnorePlugin是不引入指定库的部分内容,会减少打包的体积
- noParse是只引入库,但不对其进行
webpack
解析依赖和构建,包的体积不会减少,但打包速度会提升 - 两者都可以在开发和生产环境中使用
happyPack 多进程打包
因为js
是单线程的,如果引用的模块很多,且模块间引用的层级很深,那么webpack
在递归解析依赖时,速度就会很慢。而使用happyPack
可以开启多进程打包,会提高构建速度
它在开发或者生产环境都可以使用,不过对于小项目,使用这个优化空间不大,且开启进程可能消耗性能会更多;在大项目时,才会有较多的优化空间
- 安装:
npm i happypack -D
- 配置:
// wepback.common.js 或webpack.prod.js
const HappyPack = require('happypack')
// 将原来babel的配置改下,改为使用happypack多进程打包
module: {
rules: [
{
test: /\.js$/,
// use: ['babel-loader?cacheDirectory'],
// 改为使用 happypack打包
use: ['happypack/loader?id=babel'], // 这个id是自定义命名的,要跟插件中id对应
include: srcPath
}
]
},
plugins: [
new HappyPack({
id: 'babel', // 唯一标识符
// 使用的loader配置改写到happypack的配置项中
use: ['babel-loader']
})
]
parallelUglifyPlugin 多进程压缩js
适用生产环境
- 安装:
npm i webpack-parallel-uglify-plugin -D
- 配置:
// webpack.prod.js
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
plugins: [
new ParallelUglifyPlugin({
// 压缩js的一些配置
uglifyJS: {
output: {
beautify: false, // 不需要格式化,以最紧凑的方式输出
comments: false // 删除注释
},
warnings: false, // 删除未使用一些代码时引起的警告
compress: {
drop_console: true, // 删除所有console.log
// 是否内嵌虽定义,但只使用了一次的变量
// 比如var x = 2, y = 10, z = x + y 变成 z = 12
collapse_vars: true,
// 提出多次出现但没定义的变量,将其变成静态值;
// 比如x = 'xx', y = 'xx' 变成 var a = 'xx', x = a, y = a
reduce_vars: true
}
}
})
]
多进程使用总结:
- 项目大时,打包较慢时,我们才考虑开启多进程来提高速度
- 项目较小时,本身打包就比较快,没必要开启多进程,因为进程开启、销毁、通信,本身就有一定的性能消耗,在项目小时,有可能使用多进程反而会降低打包速度
自动刷新
在开发环境下,一般配置了devServer
会默认开启自动刷新页面的功能,即watch: true
,不需要我们手动再配置
// webpack.dev.js
// watch: true, //相当于默认开启watch
devServer: {
port: 8080,
// 设置代理
proxy: {
// 将本地 /api2/xxx 代理到 localhost:6666/xxx,通常用这个,/api2仅作为代理转发的标识
'/api2': {
target: 'http://localhost:3000',
changeOrigin: true,
pathRewrite: {
'/api2': ''
}
},
hot: true, // 热重载
open: true
}
},
热更新
- 自动刷新
是指在修改模块内存时,浏览器会自动刷新页面来更新视图内容,是整个页面刷新,速度较慢;刷新页面还会导致临时状态丢失(比如表单内容) - 热更新
是在不刷新页面的情况下,使新代码生效,整个网面不会刷新,状态也不会丢失
热更新配置:
// webpack.dev.js
const webpack = require('webpack')
module.exports = {
target: 'web',
plugins: [
new webpack.HotModuleReplacementPlugin({})
]
}