配了三年 pako,今天发现浏览器把 gzip 彻底原生化了——这件事把前端压缩的账全变了

还在 import pako 做 gzip 压缩?这件事,今天被浏览器自己彻底收了。

前端的 IndexedDB 存大 JSON,API 响应 gzip 压缩上传,或者给 CDN 预压缩静态资源——这些场景在 2022 年之前,你基本没有选择:要么引入 pako(~45KB 纯 JS zlib 移植),要么自己手写原生实现,产出质量还参差不齐。

2022 年之后,这件事变了。

Chrome 80 / Safari 16.4 / Firefox 113 先后稳定支持了 CompressionStream API——浏览器把原生 zlib 引擎直接暴露给了 JavaScript。三个格式,gzip / deflate / deflate-raw,和你服务端协商的压缩格式完全兼容。

把 pako 换成原生,产出是一样的。

// Before — pako
import pako from "pako";
const compressed = pako.gzip(jsonString); // Uint8Array

// After — CompressionStream,原生
const compressed = await compress(jsonString); // Uint8Array,完全一样

只要把 pako.gzip() 替换成 compress(),产出二进制完全一致。CDN 不需要改配置,API 不需要改接收端,Wire Format 没变。


实战一:IndexedDB 存大 JSON,省 70% 体积

200KB 的 JSON 对象,用 gzip 压缩后通常是 15-30KB。对于移动端 IndexedDB 存储配额受限的场景,这直接决定了你的数据能不能存进去:

async function putCompressed(store, key, data) {
  const compressed = await compress(JSON.stringify(data));
  return store.put(compressed, key);
}

async function getCompressed(store, key) {
  const compressed = await store.get(key);
  if (!compressed) return null;
  return JSON.parse(await decompress(compressed));
}

实战二:Web Worker 异步压缩,主线程零打扰

压缩是 CPU 密集操作,大文件在主线程跑会卡渲染。CompressionStream 支持 Web Worker,把压缩任务打发到后台:

// worker.js
self.onmessage = async ({ data }) => {
  const compressed = await compress(JSON.stringify(data.payload));
  self.postMessage({ compressed }, [compressed.buffer]);
};

主线程完全不阻塞,数据通过 Transferable 对象零拷贝传回。


pako 还能用吗?能。但理由变了。

压缩这件事,pako 是纯 JavaScript 实现,CompressionStream 调用的是浏览器底层 C 代码的 zlib——同一个引擎处理 HTTP 响应解压缩的那个。

Benchmark 数据(pako 官方仓库,Node v24,1MB 输入样本):

  • gzip-pako:~13 ops/sec
  • gzip-node zlib(即 CompressionStream 同款引擎):~30+ ops/sec

大文件(100KB+)场景下,浏览器原生实现比纯 JS 快 3-10 倍,因为没有 JS 执行开销,还能用 SIMD 指令。小文件(<10KB)差别不大,可以忽略。

另外,pako.min.gz ~15KB,打包进来就是白交 15KB 税。CompressionStream 体积是 0。

对比项 CompressionStream(原生) pako(第三方库)
体积 0 依赖,0 打包体积 ~45KB,~15KB gzipped
性能 浏览器原生 C 引擎,3-10x JS 纯 JS,v8 解释执行
格式兼容 gzip/deflate,Wire Format 一致 gzip/deflate,产出相同
Worker 支持 原生支持,零拷贝 Transferable 可用但有通信开销
浏览器支持 Baseline 2022+,全主流引擎 IE 都能跑

下一步:现在就能迁

不需要全量重写。把 pako 调用点列出来,然后:

// 封装一次,到处使用
async function compress(input, format = "gzip") {
  const stream = new Blob([input]).stream()
    .pipeThrough(new CompressionStream(format));
  return new Uint8Array(await new Response(stream).arrayBuffer());
}

async function decompress(input, format = "gzip") {
  const stream = new Blob([input]).stream()
    .pipeThrough(new DecompressionStream(format));
  return new Response(stream).text();
}

新代码直接用。旧代码里判断一下兼容性:if ("CompressionStream" in window),不兼容的才用 pako 做兜底。绝大多数 2022 年后的现代项目,走到第一个分支就够了。

一句话:之前靠 pako 续命的场景,现在浏览器自己会了,而且跑得更快、打包更小。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 16 阅读