window.print()仅触发浏览器打印对话框,需用户手动选择“另存为pdf”,无法自动保存文件;它零依赖、兼容性好,适合低交互场景,但不适用于自动化或服务端导出。

用 window.print() 实现最简 PDF 导出
浏览器原生支持通过 window.print() 触发打印对话框,用户手动选择“另存为 PDF”即可生成 PDF。这不是“后台生成”,但零依赖、兼容性极好(所有现代浏览器都支持),适合内部工具或低交互场景。
关键点在于控制哪些内容被导出、样式是否适配打印:
- 给需要导出的区域加
class="print-only",用@media print { .no-print { display: none; } }隐藏按钮、导航等干扰元素 - 避免使用
position: fixed或transform,它们在打印预览中常错位或截断 - 字体优先用系统安全字体(如
font-family: "Helvetica Neue", Arial, sans-serif;),避免自定义 Web Font 加载失败导致回退成空白 - 图片尽量用
<img>标签内联加载,不要靠 JS 动态插入——window.print()不等待 JS 渲染完成
用 html2canvas + jsPDF 做前端截图式导出
当必须自动触发、不可依赖用户操作,或需导出含 Canvas、SVG 等动态渲染内容时,html2canvas 先将 DOM 截图为 canvas,再用 jsPDF 将其转为 PDF。
这不是“真正”的 PDF 文本导出(文字不可选、搜索、复制),本质是带缩放的图片嵌入,但开发成本低、可控性强:
-
html2canvas对iframe、跨域图片、transform层级深的结构支持差,报错常见于Failed to execute 'toDataURL' on 'HTMLCanvasElement' - 导出长页面需分页:先用
html2canvas截取可视区高度,再滚动+截图拼接,否则单张 canvas 过大会导致内存溢出或白屏 -
jsPDF.addImage()的 DPI 参数影响清晰度,默认 96 DPI 在 A4 上文字发虚,建议设为192并配合scale调整 canvas 尺寸 - 中文需手动注册字体,否则显示方块:用
doc.addFileToVFS("NotoSansSC-Regular.ttf", fontArray)+doc.addFont(...)
服务端生成更可靠:Node.js 用 puppeteer 渲染 PDF
如果网页含复杂交互、权限控制、或需保证输出一致性(比如发票、合同),必须走服务端。推荐 puppeteer:它启动真实 Chromium 实例,完整执行 JS、加载字体、渲染 CSS,生成标准 PDF。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
典型流程是前端发请求 → 后端用 puppeteer.launch() 打开页面 → page.goto(url) 加载 → page.pdf() 输出 Buffer:
- 务必设置
waitUntil: "networkidle0",否则 JS 异步资源(如图表库、接口数据)未就绪就截图 - 导出前调用
page.addStyleTag({ content: "@media print { ... }" })注入打印样式,比前端写死更灵活 - Linux 服务器缺字体时中文仍会变方块,需提前安装
fonts-wqy-zenhei或把字体文件 copy 到/usr/share/fonts/ - 并发高时别复用同一 browser 实例,每个请求应
browser.newPage()+page.close(),否则页面状态互相污染
别忽略 PDF 可访问性与语义问题
导出 PDF 后,很多人只验证“能打开”“看着像”,却忽略实际用途中的硬需求:文字能否复制?屏幕阅读器能否朗读?搜索关键词是否命中?
纯截图方案(html2canvas)天然不满足这些;puppeteer 生成的 PDF 默认保留文本层,但仍有坑:
- 若原始 HTML 缺少语义标签(如用
<div> 模拟按钮但没 <code>role="button"),PDF 里仍是无意义 div,无法被辅助技术识别 - CSS 中
user-select: none或-webkit-user-select: none会阻止 PDF 中文字被选中,导出前需临时移除 - 表格若用
display: grid或float布局,puppeteer 可能无法正确识别表结构,导致导出后行列错乱——老老实实用<table><p>真正交付前,用 Acrobat Pro 的“辅助工具检查器”或 macOS 预览的“查看 → 显示标记”扫一遍,比肉眼确认靠谱得多。</p></table>










