《极客时间-玩转 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"],...}

