Webpack优化
# webpack
优化
# 优化 Webpack 的构建速度
使用高版本的
Webpack
(使用webpack4+
)多线程/多实例构建:
HappyPack
(不维护了)、thread-loader
缩小打包作用域:
exclude/include
(确定loader
规则范围)resolve.modules
指明第三方模块的绝对路径 (减少不必要的查找)resolve.extensions
尽可能减少后缀尝试的可能性noParse
对完全不需要解析的库进行忽略 (不去解析但仍会打包到bundle
中,注意被忽略掉的文件里不应该包含import
、require
、define
等模块化语句)IgnorePlugin
(完全排除模块)合理使用
alias
充分利用缓存提升二次构建速度:
babel-loader
开启缓存terser-webpack-plugin
开启缓存- 使用
cache-loader
或者hard-source-webpack-plugin
注意:thread-loader
和cache-loader
两个要一起使用的话,请先放cache-loader
接着是thread-loader
最后才是heavy-loader
DLL
:- 使用
DllPlugin
进行分包,使用DllReferencePlugin
(索引链接) 对manifest.json
引用,让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间。
- 使用
2)使用webpack4-
优化原因
V8
带来的优化(for of
替代forEach
、Map
和Set
替代Object
、includes
替代indexOf
)- 默认使用更快的
md4 hash算法
webpacks AST
可以直接从loader
传递给AST
,减少解析时间- 使用字符串方法替代正则表达式
①noParse
- 不去解析某个库内部的依赖关系
- 比如
jquery
这个库是独立的, 则不去解析这个库内部依赖的其他的东西 - 在独立库的时候可以使用
module.exports = {
module: {
noParse: /jquery/,
rules:[]
}
}
2
3
4
5
6
②IgnorePlugin
- 忽略掉某些内容 不去解析依赖库内部引用的某些内容
- 从
moment
中引用./local
则忽略掉 - 如果要用
local
的话 则必须在项目中必须手动引入
import 'moment/locale/zh-cn'
module.exports = {
plugins: [
new Webpack.IgnorePlugin(/./local/, /moment/),
]
}
2
3
4
5
6
③dillPlugin
- 不会多次打包, 优化打包时间
- 先把依赖的不变的库打包
- 生成
manifest.json
文件 - 然后在
webpack.config
中引入 webpack.DllPlugin Webpack.DllReferencePlugin
④happypack -> thread-loader
- 大项目的时候开启多线程打包
- 影响前端发布速度的有两个方面,一个是构建,一个就是压缩,把这两个东西优化起来,可以减少很多发布的时间。
⑤thread-loader
thread-loader
会将您的 loader
放置在一个 worker
池里面运行,以达到多线程构建。
把这个 loader
放置在其他 loader
之前(如下图 example
的位置), 放置在这个 loader
之后的 loader
就会在一个单独的 worker
池(worker pool
)中运行。
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /.js$/,
include: path.resolve("src"),
use: [
"thread-loader",
// 你的高开销的loader放置在此 (e.g babel-loader)
]
}
]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
每个 worker
都是一个单独的有 600ms
限制的 node.js
进程。同时跨进程的数据交换也会被限制。请在高开销的loader
中使用,否则效果不佳
⑥压缩加速——开启多线程压缩
- 不推荐使用
webpack-paralle-uglify-plugin
,项目基本处于没人维护的阶段,issue
没人处理,pr
没人合并。Webpack 4.0
以前:uglifyjs-webpack-plugin
,parallel
参数
module.exports = {
optimization: {
minimizer: [
new UglifyJsPlugin({
parallel: true,
}),
],
},};
2
3
4
5
6
7
8
- 推荐使用
terser-webpack-plugin
module.exports = {
optimization: {
minimizer: [new TerserPlugin(
parallel: true // 多线程
)],
},
};
2
3
4
5
6
7
# 优化 Webpack 的打包体积
- 压缩代码
- 提取页面公共资源:
Tree shaking
Scope hoisting
- 图片压缩
- 动态
Polyfill
speed-measure-webpack-plugin
简称 SMP
,分析出 Webpack
打包过程中 Loader
和 Plugin
的耗时,有助于找到构建过程中的性能瓶颈。 开发阶段
开启多核压缩 插件: terser-webpack-plugin
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
terserOptions: {
ecma: 6,
},
}),
]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# Gzip
优化
这里不再赘述Gzip
具体配置
具体讲讲gzip
的压缩原理
gzip
的压缩算法
gzip
中间的文件体,使用的是Deflate
算法,这是一种无损压缩解压算法。Deflate
是zip
压缩文件的默认算法,7z
,xz
等其他的压缩文件中都有用到,实际上deflate
只是一种压缩数据流的算法. 任何需要流式压缩的地方都可以用。
Deflate
算法进行压缩时,一般先用 Lz77
算法压缩,再使用 Huffman
编码。
Lz77算法原理
如果文件中有两块内容相同的话,我们可以用两者之间的距离,相同内容的长度这样一对信息,来替换后一块内容。由于两者之间的距离,相同内容的长度这一对信息的大小,小于被替换内容的大小,所以文件得到了压缩。
举个例子:
http://www.`baidu`.com` `https://www.`taobao`.com
上面一段文本可以看到,前后有部分内容是相同的,我们可以用前文相同内容的距离和相同字符长度替换后文的内容。
http://www.`baidu`.com` `(21,12)`taobao`(23,4)
Deflate
采用的Lz77
算法是经过改进的版本,首先三个字节以上的重复串才进行偏码,否则不进行编码。
其次匹配查找的时候用了哈希表,一个head
数组记录最近匹配的位置和prev
链表来记录哈希值冲突的之前的匹配位置。
而Huffman
编码,只大概了解是通过字符出现概率,将高频字符用较短字节进行表示从而达到字符串的压缩。(http2
头信息压缩就用到了哈夫曼编码)
问题:但是为什么图片不适合用于gzip
压缩
gzip
是一个压缩程序而并不是一个算法,经过gzip
压缩后文件格式为.gz
,我们对.gz
文件进行分析。
使用node
的fs
模块去读取一个gz
压缩包可以看到如下一段Buffer
内容:
const fs = require("fs");
fs.readFile("vds.gz", (err, data) => {
console.log(data); // <Buffer 1f 8b 08 00 00 00 00 00 00 0a ... >
});
2
3
4
5
通常gz
压缩包有文件头,文件体和文件尾三个部分。
头尾专门用来存储一些文件相关信息,比如我们看到上面的Buffer
数据,第一二个字节为1f 8b
(16
进制),通常第一二字节为1f 8b
就可以初步判断这是一个gz
压缩包,但是具体还是要看是否完全符合gz
文件格式,第三个字节取值范围是0
到8
,目前只用8
,表示使用的是Deflate
压缩算法。还有一些比如修改时间,压缩执行的文件系统等信息也会在文件头。
而文件尾会标识出一些原始数据大小的相关信息,被压缩的数据则是放在中间的文件体。
而对于已经压缩过的图片使用gzip
体积变大是因为中间的压缩体并没有怎么压缩,却添加了头尾的压缩相关信息
派生:brotli
压缩
概念
与常见的通用压缩算法不同,Brotli
使用一个预定义的120
千字节字典。该字典包含超过13000
个常用单词、短语和其他子字符串,这些来自一个文本和HTML
文档的大型语料库。预定义的算法可以提升较小文件的压缩密度。使用brotli
取代deflate
来对文本文件压缩通常可以增加20%
的压缩密度,而压缩与解压缩速度则大致不变。
目前该压缩方式大部分浏览器(包括移动端)新版本支持良好,详细的支持情况可在caniuse (opens new window)查询到。
支持Brotli
压缩算法的浏览器使用的内容编码类型为br
,例如以下是Chrome
浏览器请求头里Accept-Encoding
的值:
Accept-Encoding: gzip, deflate, sdch, br
如果服务端支持Brotli
算法,则会返回以下的响应头:
Content-Encoding: br
Tips:brotli
压缩只能在 https
中生效,因为 在 http
请求中 request header
里的 Accept-Encoding: gzip, deflate
是没有 br
的。