一、分页基础配置
要让 dompdf.js 实现分页,必须满足两个前提条件:
1. 启用分页选项
javascript
const options = {
pagination: true, // 关键:必须设为 true
format: 'a4' // 指定纸张大小
}
2. 容器宽度匹配纸张尺寸
A4 纸在 96dpi 下的宽度是 794px。容器宽度必须精确匹配:
css
.resume-paper {
width: 794px;
/* 其他样式 */
}
原理:dompdf.js 根据容器宽度计算内容是否溢出,溢出时自动分页。如果宽度不匹配,分页计算会出错。
二、两种分页控制方式
方式 1:自动分页
当内容高度超过单页高度(A4 纸 1123px)时,dompdf.js 自动在合适位置分页。
适用场景:内容较多,不需要精确控制分页位置。
方式 2:强制分页
在特定元素前强制分页,即使该元素位于页面中间。
实现方法:
- 给目标元素添加
pageBreakHTML 属性 - 在配置中指定选择器
html
<!-- 分页标记元素 -->
<div pageBreak data-page-break="true"></div>
javascript
const options = {
pagination: true,
pageBreak: {
before: '[data-page-break="true"]' // CSS 选择器
}
}
三、防止元素被分页切断
对于某些元素(如标题、段落),不希望它们在分页时被切断成两部分。使用 divisionDisable 属性:
html
<div class="section" divisionDisable>
<h2>工作经历</h2>
<p>详细内容...</p>
</div>
原理 :dompdf.js 通过 el.hasAttribute('divisionDisable') 检测此属性。当元素带有此属性时,如果它即将被分页切断,会将其整体移到下一页。
四、完整配置示例
基于当前项目的实际配置:
javascript
// 1. 加载字体(中文字体支持)
const fontResponse = await fetch('/fonts/SourceHanSansSC-Regular.ttf')
const fontBuffer = await fontResponse.arrayBuffer()
// 2. 配置选项
const options = {
format: 'a4',
pagination: true, // 启用分页
compress: true, // 压缩 PDF
useCORS: true, // 允许跨域图片
// 强制分页配置
pageBreak: {
before: '[data-page-break="true"]' // 在 data-page-break="true" 元素前分页
},
// 中文字体配置
fontConfig: {
fontFamily: 'SourceHanSansSC-Regular',
fontBytes: new Uint8Array(fontBuffer),
fontStyle: 'normal',
fontWeight: 400
},
// 页眉页脚配置
pageConfig: {
header: { content: '', height: 20 }, // 透明页眉占位
footer: { content: '', height: 0 } // 无页脚
}
}
// 3. 调用 dompdf
const blob = await dompdf(paperEl, options)
五、关键 HTML 结构
1. 分页标记元素
html
<div
class="page-break-marker"
pageBreak
data-page-break="true"
>
<!-- 内容在导出时会被清空 -->
</div>
属性说明:
pageBreak:告诉 dompdf.js 这是分页点data-page-break="true":供pageBreak.before选择器匹配
2. 防切断元素
html
<div class="section" divisionDisable>
<!-- 内容不会在中间被切断 -->
</div>
六、导出时的特殊处理
分页标记在编辑界面显示,但在 PDF 中不应显示。需要特殊处理:
javascript
const exportPDF = async () => {
let savedContents = [] // 保存标记内容
try {
// 1. 清除标记内容
const markers = paperEl.querySelectorAll('.page-break-marker')
markers.forEach((marker, i) => {
savedContents[i] = marker.innerHTML
marker.innerHTML = '' // 清空内容,但保留 div 和属性
})
// 2. 生成 PDF
const blob = await dompdf(paperEl, options)
} finally {
// 3. 恢复标记内容
const markers = paperEl.querySelectorAll('.page-break-marker')
markers.forEach((marker, i) => {
if (savedContents[i] !== undefined) {
marker.innerHTML = savedContents[i]
}
})
}
}
为什么不能用 CSS 隐藏?
dompdf.js 对 CSS 的支持有限,特别是:
visibility: hidden可能被忽略height: 0; overflow: hidden可能失效display: none会完全忽略元素
最可靠的方法是直接清空 innerHTML,保留元素结构和属性。
七、常见问题和解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 分页不生效 | pagination 未设为 true |
设置 pagination: true |
| 分页位置不准 | 容器宽度不匹配 | 设置 width: 794px(A4@96dpi) |
| 元素被切断 | 缺少 divisionDisable 属性 |
添加 HTML 属性 divisionDisable |
| 强制分页无效 | 选择器不匹配 | 确保 pageBreak.before 选择器正确 |
| v-show 导致失效 | display: none 让元素被忽略 |
改用 CSS 类控制可见性 |
| 标记文本显示 | CSS 隐藏被忽略 | 导出前清空 innerHTML |
八、调试技巧
- 检查元素属性 :确保
pageBreak、divisionDisable等是 HTML 属性,不是 CSS 类 - 验证选择器 :在浏览器控制台测试
document.querySelectorAll('[data-page-break="true"]') - 容器宽度 :用开发者工具检查
.resume-paper的计算宽度是否为 794px - 分页预览:先在小内容上测试,确保分页逻辑正确
九、完整代码示例
vue
<template>
<div class="resume-paper" ref="resumeRef">
<!-- 头部(防止被切断) -->
<div class="header-section" divisionDisable>
<h1>姓名</h1>
<p>联系方式</p>
</div>
<!-- 第一个分页点 -->
<div
v-if="pageBreaks.includes(1)"
class="page-break-marker"
pageBreak
data-page-break="true"
>
分页标记
</div>
<!-- 工作经历(防止被切断) -->
<div class="section" divisionDisable>
<h2>工作经历</h2>
<p>详细内容...</p>
</div>
</div>
</template>
javascript
// useResume.js
const exportPDF = async () => {
// ... 前面的代码
const options = {
format: 'a4',
pagination: true,
pageBreak: {
before: '[data-page-break="true"]'
},
// ... 其他配置
}
const blob = await dompdf(paperEl, options)
}
十、总结
dompdf.js 的分页功能依赖于:
- 正确的配置 :
pagination: true和匹配的容器宽度 - HTML 属性 :
pageBreak和divisionDisable(不是 CSS 类) - 选择器配置 :
pageBreak.before指定分页点 - 特殊处理:导出时清空标记内容,避免显示问题
记住:dompdf.js 对 CSS 支持有限,直接操作 DOM 是最可靠的方式。