第五章:webpack构建速度和体积优化策略
https://github.com/cpselvis/geektime-webpack-course
1. 初级分析,使用 webpack 内置的 stats
stats: 构建统计信息
package.json
2. 速度分析:使用 speed-measure-webpack-plugin
可以看到每个 loader 和 插件的执行耗时。
安装依赖: npm i speed-measure-webpack-plugin -D
webpack.config.js
通过将 webpack 配置,使用 smp 包裹一遍,最终可以得到每个插件,每个 loader 的时间。
3. 体积分析: 使用 webpack-bundle-analyzer 分析体积
安装依赖: npm i webpack-bundle-analyzer -D
设置 webpack.config.js
如某个包很大,想想是否有可能这个包用 CDN 进行加载,而不打入 bundle 中
是否可以利用 js 懒加载,动态 import js
4. 使用高版本的 webpack 和 nodejs(优化构建时间)
webpack 4.0 对比 webpack 3.0 ,大概构建时间能降低 60% - 90%。
使用 webpack 4 能带来优化的原因: 1. V8 带来的优化(for of 替代 forEach, Map 和 Set 代替 Object,includes 代替 indexOf) 2. 默认使用更快的 md4 hash 算法 3. webpack AST 可以直接从 loader 传递给 AST, 减少解析时间 4. 使用字符串方法,替代正则表达式
5. 多进程/多实例构建
可选方案: 1. thread-loader (官方方案) 2. parallel-webpack 3. HappyPack (webpack3 的方案,目前已不再维护,推荐使用 thread-loader)
HappyPack 原理,每次 webpack 解析一个模块,HappyPack 会将它和它的以来分配给 worker 线程中。thread-loader 其实也是如此。
happypack 配置
安装依赖: npm install --save-dev happypack
设置 webpack.config.js
thread-loader 配置
安装依赖: npm install --save-dev thread-loader
设置 webpack.config.js
不知道为啥,我的测试结果没有很大的提升~~
6. 多进程并行压缩代码
使用 parallel-uglify-plugin 插件
使用 uglifyjs-webpack-plugin 开启 parallel 参数(不支持压缩 es6 语法)
terser-webpack-plugin 开启 parallel 参数 (推荐使用)
7. 进一步分包: 预编译资源模块
分包:即设置 extrnals
思路: 将 react, react-dom 基础包通过 cdn 引入,不打入 bundle 中
方法:使用 html-webpack-extrnals-plugin
缺点:会引入很多的 script 标签,如 react 一个,react-dom 一个,react-redux 又是一个。
更好的方式 是使用 DLLPlugin,DllReferencePlugin, 将 react, react-dom, redux, react-redux 基础包和业务基础包打包成一个文件。
新建 webpack.dll.js
设置 webpack.config.js
最后,需要将 [name]_[chunkhash:8].dll.js 引入到 html 模板中。
library.json 长这样:
大致就是,DllPlugin 在打包 dll.js(内部暴露一个 library 的变量,内部包含了 react 和 react-dom 打包后的内容) 的时候,会生成以上的 .json 映射文件。
在 DllReferencePlugin 中引用该映射文件,使得在遇到 react 和 react-dom 的时候,不会打入 bundle 中。
而最终,还是要在页面中引入 .dll.js 文件。
8. 充分利用缓存提升二次构建速度
思路:
1. babel-loader 开启缓存: {cacheDirectory: ture}
参考文档 2. terser-webpack-plugin 开启压缩缓存: {cache: true}
参考文档 3. 使用 cache-loader 或者 hard-source-wekpack-plugin 参考文档
以上开启缓存之后,会在 node_modules 下生成 .cache 目录,用于存放缓存结果。
9. 缩小构建目标
比如设置 babel-loader 不解析(exclude
) node_modules
优化 resolve.modules 配置,减少模块搜索层级
优化 resolve.mainFields 配置
优化 resolve.extensions 配置
合理使用 alias
10. 使用 Tree Sharking 擦除无用的 CSS
无用的 CSS 代码如何删除掉?
Purufy CSS: 遍历代码,识别已经用到的 CSS class
uncss: HTML 需要通过 jsdom 来加载,所有的样式通过 PostCSS 解析,通过 document.querySelector 来识别在 html 文件里面不存在的选择器。 即 document.querySelector 能获取到元素,则说明该样式有被用到。否则是无用的。
做法: purgecss-webpack-plugin 与 mini-css-extract-plugin 一起使用
安装依赖: npm i purgecss-webpack-plugin -D
设置 webpack.config.js
11. 使用 webpack 进行图片压缩
基于 node 库的 imagemin (推荐) 或者 tinypng 的 API。
安装依赖: npm install image-webpack-loader --save-dev
设置 webpack.config.js
12. 使用动态 Polyfill 服务
例如 Promise 语法,Map, Set 语法。问题在于但部分手机浏览器能元素支持 Promise,有一些浏览器不能。
如果统一返回 Promise,对于能支持 Promise 的机器来说,就是多余的。
方案 | 优点 | 缺点 | 是否采用 |
babel-polyfill | React 16 官方推荐 | 1. 包体积 200k, 难以单独抽离 Map 和 Set 2. 项目里 react 是单独引用的 cdn,如果要使用 polyfill, 需要单独构建一份在 react 之前加载 | 否 |
babel-plugin-transform-runtime | 能够只引用某些用到的类或者方法,相对体积较小 | 不能 polyfill 原型上的方法,不适用于业务项目上的复杂环境 | 否 |
自己写 Map, Set,Promise 的polyfill 社区维护的 es6-shim | 定制化高,体积小 | 1. 重复造轮子,日后年久失修成为坑 2. 即使体积小,依然所有用户都要加载 | 否 |
polyfill-service (后端服务,根据请求者的UA 返回相应的 polyfill) | 只给用户返回需要的 polyfill, 社区维护 | 部分国内奇葩浏览器UA可能无法识别,(此时可以降级返回所有的 polyfill) | 是 |
polyfill-service 地址: https://polyfill.io/v3/polyfill.js
体积优化总结: 1. scope hoisting 2. tree-sharking 3. 公共资源分离 4. 图片压缩 5. 动态 polyfill
Last updated