Skip to content

好的,我们来探讨一项投入产出比极高的性能优化技术:响应压缩 (Response Compression)。这就像是为你的应用配备了一台高效的“真空压缩袋”,在将数据寄给用户前,先把它压缩到最小,让传输过程更快、更省钱。

为你的 API 响应“瘦身”:NestJS 压缩技术完全指南

想象一下你的应用是一位世界著名的摄影师。

他经常需要通过网络将他拍摄的超高清、超大尺寸的原始照片(巨大的 JSON 响应或 HTML 页面)发送给他的客户(浏览器或客户端)。

没有压缩的“原始传输”模式:

  • 一张 50MB 的照片,就需要通过网络实实在在地传输 50MB 的数据。
  • 如果客户的网络状况不佳,下载这张照片可能需要好几分钟。
  • 这不仅消耗了客户大量的时间和流量,也占用了你服务器大量的带宽。

这显然不是一个理想的方案。这位聪明的摄影师很快想到了一个好办法:

引入压缩的“智能传输”模式:

  • 在发送照片前,他先把这张 50MB 的照片用 WinRAR 或 7-Zip(压缩算法,如 Gzip, Brotli) 打包成一个 5MB 的 .zip 压缩文件。
  • 他通过网络发送这个小得多的 5MB 文件。传输过程飞快。
  • 客户收到 .zip 文件后,在自己的电脑上解压缩,就能得到完整、无损的 50MB 原始照片。

在 Web 世界里,这个过程是全自动的!

  1. 浏览器(客户) 在发送请求时,会附带一个 Accept-Encoding 请求头,告诉服务器:“你好,我能读懂 gzip, br 这些格式的压缩文件。”
  2. 服务器(你的 NestJS 应用) 看到这个请求头后,如果它配置了压缩功能,就会在准备好响应内容后,用 gzip 等算法对其进行“打包压缩”。
  3. 服务器将压缩后的、更小的数据发送给浏览器,并附带一个 Content-Encoding: gzip 响应头,告诉浏览器:“我给你的是一个 gzip 压缩文件。”
  4. 浏览器收到响应,看到这个头,就会自动地进行“解压缩”,然后将内容呈现给用户。

整个过程对用户来说是完全透明的,他们唯一能感受到的就是——网站打开速度变快了!

NestJS 本身不处理压缩,但它可以非常轻松地集成 Express(默认平台)生态中一个非常成熟的中间件:compression

1. 第一步:安装“压缩机” (compression)

我们需要先为我们的应用安装这个压缩工具。

第一步:安装依赖

bash
npm install compression
# 同时安装它的类型定义文件,以获得更好的代码提示
npm install -D @types/compression

2. 第二步:启动“压缩机” (全局启用中间件)

这是最简单,也是最神奇的一步。我们只需要在 main.ts 中,将 compression 作为一个全局中间件启用即可。

src/main.ts

typescript
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as compression from 'compression'; // 导入 compression

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // 启用压缩中间件
  app.use(compression());

  await app.listen(3000);
}
bootstrap();

就这一行 app.use(compression()),你的应用现在就拥有了全自动的响应压缩能力!

它会在后台默默地为你完成所有工作:检查请求头、判断是否应该压缩、选择合适的压缩算法、执行压缩、并设置正确的响应头。这是典型的“一次配置,永久受益”的功能。

3. 第三步:见证“瘦身”效果 (如何验证?)

口说无凭,我们怎么知道压缩真的生效了呢?

让我们创建一个返回大量数据的接口来做实验。

src/app.controller.ts

typescript
@Controller()
export class AppController {
  @Get('large-data')
  getLargeData() {
    // 创建一个包含 1000 个对象的大数组
    const largeArray = Array.from({ length: 1000 }, (_, i) => ({
      id: i,
      name: `Item #${i}`,
      description:
        'This is a sample description for the item. Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
      createdAt: new Date(),
    }));
    return largeArray;
  }
}

现在,启动应用,并打开你的浏览器,访问 http://localhost:3000/large-data。然后,按下 F12 打开开发者工具 (Developer Tools),并切换到 Network (网络) 标签页。

你会看到对 large-data 的这次请求。点击它,查看详细信息,重点关注两个地方:

  1. Response Headers (响应头): 你会看到一个关键的响应头:Content-Encoding: gzip。这就是服务器在告诉浏览器:“我发给你的是压缩过的数据”。

  2. Size (大小): 你可能会看到两列关于大小的信息:

    • Size (或 Transferred): 这个值会比较小,比如 15 KB。这表示通过网络实际传输的数据大小。
    • Content (或 Uncompressed): 这个值会大得多,比如 180 KB。这表示数据解压后的原始大小。

这个显著的大小差异,就是压缩带来的实实在在的好处!

4. 高级选项:定制你的“压缩机”

compression() 中间件还可以接受一个配置对象,让你对压缩行为进行微调。

typescript
// main.ts
app.use(
  compression({
    level: 9, // 压缩级别,从 1 (最快) 到 9 (最佳压缩)。默认为 -1 (zlib 默认值)。
    threshold: 1024, // 只有当响应体的大小超过 1KB (1024 字节) 时才进行压缩。
  })
);
  • level: 这是一个在压缩速度和压缩率之间的权衡。级别越高,压缩效果越好,但消耗的 CPU 时间也越多。通常默认值已经足够好。
  • threshold: 这个选项非常有用。对于非常小的响应(比如几十个字节),进行压缩的开销(CPU 计算和添加响应头)可能比节省下来的带宽还要大。设置一个阈值可以避免对这些小响应进行不必要的压缩。

总结

响应压缩是一项投入成本极低、但性能收益极高的优化。

  • 它解决了什么问题? 减小了网络传输的数据量,从而加快了加载速度、节省了带宽。
  • 如何在 NestJS 中实现? 只需安装 compression 包,并在 main.ts 中通过 app.use(compression()) 全局启用即可。
  • 什么时候应该使用? 几乎总是! 只要你的应用会返回基于文本的数据(如 HTML, CSS, JavaScript, JSON, XML),开启压缩几乎总是有益的。对于本身已经高度压缩的二进制数据(如 JPEG 图片、MP4 视频),压缩中间件足够智能,通常不会再次尝试压缩它们。

给你的应用装上这台“压缩机”吧,你的用户会感谢你的网站那“飞一般”的加载速度。