Vue + dompdf.js 实现简历分页导出的完整踩坑记录

背景

我用 Vue 3 开发了一个在线简历编辑器,支持手动插入分页符,并导出为 PDF。PDF 生成使用的是 dompdf.js v2.0.0,一个基于 WASM 的纯前端 DOM 转 PDF 引擎。

功能看起来很简单:用户在界面上插入分页符,导出 PDF 时在对应位置分页。但实际开发中踩了不少坑。

环境

  • Vue 3 + Composition API
  • dompdf.js v2.0.0(WASM 版本)
  • A4 纸张:96dpi 下宽度 794px,高度 1123px

第一个坑:dompdf.js 的分页机制

dompdf.js 的分页依赖两个关键 HTML 属性(注意:是 HTML 属性,不是 CSS 类名):

属性 作用
divisionDisable 添加到不想被分页切断的元素上,跨页时整个块移到下一页
pageBreak 添加到希望在其前面强制分页的元素上

dompdf.js 内部通过 el.hasAttribute('divisionDisable')root.querySelectorAll('[pageBreak], [divisionDisable]') 来检测这些属性。

踩坑点 :我一开始把 divisionDisable 当成了 dompdf 的配置选项,写成了:

javascript 复制代码
// ❌ 错误:这不是配置选项
const options = {
  divisionDisable: '.page-break-inside-avoid, .header-section'
}

实际上 divisionDisable 必须作为 HTML 属性写在 DOM 元素上:

html 复制代码
<!-- ✅ 正确:作为 HTML 属性 -->
<div class="section" divisionDisable>...</div>

第二个坑:强制分页的正确配置

dompdf.js 支持通过配置项指定在哪些元素前强制分页:

javascript 复制代码
const options = {
  pagination: true,  // 必须启用
  pageBreak: {
    before: '[data-page-break="true"]'  // CSS 选择器
  }
}

对应的 HTML:

html 复制代码
<div pageBreak data-page-break="true"></div>

第三个坑:v-show 和 v-if 会破坏分页

初始实现中,分页标记使用了 v-show="isEditing" 来控制可见性:

html 复制代码
<div v-show="isEditing" class="page-break-marker">✂️ 分页符</div>

问题v-show="false" 会给元素添加 display: none,dompdf.js 会直接忽略 display: none 的元素,导致分页失效。

解决方案:改用 CSS 类控制可见性,确保元素始终在 DOM 中:

html 复制代码
<div class="page-break-marker" :class="{ 'editing-visible': isEditing }">
  ✂️ 分页符(此处将在 PDF 中分页)
</div>
css 复制代码
.page-break-marker {
  height: 0;
  overflow: hidden;
}

.page-break-marker.editing-visible {
  height: auto;
  overflow: visible;
}

第四个坑(最坑):CSS 隐藏对 dompdf.js 无效

导出 PDF 时,需要隐藏分页标记的文本。我尝试了:

css 复制代码
.resume-paper.exporting .page-break-marker {
  visibility: hidden !important;
  height: 0 !important;
  overflow: hidden !important;
}

结果:在浏览器中确实隐藏了,但导出的 PDF 中文本依然显示!

原因 :dompdf.js 不完全支持 CSS 的 visibility: hiddenheight: 0 + overflow: hidden 组合。它会忽略这些样式,直接渲染元素内容。

最终解决方案

在调用 dompdf 之前,直接清除分页标记的内部内容,保留外层 div 和属性;生成 PDF 后再恢复:

javascript 复制代码
const exportPDF = async () => {
  let savedContents = []

  try {
    const paperEl = resumeRef.value.querySelector('.resume-paper')
    paperEl.classList.add('exporting')

    // 清除分页标记的内部内容(保留外层div和pageBreak属性供dompdf识别)
    const markers = paperEl.querySelectorAll('.page-break-marker')
    markers.forEach((marker, i) => {
      savedContents[i] = marker.innerHTML
      marker.innerHTML = ''
    })

    // 生成 PDF
    const blob = await dompdf(paperEl, options)
    // ... 下载逻辑

  } finally {
    // 恢复分页标记内容
    const paperEl = resumeRef.value?.querySelector('.resume-paper')
    if (paperEl) {
      const markers = paperEl.querySelectorAll('.page-break-marker')
      markers.forEach((marker, i) => {
        if (savedContents[i] !== undefined) {
          marker.innerHTML = savedContents[i]
        }
      })
      paperEl.classList.remove('exporting')
    }
  }
}

完整配置示例

javascript 复制代码
const options = {
  format: 'a4',
  pagination: true,
  compress: true,
  useCORS: true,
  // 强制分页:在带有 data-page-break="true" 的元素前分页
  pageBreak: {
    before: '[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 }
  }
}

总结

问题 原因 解决方案
分页不生效 divisionDisable 写成了配置项 改为 HTML 属性
v-show 导致分页失效 display: none 让 dompdf 忽略元素 改用 CSS 类控制可见性
强制分页不生效 没有正确配置 pageBreak.before 添加选择器配置
分页文本在 PDF 中显示 dompdf 忽略 CSS 隐藏样式 导出前清除 innerHTML,导出后恢复

核心原则:dompdf.js 对 CSS 的支持有限,不要依赖 CSS 来控制元素的显示/隐藏。需要隐藏元素内容时,直接操作 DOM 是最可靠的方式。

相关推荐
其美杰布-富贵-李1 小时前
第 6 篇:相机与 OrbitControls
前端·javascript·three.js
lichenyang4531 小时前
从图片生成任务到用户隔离:AIGC Creative Studio 后端与 PostgreSQL 建模实践
前端·后端
小黑技术栈2 小时前
关于前端的一些话
前端
无人生还2 小时前
从 Vue3 到 React · 快速上手系列第 11 篇:状态管理
前端·vue.js·react.js
GuWenyue2 小时前
90%前端写React TS都踩坑!一套父子组件+Hooks完整实战,彻底搞懂类型约束
前端·react.js
Hilaku3 小时前
当 AI 一天写完一周的代码,我们还剩什么优势?
前端·javascript·程序员
尘世中一位迷途小书童3 小时前
无人机航线里的云台角度怎么调?Cesium 做了个视锥 + 鹰眼联动
前端·javascript·数据可视化
思码梁田3 小时前
CSS display 属性:从元素类型转换到隐藏元素的实用指南
前端·css·display·inline·block·none·inline-block
Csvn3 小时前
⚡ Vite 依赖预构建(optimizeDeps)的 3 个经典坑:改了源码不生效、新增依赖 404、缓存怎么清都不行
前端