《极客时间-玩转 webpack》进阶篇:webpack 构建速度和体积优化策略
1. 使用高版本的 webpack 和 Node.js
- 使用webpack4 : 优化原因
includes 的性能远好 于indexOf
2. 多进程/多实例构建:资源并行解析可选方案
HappyPack
原理:每次 webpack 解析一个模块,HappyPack 会将它以及它的依赖分配给 worker 线程中
webpack4推荐使用thread-loader
thread-loader
原理:每次 webpack 解析一个模块,thread-loader 会将它以及它的依赖分配给 worker 线程中
3. 并行压缩
使用 parallel-uglify-plugin 插件
uglifyjs-webpack-plugin 开启 parallel 参数
terser-webpack-plugin 开启 parallel 参数
webpack4 推荐,支持ES6
4. 分包
- 设置 Externals
- 思路:将 react、react-dom 基础包通过 cdn 引入,不打入 bundle 中
- 方法:使用 html-webpack-externals- plugin
基础包太多的情况,不合适
5. 进一步分包
思路:将 react、react-dom、redux、react-redux 基础包和业务基础包打包成一个文件
方法:使用 DLLPlugin 进行分包,DllReferencePlugin 对 manifest.json 引用
使用 DLLPlugin 进行分包,需要创建单独的 DLL 配置文件
使用 DllReferencePlugin 引用 manifest.json
如果项目上了 webpack 4,再使用 dll 收益并不大 https://www.cnblogs.com/skychx/p/webpack-dllplugin.html
6. 缓存
目的:提升二次构建速度
缓存思路:
- babel-loader 开启缓存
- terser-webpack-plugin 开启缓存
- 使用 cache-loader 或者 hard-source-webpack-plugin
7. 缩小构建目标
- 目的:尽可能的少构建模块
- 比如 babel-loader 不解析 node_modules
8. 减少文件搜索范围
- 优化 resolve.modules 配置(减少模块搜索层级)
- 优化 resolve.mainFields 配置
- 优化 resolve.extensions 配置
- 合理使用 alias
9. 图片压缩
要求:基于 Node 库的 imagemin 或者 tinypng API
使用:配置 image-webpack-loader
Imagemin的优点分析
- 有很多定制选项
- 可以引入更多第三方优化插件,例如pngquant
- 可以处理多种图片格式
Imagemin的压缩原理
- pngquant: 是一款PNG压缩器,通过将图像转换为具有alpha通道(通常比24/32位PNG 文件小60-80%)的更高效的8位PNG格式,可显著减小文件大小。
- pngcrush:其主要目的是通过尝试不同的压缩级别和PNG过滤方法来降低PNG IDAT数据 流的大小。
- optipng:其设计灵感来自于pngcrush。optipng可将图像文件重新压缩为更小尺寸,而不 会丢失任何信息。
- tinypng:也是将24位png文件转化为更小有索引的8位图片,同时所有非必要的metadata 也会被剥离掉
10. Tree Shaking
概念:1 个模块可能有多个方法,只要其中的某个方法使用到了,则整个文件都会被打到 bundle 里面去,tree shaking 就是只把用到的方法打入 bundle ,没用到的方法会在 uglify 阶段被擦除掉。
- 使用:webpack 默认支持,在 .babelrc 里设置
modules: false
即可- production mode的情况下默认开启
- 要求:必须是 ES6 的语法,CJS 的方式不支持
利⽤ ES6 模块的特点:
- 只能作为模块顶层的语句出现
- import 的模块名只能是字符串常量
- import binding 是 immutable(不可变)的
11.Scope Hoisting
webpack4 production模式默认开启
webpack.optimize.ModuleConcatenationPlugin()
必须是 ES6 语法,CJS 不⽀持
new webpack.optimize.ModuleConcatenationPlugin()
https://www.webpackjs.com/plugins/module-concatenation-plugin/ https://zhuanlan.zhihu.com/p/27980441
10.CSS去除无用代码
无用的 CSS 如何删除掉?
- PurifyCSS: 遍历代码,识别已经用到的 CSS class
- uncss: HTML 需要通过 jsdom 加载,所有的样式通过PostCSS解析,通过 document.querySelector 来识别在 html 文件里面不存在的选择器
在 webpack 中如何使用 PurifyCSS:
使用 purgecss-webpack-plugin 和 mini-css-extract-plugin 配合使用
· https://github.com/FullHuman/purgecss-webpack-plugin
11. 动态 Polyfill
polyfill.io 官方提供的服务
基于官方自建 polyfill 服务
//huayang.qq.com/polyfill_service/v2/polyfill.min.js?unknown=polyfill&features=Promise,Map,Set
12. SplitChunksPlugin
Webpack4 内置的,替代CommonsChunkPlugin插件
chunks 参数说明:
- async 异步引⼊的库进⾏分离(默认)
- initial 同步引⼊的库进⾏分离
all 所有引⼊的库进⾏分离(推荐)
module.exports = {
optimization: {
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
}
}
}
}
};
利⽤ SplitChunksPlugin 分离基础包
test: 匹配出需要分离的包module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /(react|react-dom)/,
name: 'vendors',
chunks: 'all'
}
}
}
}
};
利⽤ SplitChunksPlugin 分离⻚⾯公共⽂件
minChunks: 设置最⼩引⽤次数为2次
minuSize: 分离的包体积的⼤⼩
module.exports = {
optimization: {
splitChunks: {
minSize: 0,
cacheGroups: {
commons: {
name: 'commons',
chunks: 'all',
minChunks: 2
}
}
}
}
}
};
13.懒加载
CommonJS:require.ensure
- ES6:动态 import(⽬前还没有原⽣⽀持,需要 babel 转换)
安装 babel 插件
npm install @babel/plugin-syntax-dynamic-import --save-dev
{
"plugins": ["@babel/plugin-syntax-dynamic-import"],
...
}