← dompdf.js Studio

dompdf.js 与 html2pdf.js 对比:告别模糊位图 PDF

html2pdf.js 是 star 数最高的前端 HTML 转 PDF 库之一,很多教程都在推荐它。但它有一个先天缺陷:通过 html2canvas 把网页截成图片再塞进 PDF,输出是位图,文字不可选中、放大模糊、文件臃肿。dompdf.js 用 Rust+WASM 内核直接渲染矢量 PDF,同样的 HTML 输入,输出却截然不同。本文从原理、清晰度、中文、性能、体积等维度客观对比两个库,配同款代码的两种写法与完整对比表,最后给出迁移步骤与注意事项,帮助还在用位图方案的项目看清差距、平稳切换,避免继续在错误的技术路线上积累代码。

html2pdf.js 的原理与局限

html2pdf.js 本质上是两个库的封装:先用 html2canvas 把 DOM 渲染成 Canvas 位图,再用 jsPDF 把这张图片嵌入 PDF 文件。整个流程是 HTML 到图片、图片到 PDF 两步,每一步都有信息损耗,最终质量受制于截图环节。

位图路线的局限是结构性的:文字变成像素,无法选中、无法搜索、无法复制;放大到 200% 以上边缘发虚;打印到纸张上效果也明显差于矢量文字。对合同、报表、学术文档这类需要长期存档检索的场景是硬伤,难以妥协。

此外 html2canvas 对部分 CSS 支持不完整(如复杂的 background、部分 flex 布局、某些滤镜与伪元素技巧),页面稍复杂就可能出现样式偏差,排查起来非常痛苦,往往要逐个属性试验才能定位,耗费大量开发时间。

还有一个常见的坑:html2pdf.js 对 canvas、iframe、跨域图片的兼容问题需要逐个打补丁,社区里流传着大量 workaround 代码,维护成本被不断推高,越用越重,新功能开发也被拖累。

还有一个现实问题:html2pdf.js 的维护节奏较慢,新特性与浏览器兼容修复滞后,遇到新版浏览器或复杂页面时往往要等很久才有修复,团队只能自己打补丁,长期看风险不小。

dompdf.js 的矢量方案

dompdf.js 使用 Rust 编写核心渲染引擎,通过 WASM 在浏览器内运行,直接解析 HTML/CSS 并生成 PDF 指令,全程不经过截图,输出是真正的矢量 PDF,从原理上绕开了位图方案的种种问题,架构上更干净。

矢量意味着文字以字形曲线写入文件:可搜索、可选中、可复制,任意缩放清晰,文件体积小。这对合同、报表、学术文档这类需要长期存档和检索的场景至关重要,文档与图片的差别就在于此,价值无法用性能参数衡量。

同时 dompdf.js 内置思源黑体,中文零配置;支持 @page 分页媒体、自动分页、flex/grid 布局与表格边框,覆盖了 html2pdf.js 用户最常遇到的痛点,迁移动力充足,切换后的体验提升立竿见影。

矢量方案的另一个优势是可访问性:PDF 内的文字可以被屏幕阅读器朗读,符合无障碍要求;位图 PDF 对读屏软件来说是一张空白图片,在法律合规要求严格的行业这是硬性门槛,不容忽视。

对于需要长期存档的行业,矢量 PDF 还意味着文件可以无损缩放打印,不会因为分辨率不足而模糊。这在医疗、法律、金融等对文档质量有硬性要求的行业尤其重要,是选型时的加分项。

代码对比:同样的 HTML 两种输出

// ---- html2pdf.js:HTML -> Canvas 位图 -> PDF ----
import html2pdf from 'html2pdf.js';
const el = document.getElementById('report');
html2pdf()
  .set({ margin: 10, filename: 'report.pdf' })
  .from(el)
  .save();
// 输出为位图:文字不可选中,放大发虚

// ---- dompdf.js:HTML -> 矢量 PDF ----
import { DomPDF } from 'dompdf.js';
const pdf = new DomPDF();
pdf.addPage(document.getElementById('report'), {
  format: 'A4',
  margin: '10mm',
});
pdf.save('report.pdf');
// 输出为矢量:文字可搜索,任意缩放清晰

核心能力对比表

