WEBKT

Node.js Serverless 瘦身指南:用 esbuild 榨干发布包的每一 KB

4 0 0 0

在 Serverless 架构中,发布包(Deployment Package)的体积直接关系到两个核心指标:部署速度冷启动时间。对于 AWS Lambda、阿里云函数计算等平台,过大的压缩包会导致云端解压耗时大幅增加。

传统的 zip -r node_modules 方案往往会将大量无用的依赖(如测试脚本、文档、甚至是未被使用的子依赖)打包进去,导致体积动辄几十甚至上百 MB。本文将教你如何利用 esbuild 这款高性能构建工具,实现发布包的极致优化。

为什么选择 esbuild?

  1. 极速编译:比 Webpack 快 10-100 倍,极大地缩短 CI/CD 流程。
  2. 原生 Tree Shaking:能有效剔除未被引用的代码。
  3. 单文件输出:将所有依赖打入一个 JS 文件,减少文件系统 IO 寻址。

核心配置策略

要实现最小化打包,我们需要在 esbuild 的配置中重点关注以下几个参数。

1. 基础打包配置

首先,我们需要开启 bundleminify

const esbuild = require('esbuild');

esbuild.build({
  entryPoints: ['src/index.ts'],
  bundle: true,          // 开启打包,将依赖合并
  minify: true,          // 压缩代码,剔除空格、注释、缩短变量名
  platform: 'node',      // 目标平台
  target: 'node18',      // 根据你的运行环境设定
  outfile: 'dist/index.js',
  sourcemap: false,      // 生产环境建议关闭,或使用 hidden 模式
}).catch(() => process.exit(1));

2. 精确设置 External(外部依赖)

这是优化体积最关键的一步。有些库是不需要打进包里的:

  • 运行时已内置的库:如 AWS SDK(在 Lambda 环境中已内置)。
  • 二进制 Native 模块:如 sharpcanvaspg-native。esbuild 无法直接打包这些 C++ 模块,应将其排除并通过 Layer 或重新安装的方式处理。
  external: [
    'aws-sdk',    // AWS Lambda 已内置
    '@aws-sdk/*', // v3 版本的 SDK
    'sqlite3',    // 含有原生二进制,建议排除
    'fsevents'    // 仅开发环境需要
  ],

3. 树摇优化(Tree Shaking)

esbuild 默认对 ESM 模块开启 Tree Shaking。如果你的依赖项使用的是 CommonJS,可以通过以下方式强制优化:

  • 尽量使用支持 ESM 的第三方库。
  • package.json 中配置 sideEffects: false(如果你是库作者)。
  • 即使是 CJS,esbuild 也会尝试进行简单的死代码删除。

进阶优化技巧

处理 CJS 兼容性问题

部分旧的 Node.js 库依赖 __dirname__filename。由于 esbuild 将所有内容打成一个文件,这些变量会失效。
解决方案:使用 define 注入环境变量,或者使用 esbuild 插件修复路径。

  define: {
    'process.env.NODE_ENV': '"production"',
  },
  banner: {
    js: "import { createRequire } from 'module'; const require = createRequire(import.meta.url);",
  },

结合 Serverless Framework 使用

如果你使用 Serverless Framework,可以直接集成 serverless-esbuild 插件,它会自动处理上述逻辑:

# serverless.yml
plugins:
  - serverless-esbuild

custom:
  esbuild:
    bundle: true
    minify: true
    external:
      - 'aws-sdk'
    exclude:
      - '*' # 排除 node_modules 目录进入最终的 zip

避坑指南

  1. 动态 Require:如果代码中存在 require('./langs/' + lang) 这种动态加载,esbuild 无法静态分析。你需要使用 loader 配置或者手动将这些资源文件放入发布包。
  2. Native Modules:如 prismabcrypt 等。这些库必须在目标运行环境(如 Linux)下安装。建议通过 Docker 构建,并将这些依赖放在 external 中,最后在 dist 目录下运行 npm install --production
  3. Top-level Await:虽然 esbuild 支持,但请确保你的 Node.js 运行时版本(14.8+)也支持。

总结

通过 esbuild,一个典型的 Node.js Serverless 项目包体积通常能从 80MB (node_modules) 锐减至 500KB - 2MB (Single File)。这不仅节省了存储成本,更让你的函数响应速度有了质的飞跃。

建议实践: 先开启 bundleminify,观察运行结果。如果报错提示找不到模块,再通过 external 排除特定库。追求极致的你,甚至可以配合压缩工具(如 Brotli)进一步处理静态资源。

架构师老李 NodejsesbuildServerless

评论点评