← dompdf.js Studio

addPage API 完全指南:从单页到多页文档

addPage 是 dompdf.js 中最核心、使用频率最高的 API,它把一段 HTML 或一个真实 DOM 元素渲染成 PDF 页面。很多教程只演示最简单的单页用法,但实际业务里更常见的是多页文档:封面、目录、正文、附件,每一页的版式与尺寸都可能不同。addPage 的设计恰好为此而生——它可以被多次调用,每页独立传参,配合自动分页能力,一条调用链就能拼出完整的多页文档。本文从 addPage 的签名讲起,对比 HTML 字符串与 DOM 元素两种输入的适用场景,说明每页参数如何覆盖构造默认值,并结合自动分页、@page 规则、页眉页脚与页码,给出一套从单页到多页的完整实践路线。读完本文,你将彻底掌握这个核心 API,无论是简单单页还是复杂多页文档都能信手拈来。同时给出逐页校验与统一入口封装等进阶实践,让多页文档的维护更省心。

addPage 的签名与两种输入形式

addPage(htmlOrElement, options) 接收两个参数:第一个是内容,可以是 HTML 字符串,也可以是真实的 DOM 元素;第二个是可选的每页配置对象。调用本身是同步的,真正耗时的渲染与写入发生在 save 阶段,理解这个时序对排查问题很有帮助,很多开发者以为 addPage 就完成了渲染,其实它只是收集内容。

HTML 字符串适合模板化的动态内容:数据拼接、循环生成、服务端下发的模板片段,都可以直接以字符串形式传入,不需要先挂载到页面。字符串形式还方便与前端模板引擎配合,代码更内聚,便于单元测试,模板可以独立维护、独立验证,改动一处即可全局生效。

DOM 元素适合页面中已经存在的真实内容:报表容器、表单区域、可视化图表。直接传元素可以省去序列化步骤,元素当前的样式、图片、布局状态都会被完整捕获。注意元素必须已经完成渲染与资源加载,异步数据未就绪就导出,容易得到缺内容的结果,这一点在动态页面里尤其要小心。

还有一个实用的混合技巧:如果模板里既有固定样式又有动态数据,可以先把静态部分写成常量字符串,再通过模板字符串把动态数据插入,最后整体传给 addPage。这样静态模板可以单独保存、单独维护,数据变化时只需重新拼接,逻辑清晰且不容易出错。

每页参数:按页覆盖构造默认值

addPage 的第二个参数支持按页指定 format、margin 等配置,未指定的字段继续使用构造函数里的全局默认值。这个机制让一份文档可以混合多种页面规格:封面、正文、附件各自独立,互不干扰,一份文档里不同页面的尺寸差异也能轻松实现。

典型用法是在循环里给每页传入不同配置,或在文档不同阶段调用不同参数组合。注意每次 addPage 都会追加一个新页面,页面顺序就是调用顺序,因此拼接文档时务必保证调用次序正确:先封面后正文,先正文后附件,顺序错了整份文档的结构就乱了。

如果整份文档使用统一规格,最简单的方式是只在构造函数配置一次,addPage 只传内容,代码最短也最不容易出错。配置的职责边界可以这样划分:跨页稳定的放构造,逐页变化的放 addPage,两层配合即可覆盖绝大多数需求,代码意图也一目了然。

当页面参数需要动态计算时,可以在调用 addPage 之前先算出配置对象,再解构传入,代码的可读性会更好。比如根据内容长度决定页面尺寸,先用条件分支确定 format,再统一调用 addPage,避免在参数里写复杂的三元表达式,逻辑更直白。

多页文档完整示例

import { DomPDF } from 'dompdf.js';

const pdf = new DomPDF({ format: 'A4', margin: '20mm' });

// 第 1 页:封面(单独设置边距,覆盖构造默认值)
pdf.addPage(coverHTML, { margin: '15mm' });

// 第 2 页:目录(使用构造默认值)
pdf.addPage(tocHTML);

// 第 3-N 页:正文,循环追加,自动分页
const chapters = [/* 章节数据 */];
for (const chapter of chapters) {
  pdf.addPage(renderChapter(chapter));
}

