在 Node.js 中从 HTML 生成 PDF(以及我为何停止使用 Puppeteer)

发布: (2026年3月18日 GMT+8 14:03)
6 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您希望翻译的正文内容,我将为您翻译成简体中文。

实际上 Puppeteer 有哪些问题

  • Memory – Chromium 是一个完整的浏览器。每个实例消耗 300–500 MB。如果渲染崩溃,浏览器进程可能无法清理,最终在真实流量下耗尽服务器内存。
  • Cold starts – 启动一个 Chromium 实例需要 1–3 秒,每次都是如此。在会缩至零的无服务器函数上,这种延迟会影响第一个请求。
  • Fonts and assets – Puppeteer 在沙箱中运行。任何从相对路径或 file:// URL 加载的内容要么静默失败,要么渲染不正确。本地看起来正常的 PDF 在生产环境中可能出现破损。
  • Server dependencies – Chromium 需要 libgliblibnsslibatk 等库,而这些在普通的 Ubuntu 服务器上并不存在。每个新环境都成了全新的调试会话,Docker 镜像体积也会增加约 400 MB。

这些都不是 Puppeteer 的错;它是一个浏览器自动化工具,却被要求完成它本身并未真正设计去做的事情。

人们尝试的其他选项

wkhtmltopdf

使用 WebKit 渲染 HTML。它快速且轻量,无需管理浏览器进程。
缺点: 自 2020 年起未再维护;CSS 支持停留在 2013 年左右(不支持 flexbox、grid 或 CSS 变量)。现代布局会出现问题。

PDFKit / jsPDF

用代码描述文档——放置文本、绘制线条、设置字体。精确度高,适用于固定布局的文档。
缺点: 不能复用 HTML 模板。每次设计变更都需要修改代码,即使是带动态表格的简单发票也会变得冗长。

API

发送 HTML,返回 PDF。渲染基础设施由第三方处理——无需管理 Chromium、无需系统依赖、无需部署任何东西。大多数团队在尝试完其他方案后都会选择这种方式。

使用 API 实际的样子

const response = await fetch("https://lightningpdf.dev/api/v1/pdf/generate", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    html: `
      
        
        
          

发票 #1042

到期日:2026年3月31日

`,
options: { format: "A4" }

}) });

const { data } = await response.json(); const pdfBuffer = Buffer.from(data.pdf, “base64”);


就是这样。您现有的 HTML 模板可以直接使用,Tailwind 类无需构建步骤即可渲染,迁移自 Puppeteer 主要就是去掉浏览器的设置/拆卸代码。

## 可重复生成的文档模板

如果您需要生成结构固定的发票或报告,可以在可视化设计器中一次性创建模板,并在渲染时传入数据:

```js
body: JSON.stringify({
  template_id: "invoice-001",
  data: {
    company: "Acme Corp",
    invoice_number: "1042",
    items: [
      { name: "Web development", quantity: 10, price: 150 },
      { name: "Design review", quantity: 2, price: 200 }
    ]
  }
})

在应用代码中不需要进行 HTML 字符串拼接——模板独立存在,并在渲染时填充数据。

批量生成

对于批量作业(月末发票、报告运行),请使用异步端点,并在 PDF 准备好时接收 webhook:

const response = await fetch("https://lightningpdf.dev/api/v1/pdf/async", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    template_id: "monthly-statement",
    data: { user_id: "usr_123", month: "February" },
    webhook_url: "https://yourapp.com/webhooks/pdf-ready"
  })
});

粗略性能数据

方法典型渲染时间内存开销
Puppeteer(自托管)2–4 秒每实例 300–500 MB
wkhtmltopdf0.5–1 秒
API(简单文档)< 100 毫秒与您无关
API(复杂 CSS)1–3 秒与您无关

对于简单文档,速度差异显著。Go 原生渲染器可以在 100 毫秒以内生成基本发票,而只有在处理复杂的 HTML/CSS 时才需要使用 Chromium。

对小项目来说值得吗?

可能是的,主要因为部署复杂性。即使你每月只生成 20 份 PDF,避免在每台服务器上安装 Chromium 也是有价值的。大多数 PDF API 都提供免费层,足以覆盖低流量。

对于实际流量或批量生成的情况,优势更为明显——你不再需要担心内存限制或进程管理。

你目前在使用什么工具进行 PDF 生成?如果你已经找到让自托管的 Puppeteer 在生产环境中良好运行的方法,欢迎分享经验。

0 浏览
Back to Blog

相关文章

阅读更多 »

Rob Pike 的5条编程规则

规则 1:你无法判断程序会把时间花在哪里。瓶颈往往出现在意想不到的地方,所以不要事后猜测并加入 speed hack……