| 对比维度 | html2pdf.js | dompdf.js |
|:---|:---|:---|
| 输出类型 | 位图(截图嵌入) | 矢量 PDF |
| 文字可选中/搜索 | 不可以 | 可以 |
| 200% 放大清晰度 | 模糊 | 清晰 |
| 中文支持 | 依赖页面字体 | 内置思源黑体 |
| 自动分页 | 按高度截断,易裁切 | CSS 分页,精准 |
| 大文档性能 | 慢,逐页截图 | WASM 快,千页两秒 |
| 文件体积 | 大(整页图片) | 小(矢量) |
| 依赖 | jsPDF + html2canvas | 单库 |
| 包体积 | 大 | 小 |

大文档与性能对比

html2pdf.js 的逐页截图在长页面下表现不佳:每页都要重新渲染 Canvas 并编码为图片,页面越长耗时越长,内存占用也高,10 页以上的文档体验明显变差,超长页面甚至可能直接失败,稳定性是硬伤。

dompdf.js 的 WASM 渲染不经过图片编码,实测千页文档约两秒完成,且输出体积与内容量线性相关,不会因为页面尺寸而爆炸式增长,性能表现稳定可预期,适合批量导出等重负载场景。

对移动端和低端设备,位图方案还会带来额外的解码与内存压力;矢量方案渲染路径短、峰值内存低,在手机浏览器上生成 PDF 的体验更稳定,移动办公场景下优势明显,适配成本也更低。

如果你的场景是几十页的合同或报表,位图方案的页数一多,生成时间、内存、体积三线齐涨;矢量方案的增长则平稳得多,适合对性能有要求的正式业务,上线后运维压力也更小。

如果只是偶尔导出简单页面,html2pdf.js 的便利性仍然值得肯定,不必为了切换而切换;当文档复杂度、页数、质量要求上升时,再评估切换到矢量方案,按需升级最稳妥。

从开发角度看,html2pdf.js 的配置项虽然多,但每个配置都对应一个已知缺陷的修补;dompdf.js 需要关注的配置极少,团队上手更快,新人也能快速产出可用功能,招聘与培训成本更低。

如果团队已经在 html2pdf.js 上积累了大量配置,切换前建议把配置逐条对照:哪些是必需的、哪些是为兼容打的补丁,补丁类配置大概率可以直接丢弃,迁移反而会简化代码。

迁移建议与注意事项

如果现有项目在用 html2pdf.js,迁移 dompdf.js 的成本很低:接口同样是传入 HTML/DOM 并导出 PDF,核心改动是把 html2pdf().set(...).from(el).save() 换成 new DomPDF() 加 addPage 加 save,半天就能完成改造,风险可控。

迁移时重点检查三点:依赖 html2canvas 兼容的特殊 CSS 是否在 dompdf.js 中表现一致、自定义页眉页脚的实现方式、以及原页面里图片资源的加载方式(推荐转 base64 内联),提前排查可避免上线后返工。

最后提醒:html2pdf.js 社区大、教程多,dompdf.js 还在成长中,遇到冷门问题可能要靠读源码;但从输出质量与性能看,矢量方案是长期正确的方向,越早切换,积累的位图兼容代码就越少,值得投入。

迁移时建议分两步走:先在新功能模块直接使用 dompdf.js,老模块保持 html2pdf.js 不动,验证稳定后再逐步切换存量页面,风险最小,也方便回退,遇到问题可以随时还原。

如果迁移后发现个别样式差异,先对照浏览器打印预览确认是 dompdf.js 的问题还是原 CSS 本身就不符合打印规范,多数差异其实源自页面样式没有按打印场景设计,调整 CSS 即可解决。

验收标准也要提前定好:清晰度、文件体积、生成耗时、分页正确性各设一条基线,迁移前后对比数据,用数字说话,避免主观感受干扰决策,也让验收过程更顺畅。

还要关注一点:迁移不是非黑即白,可以先在非关键页面试点,跑一段时间对比线上反馈,再决定全面切换的节奏,稳妥推进比一步到位更可靠。

最后从成本角度算一笔账:位图方案的服务器资源、存储空间与用户等待时间都是长期支出,矢量方案一次切换,收益是持续的,投入产出比随时间推移越来越可观。

⚡ 现场演示(点击生成 PDF)

下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:

Hello from dompdf.js!

这是由 dompdf.js 渲染的示例 PDF 内容。