webpack优化主要分为3个方面:
- 优化开发体验(提升开发效率)
- 优化构建速度
- 优化使用体验
- 优化输出质量
一、优化构建速度
1. 缩小文件搜索范围
webpack在启动后会从配置的Entry出发,解析出文件的导入语句,在递归解析。
在导入时会进行会进行如下两步操作:
- 根据导入语句去寻找对应的需要导入的文件
- 根据要导入文件的后缀,使用相对应的loader去处理文件。
1.1 优化loader配置
善用test、include、exclude三个配置项,尽可能少的让文件被loader处理。
1.2 优化resolve配置
resolve用来设置模块如何被解析。
-
设置
resolve.modules:[path.resolve(__dirname, 'node_modules')]
避免层层查找。resolve.modules告诉webpack去哪些目录下寻找第三方模块,默认值为[‘node_modules’],如果没找到,会依次查找./node_modules、…/node_modules、…/…/node_modules。 -
resolve.mainFields
,设置尽量少的值可以减少入口文件的搜索步骤
mainFields定义使用第三方模块的哪个入口文件,由于大多数第三方模块都使用main字段描述入口文件的位置,所以可以设置单独一个main值,减少搜索 -
resolve.alias
,创建 import 或 require 的别名,使模块引入变得更简单 -
reslove.extensions
减少文件查找
默认值:extensions:[’.js’, ‘.json’],当导入语句没带文件后缀时,Webpack会根据extensions定义的后缀列表进行文件查找,所以:- 列表值尽量少
- 频率高的文件类型的后缀写在前面
- 源码中的导入语句尽可能的写上文件后缀,如require(./data)要写成require(./data.json)
-
module.noParse
字段告诉Webpack不必解析哪些文件,可以用来排除对非模块化库文件的解析
如jQuery、ChartJS,另外如果使用resolve.alias配置了react.min.js,则也应该排除解析,因为react.min.js经过构建,已经是可以直接运行在浏览器的、非模块化的文件了。
二。优化开发体验
2.1 使用自动刷新
- 文件监听
webpack开启文件监听的两种方法:
- 启动webpack时加上–watch参数
- 在配置文件中设置watch:true
module.exports = {
watch: true,
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 300, //文件变动后多久发起构建,越大越好(降低重新构建的频率)
poll: 1000, //每秒询问次数,越小越好(降低检查的频率)
}
}
文件监听的原理:
- 定时获取文件的最后编辑时间,每次都存下最新的最后编辑时间,如果当前获取的和最后一次编辑的时间不一致就认为文件发生了变化,
watchOptions.poll
用于定义检查周期,也就是每秒检查多少次。 - 当发现文件已经发生了变化以后并不会立刻告诉监听者,而是先缓存起来,收集一段时间后,再一次性告诉监听者。
watchOptions.aggregateTimeout
配置等待时间。目的是编辑代码的过程中高频输入文字,导致文件变化一直发生,每次重新构建就会卡死。 - 对于多个文件来说,其原理相似,对列表中的每个文件都会定时检查。在默认情况下,webpack会从entry配置的文件出发,递归解析出entry依赖的文件,将所有文件都加入监听列表中。
- 自动刷新浏览器(webpack-dev-server)
有两种方式:
-
向网页中注入代理客户端代码,通过客户端发起刷新(默认)
-
向网页装入一个iframe,通过刷新iframe实现刷新效果
第一种方式DevServer因为不知道网页依赖哪些Chunk,所以会向每个chunk中都注入客户端代码,当要输出很多chunk时,会导致构建变慢。而一个页面只需要一个客户端,所以关闭inline模式可以减少构建时间,chunk越多提升月明显。关闭方式:1. 启动时使用webpack-dev-server --inline false 2. 配置 devserver:{inline:false}
2.2 开启模块热替换
模块热替换不刷新整个网页而只重新编译发生变化的模块,并用新模块替换老模块,所以预览反应更快,等待时间更少,同时不刷新页面能保留当前网页的运行状态。原理也是向每一个chunk中注入代理客户端来连接DevServer和网页。开启方式:
1. webpack-dev-server --hot
2.使用HotModuleReplacementPlugin,比较麻烦
开启后如果修改子模块就可以实现局部刷新,但如果修改的是根JS文件,会整页刷新,原因在于,子模块更新时,事件一层层向上传递,直到某层的文件接收了当前变化的模块,然后执行回调函数。如果一层层向外抛直到最外层都没有文件接收,就会刷新整页。
使用 NamedModulesPlugin 可以使控制台打印出被替换的模块的名称而非数字ID,另外同webpack监听,忽略node_modules目录的文件可以提升性能。
3.代码压缩
- 压缩js(uglifyjs-webpack-plugin)
js代码压缩,默认会使用 optimization.minimizer,
- cache: Boolean/String ,字符串即是缓存文件存放的路径;
- test:正则表达式、字符串、数组都可以,用于只匹配某些文件,如:/.js(?.*)?$/i;
- parallel : 启用多线程并行运行来提高编译速度
- output.comments : 删除所有注释,
- compress.warnings :插件在进行删除一些无用代码的时候,不提示警告,
- compress.drop_console:喜欢打console的同学,它能自动帮你过滤掉,再也不用担心线上还打印日志了;
…
//默认:
optimization:{
minimizer:true
};
//自定义
minimizer: [
new UglifyJsPlugin({
cache: true,
// cache: "assets",
parallel: true, //也可以指定 Number ,即最多并行运行数量
sourceMap: true,
uglifyOptions: {
output: {
comments: false,
……
},
compress: {
warnings: false,
drop_console:true,
……
}
},
}),
]
- 压缩css(optimize-css-assets-webpack-plugin)
- assetNameRegExp:默认是全部的css都会压缩,该字段可以进行指定某些要处理的文件
- cssProcessor:指定一个优化css的处理器,默认cssnano,
- cssProcessorPluginOptions:cssProcessor后面可以跟一个process方法,会返回一个promise对象,而cssProcessorPluginOptions就是一个options参数选项!
- canPrint:布尔,是否要将编译的消息显示在控制台
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.optimize\.css$/g,
cssProcessor: require('cssnano'),
cssProcessorPluginOptions: {
preset: ['default', { discardComments: { removeAll: true } }],
//autoprefixer: { browsers: CSS_BROWSERS }, 也是可以指定前缀的
},
canPrint: true
})