// 最后一页:附件
pdf.addPage(appendixHTML, { format: 'A4' });

// 页面顺序 = addPage 调用顺序:封面、目录、正文、附件
pdf.save('完整报告.pdf');

自动分页与 @page 规则

单次 addPage 的内容超出页面容量时,dompdf.js 会自动分页:按内容流顺序把溢出的部分排到后续页面,表格、图片等块级元素也会在分页边界处妥善处理。这意味着你不需要手动切分长内容,一份几十页的长文也能通过一次 addPage 完成,极大简化了长文档的生成逻辑。

自动分页的边界由内容宽度与剩余高度共同决定,而内容宽度又受页面尺寸、边距影响。为了让分页结果可控,建议在 HTML 里用 @page 规则声明页面背景、边距等全局样式,配合容器宽度设置,让网页排版与 PDF 输出保持一致,避免浏览器宽度与页面宽度不一致导致的分页漂移。

需要精确控制分页位置时,可以在模板里给元素设置分页相关样式:希望整体保持在同一页的块(如表格行、图片配说明)声明为不拆分,需要从新页开始的章节设置强制换页。这类控制在长文档里非常实用,能避免出现孤行、表格被拦腰截断等不专业的分页结果,提升文档的正式感。

自动分页还有一个使用技巧:把需要跨页展示的长表格拆成多个短表格,每个表格块之间用分页样式隔开,可以让每一页的表格都完整、不出现被截断的行。虽然牺牲了一点布局连续性,但换来的是更专业的阅读体验,尤其适合财务报表与多页明细清单。

页眉页脚与页码

多页文档几乎都需要页眉页脚:页眉放文档标题与公司 Logo,页脚放页码与日期。dompdf.js 支持页眉页脚渲染,页码支持当前页与总页数这类动态占位,输出时自动替换为真实数值,无需手工计算,页数变化时页码也会自动更新,不会出现页码错乱。

建议把页眉页脚作为全局配置在构造函数或模板层面统一声明,而不是在每页 addPage 里重复设置。封面页通常不需要页眉页脚,可以通过排除指定页面的机制跳过,保持封面的干净整洁。这类细节最能体现文档的专业度,也是评审时最容易被打分的点。

页眉页脚的高度需要与页边距协调:页眉占用边距区域的一部分,边距过小会导致页眉与正文重叠或视觉挤压。设计模板时先确定页眉页脚高度,再反推上下边距,两个值一起调,得到的效果最稳定也最耐看,打印时也不会出现页眉被裁掉的问题。

页眉页脚的内容建议保持克制:页眉一行标题、页脚一行页码即可,不要堆砌公司全称、地址、电话等冗余信息。信息越多越容易与正文视觉冲突,打印时也更容易出现排版问题,简洁的设计往往更耐看,也更容易跨设备保持一致。

常见问题与性能建议

Q: 图片没加载完就导出,页面缺图?A: 先等待图片加载完成再调用 addPage。对 DOM 元素,可以用图片的 decode 或 Promise.all 等待所有资源就绪;对 HTML 字符串,尽量使用内联 base64 图片,从源头消除时序问题,渲染结果最稳定。

Q: 一次 addPage 传了超长内容,渲染很慢?A: 长文档建议按章节拆分多次 addPage。每次调用内容更少,自动分页的计算压力更小,进度反馈也更细粒度,用户体验明显更好;出问题时定位范围也更小,排查效率更高。

性能上还有两个小技巧:模板中避免过深的嵌套与过多的背景渐变,减少快照阶段的计算量;同一图片多处引用时复用同一 URL,让资源只编码一次,多页文档的文件体积会显著下降,导出速度也随之提升,批量场景收益尤其明显。

最后建议在项目里为 addPage 封装一个统一的入口函数:内部处理模板拼接、资源等待、参数合并,业务代码只调用一个函数。这样所有导出都走同一套逻辑,后续优化(比如增加进度反馈)只需改动这一处,团队协作时也更容易对齐。

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

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

Hello from dompdf.js!

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