构建与资源压缩优化

一、构建优化

Tree Shaking

摇掉未使用的代码,依赖于 ES 模块语法。

1
2
3
4
5
import _ from 'lodash';                 // ❌ 全量导入,无法 tree-shaking
import _isEmpty from 'lodash/isEmpty'; // ✅ 按需导入

// 或在 package.json 中声明
{ "sideEffects": false }

在 webpack 4+ 中默认对 tree-shaking 进行了支持。

Split Chunks(代码分割)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
splitChunks({
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial',
},
dll: {
name: 'chunk-dll',
test: /[\\/]bizcharts|[\\/]\@antv[\\/]data-set/,
priority: 15,
chunks: 'all',
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'all',
},
},
});

webpack 4 内置的分割策略:

  • 新的 chunk 是否来自 node_modules 或被共享
  • 压缩前体积是否大于 30KB
  • 按需加载并发请求数 ≤ 5 个
  • 页面初始加载并发请求数 ≤ 3 个

拆包(CDN 外置)

将体积大且不变的三方库(react、vue 等)通过 CDN 加载,不打包进主 bundle:

1
<script src="https://cdn.example.com/react@18.2.0.min.js"></script>
1
externals: { react: 'React', 'react-dom': 'ReactDOM' }

Prefetch / Preload(魔术注释)

1
2
import(/* webpackPrefetch: true */ './someModule');
import(/* webpackPreload: true */ './someModule');

逻辑后移

将主体内容的接口请求前移,非主体的请求后移,尽快渲染主要内容。

二、文件压缩

Gzip 压缩

gzip 对纯文本内容可压缩到原大小的 40%,但 png、jpg 等图片不推荐使用 gzip。

Nginx 配置:

1
2
3
4
5
6
7
8
9
http {
gzip on;
gzip_buffers 32 4K;
gzip_comp_level 6;
gzip_min_length 100;
gzip_types application/javascript text/css text/xml;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
}

压缩原理

gzip 使用 Deflate 算法,先用 Lz77 算法压缩,再使用 Huffman 编码。

Lz77 原理:如果文件中有两块内容相同,用距离 + 相同长度这一对信息替换后一块内容。由于这对信息的大小小于被替换内容的大小,所以文件得到了压缩。

构建时预压缩

1
2
3
4
5
6
const fs = require('fs');
const zlib = require('zlib');
const rs = fs.createReadStream('static/vds.js');
const gz = zlib.createGzip();
res.setHeader('content-encoding', 'gzip');
rs.pipe(gz).pipe(res);

开启 gzip 后,响应头中会出现 content-encoding: gzip

Brotli 压缩

Brotli 由 Google 在 2015 年推出,使用预定义的 120KB 字典(包含 13000+ 常用单词和子字符串),对文本文件压缩通常比 gzip 增加 20% 的压缩密度。

1
2
请求头:Accept-Encoding: gzip, deflate, sdch, br
响应头:Content-Encoding: br

目前 Brotli 只能在 HTTPS 下生效,主流 CDN 服务商已支持。