前端生成 PDF 的技术路线大致分成两类:一类以 jsPDF、html2canvas 为代表,先把页面画到 Canvas 上,再把位图数据写进 PDF;另一类以 dompdf.js 为代表,直接解析 DOM 与 CSS,由 Rust/WASM 内核输出矢量 PDF。两条路线在原理上就分道扬镳,产物质量也截然不同:Canvas 方案的文字不可选中、不可搜索,放大后边缘模糊,文件体积还大;dompdf.js 输出的每个字符都是真实文本对象,可搜索、可复制,千页文档两秒左右即可生成。本文从渲染原理、文字能力、体积、性能与生态五个维度对比两条路线,给出选型建议与迁移路径,帮助团队在立项阶段做出正确的技术决策。
Canvas 方案的典型链路是 html2canvas 把 DOM 截图成位图,jsPDF 再把位图塞进 PDF 页面。整个过程没有保留真实的文本信息,只有像素;PDF 里每个页面就是一张大图,文字自然不可选、不可搜索。为了控制体积,位图通常要经过 JPEG 压缩,压缩比越高文字边缘越糊,清晰度与文件大小是一对天然的矛盾,调参数只能两头取舍。
dompdf.js 走的是另一条路:它直接读取 DOM 树与计算后的样式,把每个文本节点、每个盒模型都转换成 PDF 的矢量指令。文本以字体轮廓的形式写入,图像按原样嵌入,背景与边框用矢量图形描述。PDF 内部是结构化的对象,文本层与图形层分离,文字和图形都可以被阅读器精确解析,这是位图方案无论如何都做不到的底层能力。
原理差异决定了能力上限:矢量方案可以做文字搜索、复制、无障碍阅读,甚至二次编辑;位图方案只能提供看起来像文档的图片。对合同、报表、发票这类需要存档检索的文档,矢量是底线能力而不是加分项,选型时应该把这一条放在第一位,先确认产物形态满足业务要求,再谈体积与速度的优化。
位图 PDF 的文字本质是像素,用户选中文字时只能框选一块图片区域,复制出来是乱码,全文搜索更是无从谈起。这在档案管理、司法取证、知识库场景是致命缺陷:文档要入库检索,就必须有真实的文本层,位图方案要么接受不可搜索的现实,要么额外叠加 OCR,成本和准确率都是问题,长尾字符的识别错误还会污染索引。
dompdf.js 生成的 PDF 文本层完整保留字符与位置信息,浏览器和 PDF 阅读器都能直接选中、复制、搜索。中文、英文、数字混排同样成立,字体嵌入保证了任意设备上打开字形一致。对需要做全文索引的系统,导出的 PDF 可以直接走文本抽取管线,无需 OCR 介入,索引质量与构建成本都更可控,运维负担明显更轻。
还有容易被忽视的一点:位图方案生成的 PDF 在打印时依赖打印机驱动对图片的处理,颜色与清晰度可能再损失一档;矢量 PDF 打印时由打印机驱动直接渲染轮廓,文字锐利、颜色准确。同一个文件,交付形态不同,位图的劣势会在打印环节被进一步放大,正式交付场景下这种差异用户一眼就能看出来。
上表从渲染原理、文字能力、体积、性能、字体与可维护性六个维度总结两条路线的差异,方便团队在技术评审时直接引用。需要说明的是,对比基于典型实现:Canvas 方案以 html2canvas 加 jsPDF 组合为代表,dompdf.js 以自身 WASM 管线为代表,具体数值与版本有关,但结论方向是稳定的,不会因为小版本变化而反转。
表格里的千页约两秒来自 dompdf.js 官方基准,指典型长文档在主流桌面浏览器上的渲染耗时;位图方案没有可比的整数指标,因为逐页截图的时间与页面复杂度强相关,长文档通常按分钟计。选型时建议用自己业务的真实模板各跑一轮基准,记录耗时、体积与文字可用性三个数据,用数据说话最可靠,比任何宣传口径都有说服力。
| 维度 | Canvas 方案(html2canvas + jsPDF) | dompdf.js(WASM 矢量管线) |
| --- | --- | --- |
| 渲染原理 | DOM 截图成位图,写入 PDF | 解析 DOM/CSS,输出矢量指令 |
| 文字可选/可搜索 | 不可选,不可搜,复制是乱码 | 可选中、可复制、可全文搜索 |
| 放大清晰度 | 放大后边缘模糊(位图像素) | 任意倍率锐利(字体轮廓) |
| 文件体积 | 大(每页一张 JPEG/PNG 图) | 小(文本+矢量指令,天然紧凑) |
| 长文档性能 | 逐页截图,分钟级 | 千页约 2 秒(官方基准) |
| 字体嵌入 | 受位图限制,依赖截图像素 | 完整嵌入 TTF/OTF,字形一致 |
| 依赖与体积 | html2canvas + jsPDF 两套库 | 单库,ESM/CJS 双格式 |
| 适用场景 | 简单预览、纯视觉快照 | 合同/报表/发票等正式文档 |
文件体积的差异来自信息形态:位图把一页内容编码成几万到几十万个像素,JPEG 压缩对文字边缘不友好,一页 A4 的截图动辄几百 KB 到数 MB;矢量 PDF 里文字是轮廓指令,一页纯文本往往只有几十 KB,同样的内容体积可能相差一个数量级,对邮件附件、系统存储、带宽消耗都是实打实的成本,批量导出场景差异更明显。
性能上,位图方案的瓶颈是逐页截图与编码,页面越复杂截图越慢,长文档基本以分钟计,用户体验是长时间的等待,导出期间页面还常常卡死;dompdf.js 的 WASM 管线把排版与编码都放在原生代码里执行,千页文档约两秒完成,且渲染在 Worker 中进行,页面不卡顿,交互体验完全不同,用户等待的耐心阈值被大幅放宽。
还要考虑内存:位图方案需要同时持有 DOM 截图与 PDF 数据,多份大图同时驻留内存,长文档容易触发浏览器内存压力甚至崩溃;矢量方案以文本和图形指令为主,内存占用低得多,批量导出场景下差异尤其明显,服务的稳定性更有保障,运维同学也不必频繁处理内存相关的线上事故。
什么场景仍然适合 Canvas 方案?如果需求只是把页面拍个快照发给别人看,不需要文字搜索、不需要放大查看细节,页面本身以图片和视觉效果为主,位图方案接入简单、效果直观,可以接受。例如简单的分享卡片、视觉预览类工具,位图足够胜任,强行上矢量方案反而增加集成成本,选型不必教条。
什么场景必须用矢量方案?合同、发票、报表、法律文书、技术文档,凡是需要存档、检索、打印、复制的文档,都应该选择 dompdf.js 这类矢量渲染。还有内容量大、导出频繁的系统,矢量方案在体积与速度上的优势会直接转化为存储成本的下降与用户体验的提升,长期收益远超一时的集成工作量。
混合场景也不少见:预览用位图快照,正式导出用矢量 PDF。两个方案可以共存,导出入口按用途分流即可,互不干扰。关键是立项时把文档要可搜索、要打印这类需求问清楚,多数团队踩坑都是因为需求没问到位,而不是技术本身选错,需求澄清比技术选型更值得花时间。
迁移的第一步是盘点模板:把页面里用于导出的 HTML 从业务代码中抽离,确保导出模板与页面展示解耦,模板里不要依赖运行时状态,图片用稳定的 URL 或 data URL,字体明确声明。模板独立之后,dompdf.js 的接入就只是替换导出函数内部的实现,业务调用方几乎不用改动,迁移风险被控制在模板层。
迁移期间可以双轨运行:新旧两套导出并存,同一份数据分别走两条管线,用自动化对比脚本抽查关键页面的文本完整性与版式,确认新方案在文字、分页、字体上全面达标后再切换默认入口,最后下线旧代码。整个过程风险可控,任何一步出问题都可以回退,团队可以放心地在真实流量上验证新方案。
迁移完成后的收益是长期的:文件体积下降一个数量级,导出速度从分钟级进入秒级,文字可搜索,打印质量提升。团队可以把省下来的时间投入到更多导出相关的功能上,比如自定义模板、批量归档、导出记录,整个文档能力的上限被彻底打开,这次技术债的偿还会持续产生复利。
下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:
这是由 dompdf.js 渲染的示例 PDF 内容。