《极客时间-玩转 webpack》进阶篇:webpack 构建速度和体积优化策略

1. 使用高版本的 webpack 和 Node.js

image.png

  • 使用webpack4 : 优化原因

image.png

includes 的性能远好 于indexOf image.png

2. 多进程/多实例构建:资源并行解析可选方案

image.png

HappyPack

原理:每次 webpack 解析一个模块,HappyPack 会将它以及它的依赖分配给 worker 线程中
image.png

webpack4推荐使用thread-loader

thread-loader

https://www.webpackjs.com/loaders/thread-loader/

原理:每次 webpack 解析一个模块,thread-loader 会将它以及它的依赖分配给 worker 线程中

image.png

3. 并行压缩

使用 parallel-uglify-plugin 插件

image.png

uglifyjs-webpack-plugin 开启 parallel 参数

image.png

terser-webpack-plugin 开启 parallel 参数

webpack4 推荐,支持ES6

image.png

4. 分包

  • 设置 Externals
  • 思路:将 react、react-dom 基础包通过 cdn 引入,不打入 bundle 中
  • 方法:使用 html-webpack-externals- plugin

image.png

基础包太多的情况,不合适

5. 进一步分包

思路:将 react、react-dom、redux、react-redux 基础包和业务基础包打包成一个文件

方法:使用 DLLPlugin 进行分包,DllReferencePlugin 对 manifest.json 引用

使用 DLLPlugin 进行分包,需要创建单独的 DLL 配置文件
image.png

使用 DllReferencePlugin 引用 manifest.json

image.png

如果项目上了 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

image.png

8. 减少文件搜索范围

  • 优化 resolve.modules 配置(减少模块搜索层级)
  • 优化 resolve.mainFields 配置
  • 优化 resolve.extensions 配置
  • 合理使用 alias

image.png

9. 图片压缩

要求:基于 Node 库的 imagemin 或者 tinypng API
使用:配置 image-webpack-loader
image.png
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 不⽀持

  1. 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

image.png

11. 动态 Polyfill

image.png
polyfill.io 官方提供的服务
image.png
基于官方自建 polyfill 服务
//huayang.qq.com/polyfill_service/v2/polyfill.min.js?unknown=polyfill&features=Promise,Map,Set

12. SplitChunksPlugin

Webpack4 内置的,替代CommonsChunkPlugin插件

chunks 参数说明:

  • async 异步引⼊的库进⾏分离(默认)
  • initial 同步引⼊的库进⾏分离
  • all 所有引⼊的库进⾏分离(推荐)

    1. module.exports = {
    2. optimization: {
    3. splitChunks: {
    4. chunks: 'async',
    5. minSize: 30000,
    6. maxSize: 0,
    7. minChunks: 1,
    8. maxAsyncRequests: 5,
    9. maxInitialRequests: 3,
    10. automaticNameDelimiter: '~',
    11. name: true,
    12. cacheGroups: {
    13. vendors: {
    14. test: /[\\/]node_modules[\\/]/,
    15. priority: -10
    16. }
    17. }
    18. }
    19. }
    20. };

    利⽤ SplitChunksPlugin 分离基础包
    test: 匹配出需要分离的包

    1. module.exports = {
    2. optimization: {
    3. splitChunks: {
    4. cacheGroups: {
    5. commons: {
    6. test: /(react|react-dom)/,
    7. name: 'vendors',
    8. chunks: 'all'
    9. }
    10. }
    11. }
    12. }
    13. };

    利⽤ SplitChunksPlugin 分离⻚⾯公共⽂件

  • minChunks: 设置最⼩引⽤次数为2次

  • minuSize: 分离的包体积的⼤⼩

    1. module.exports = {
    2. optimization: {
    3. splitChunks: {
    4. minSize: 0,
    5. cacheGroups: {
    6. commons: {
    7. name: 'commons',
    8. chunks: 'all',
    9. minChunks: 2
    10. }
    11. }
    12. }
    13. }
    14. }
    15. };

    13.懒加载

  • CommonJS:require.ensure

  • ES6:动态 import(⽬前还没有原⽣⽀持,需要 babel 转换)

安装 babel 插件

  1. npm install @babel/plugin-syntax-dynamic-import --save-dev
  1. {
  2. "plugins": ["@babel/plugin-syntax-dynamic-import"],
  3. ...
  4. }