前端打印,听起来就是个不起眼的小功能,真做起来能让你怀疑人生。表格多一列换行错位、分页把行拦腰截断、页眉页脚固不了位——这些问题我在不同项目里都踩过一遍。更难受的是,每次遇到打印需求,都要临时拼一套
window.print
加 CSS 的方案,换个项目又要推倒重来。最近我在一个管理端项目里换了思路,直接用一个开箱即用的 npm 库
web-print-pdf
,代码量比原来少了三分之二,平时最头疼的表格分页和表头重复也顺手解决了。这篇文章就把我完整的接入过程、踩过的坑和最终的方案整理出来,给还在被前端打印折磨的同行一个可以直接抄作业的参考。
我不打算写那种官方 README 式的功能介绍,而是把“为什么前端打印难”、“这个库到底解决什么问题”、“安装过程中最容易卡住的地方”、“真正写业务代码时要注意什么”这些实际决策过程都过一遍。如果你正打算在项目里接入打印功能,或者已经试过其他方案觉得不顺手,这篇应该能省下你不少时间。
1. 为什么一碰到打印需求,前端就变得很麻烦
1.1 原生打印的三座大山:样式、分页和页眉页脚
先说清楚一个事实:浏览器的打印功能本身没你想的那么简单。很多人第一次接触打印需求时会天真地觉得,
window.print()
一调用,把当前页面打出来不就行了?但实际业务里,“当前页面”几乎永远不适合直接打印。
第一座大山是样式污染。页面上有侧边栏、顶部导航、弹窗遮罩、按钮组,这些东西到了纸上全是垃圾信息。用
@media print
去隐藏它们当然可行,但一个复杂页面的打印样式能写出一百多行 CSS,而且每个页面的 DOM 结构不同,隐藏规则没法通用。改一次页面结构,打印样式大概率跟着崩。
第二座大山是分页完全不受控。浏览器默认是“内容走到哪,就在哪切页”,它根本不关心你的表格行是不是被从中间劈开。我遇到过最典型的场景:一张十几行的业务表格,每一页底部都有一行被切成上下两半,剩下的半行跑到下一页顶部,读起来非常难受。这种问题用 CSS 可以缓解,但前提是你得知道
break-inside: avoid
这些属性,并且能预判哪些元素会触发分页问题。
第三座大山是页眉页脚和页面尺寸。屏幕分辨率是 1920 乘 1080 像素,A4 纸是 210mm 乘 297mm,单位体系都不一致。直接打印网页,页边距往往失控,而且每个浏览器还会自作主张往页面上加默认的页眉页脚——什么网址、页码、日期,丑得没法看,用户还得手动去调打印对话框里的设置。
1.2 绕开原生打印,选什么方案?我看到的几个老思路
既然原生打印问题多,业界自然出现了一批“曲线救国”的方案,我也试过其中两三个,说说真实体验。
第一种是用
html2canvas
加
jsPDF
把页面截图转成 PDF。思路是先把 DOM 画成 canvas,再把 canvas 导出成图片塞进 PDF。听起来灵巧,用起来问题一大堆。首先是清晰度,默认导出的 PDF 里文字是位图,缩放一放大就发虚,打印出来质量很难看。其次是体积,一个三四页的长报表,转完的 PDF 经常十几甚至几十 MB,发给客户人家邮箱都嫌大。更关键的是文字不可选中、不可搜索、没有语义,如果你打印的是合同或单据,后续要做文字提取,这路子直接堵死。
第二种是手动开一个隐藏 iframe,把打印的内容拼成 HTML 塞进去,再调用 iframe 里的
window.print()
。这是很多团队实际在用的方案,优点是能保留文本、保留矢量清晰度,但缺点同样明显:每次都要写模板、处理样式注入,还要管理 iframe 的创建销毁时机。遇到字体加载、图片懒加载、异步数据渲染时,节奏稍没控制好,打印出来的就是空白页或者半截内容。这个方案不是不能用,而是代码复用性太差,每个项目都得重复造一遍轮子。
第三种是干脆不打印,直接把表格导出成 Excel 或让用户自己截图。这是需求方最容易提的“替代方案”,但业务里很多场景就是要纸质件归档、要签名、要盖章,电子表格替代不了。而且截图的分辨率、版式完全不可控,体验更差。
1.3 什么时候值得专门引入一个库?
我的判断标准很简单:如果项目里打印次数多、打印内容跨页面、或者对版式有要求,就不该继续靠浏览器原生能力硬扛,而是应该用一个把打印流程封装好的库。
这也是我决定试试
web-print-pdf
的出发点。我需要的是一个能传内容和配置、直接产出 PDF 的接口,不用操心 iframe 怎么开、样式怎么隔离、分页怎么控制这些重复劳动。它正好把这些都包掉了,再加上它本身不依赖 canvas 截图,能保住文字清晰度,可以说是踩在了前面几个方案中间的一条路。
2. web-print-pdf 把“打印”简化成三步:选内容、定页面、交 PDF
2.1 核心定位:直接渲染 DOM,而不是截图
先放下代码,聊聊这个库的设计思路。它和
html2canvas + jsPDF
最大的区别是:不会把页面转成图片,而是在一个受控的隐藏渲染环境里,把目标 DOM 按打印规则重排,最终产出矢量 PDF。这意味着 PDF 里的文字仍然是文字,可以选中、可以搜索、打印出来不会发虚。
这类库通常还解决了几个实际问题:样式隔离、分页控制、页眉页脚统一。我在接入前的理解是,它相当于给你提供了一台“打印专用浏览器”,你只需要告诉它要打什么、用什么纸张规格、边距留多少,剩下的它自己搞定。
2.2 配置项概览
在实际使用中,我面向
printPDF
传入的配置项大致有这些:
const res = await printPDF({
content: '#printArea', // 要打印的 DOM 选择器或节点
html: '<div>...</div>', // 也可以直接传 HTML 字符串
css: ['public/print.css'], // 额外打印样式
pageSize: 'A4', // A3/A4/A5/Letter/Legal
orientation: 'portrait', // portrait: 纵向 / landscape: 横向
margins: { top: 20, right: 15, bottom: 20, left: 15 }, // 单位 mm
pageBreak: {
avoid: ['tr', 'td', '.card'], // 避免这些元素被分页截断
after: ['table', 'section'], // 在这些元素之后强制分页
},
header: {
content: '<span>巡检报告</span><span class="pageNo"></span>',
},
footer: {
content: '<span>第 <span class="pageNo"></span> 页 / 共 <span class="totalPages"></span> 页</span>',
},
filename: '月度巡检报告.pdf',
open: true, // 打印前是否先展示预览
});
从这个配置就能感受到它省了多少事。
content
指定内容范围,
pageSize
定纸张,
margins
定边距,
header/footer
直接管住页眉页脚和页码。以前这些全靠手写 CSS 模拟,现在全变成参数了。
2.3 和传统组合方案对比:数据不会骗人
我在接入前专门做过一次对比,结论很直观:
| 对比维度 | html2canvas + jsPDF | 手写隐藏 iframe + CSS | web-print-pdf |
|---|---|---|---|
| 文字清晰度 | 低,位图会发虚 | 高,矢量文字 | 高,矢量文字 |
| 分页控制 | 困难,要手动切图 | 中等,全靠 CSS | 内置配置项,可控性高 |
| 重复表头 | 不支持 | 依赖 CSS 写法 | 内置支持 |
| 代码维护量 | 中,每页都要适配 | 高,每个项目都重写 | 低,API 固定 |
| PDF 文件体积 | 偏大 | 正常 | 正常 |
| 是否可选中文字 | 否 | 是 | 是 |
| 学习成本 | 中等 | 高 | 低 |
表格放下之后,选择就很清楚了。对于已经有基础 DOM 结构的项目,把打印逻辑收敛到一个库的 API 后面,比继续维护自己那套 iframe 打印工具函数靠谱得多。
3. 安装阶段最大的坑不在库里:npm 镜像与脚本权限一并说清
这一章标题可能让你觉得奇怪,装个 npm 包有什么好讲的?但我在新机器和同事电脑上装这个库时,确实遇到了好几个跟 npm 环境本身相关的报错,每个都能卡住十几分钟。如果你也碰到下面这些问题,直接照着重来一遍就行。
3.1 一条安装命令,先解决网络问题
安装本身没什么特别的,项目根目录执行:
npm install web-print-pdf
如果网络条件不理想,你会看到下载卡住、超时,或者一串重试日志。这种时候优先做的不是反复重试,而是确认 npm 源地址。国内开发环境比较省事的做法是换成 npmmirror 镜像:
npm config set registry https://registry.npmmirror.com/
设置完检查一下:
npm config get registry
能打印出上面的镜像地址就说明切过去了。这里多说一句:改 registry 是对当前用户的 npm 全局配置,不会影响项目代码,也不会造成什么副作用。如果你只想在本次安装时临时换源,也可以不加
--save
参数之前临时指定:
npm install web-print-pdf --registry=https://registry.npmmirror.com/
这个方式适合不想永久改配置的场景。
3.2 npm 命令都识别不了:八成是环境变量的问题
如果你在终端输入
npm install web-print-pdf
,系统回了一句“npm: 无法将‘npm’项识别为 cmdlet、函数、脚本文件或可运行程序的名称”,大概率不是 web-print-pdf 的问题,而是 Node.js 没装好或者 PATH 没配好。
解决办法分两步。第一步重新执行 Node.js 安装包,安装界面里务必勾选“自动添加到系统 PATH”,没有这个选项的版本就在安装完成后手动配置。第二步手动验证,在终端执行:
node -v
npm -v
两个都能正常输出版本号,就说明环境没问题了。万一还是不行,打开系统环境变量设置,在 Path 里把 Node.js 的实际安装目录加进去,比如
C:\Program Files\nodejs\
,然后重新开一个终端窗口。
3.3 Windows 上最常见的脚本执行策略报错
装完 Node.js,别激动,Windows 用户大概率会碰到的第二个坑是这个:
npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本
这个报错是关于 PowerShell 执行策略的,它默认拒绝运行任何没有签名的脚本,而 npm 的命令本质就是一个
.ps1
脚本。解决方法是在 PowerShell 里执行:
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
然后按提示输入
Y
确认,再重新执行 npm 命令。这个操作只对当前用户生效,不会改动系统全局的安全策略,风险很低。如果你不想动执行策略,也完全可以换到 CMD 或者 Git Bash 里去执行 npm 命令,那里面不存在这个限制。
3.4 安装时滚过的 deprecated 警告要不要管?
我第一次安装时,终端滚动了一条很显眼的警告:
npm WARN deprecated node-domexception@1.0.0: use your platform's native dome...
当时心里咯噔一下,以为是库本身有问题。后来查了才发现,这多半是某个传递依赖带的警告,不是 web-print-pdf 自己的错误,功能也不受影响。npm 的
deprecated
警告只是上游包作者在告诉你“这个包以后别用了”,但还有老依赖在引用它,所以安装时会被牵连出来。只要最终安装完成、没有
ERR!
级别的报错,基本不用管它。
如果你很在意,可以在项目里执行:
npm install
npm audit
看看有没有可升级的依赖,但不建议为了消除一条 deprecated 警告去强行升级依赖树,那样反而容易引入不兼容。这里我的经验是:安装后的第一件事不是看警告,而是打开文档跑一个最简单的示例,用结果说话。
4. 第一个可运行的打印示例:从一段 DOM 到一份 A4 PDF
4.1 最小项目如何搭
为了方便测试,我通常会在临时目录里用 Vite 搭一个最小页面,把业务无关的东西全去掉。你本地如果已经有一个项目,直接在现有项目里操作也行,但新手还是建议从干净项目开始,避免被无关代码干扰判断。
npm create vite@latest print-demo -- --template vanilla
cd print-demo
npm install web-print-pdf
4.2 写一段能直接跑的打印代码
在
index.html
里准备一个简单的报表区域和一个打印按钮:
<div id="app">
<button id="printBtn">打印报表</button>
<div id="printArea">
<h1>设备巡检记录</h1>
<table>
<thead>
<tr><th>设备编号</th><th>巡检日期</th><th>状态</th></tr>
</thead>
<tbody>
<tr><td>EQ-001</td><td>2025-01-12</td><td>正常</td></tr>
<tr><td>EQ-002</td><td>2025-01-12</td><td>需要保养</td></tr>
</tbody>
</table>
</div>
</div>
<script type="module" src="/main.js"></script>
在
main.js
里调用:
import { printPDF } from 'web-print-pdf';
document.querySelector('#printBtn').addEventListener('click', async () => {
await printPDF({
content: '#printArea',
pageSize: 'A4',
orientation: 'portrait',
margins: 16,
filename: '设备巡检记录.pdf',
});
});
这里有个容易忽略的细节:
content
只传了
#printArea
,也就是说只有这块区域会被纳入打印范围,外面的按钮、标题栏会自动排除。这正是我前面说的样式隔离能力,你不用再为打印需求去写一堆隐藏侧边栏的 CSS。
4.3 参数背后的行为逻辑
执行上面的代码后,PDF 默认会直接弹出打印预览窗口。如果你在自动化的批处理场景里不想弹窗,可以加一个
print: false
让函数只生成 PDF 文件对象,想要下载就自己处理保存逻辑。
margins
参数我习惯写成对象形式,能精确控制上下左右边距。在业务单据上,右边距我一般会留得比左边大一点,因为装订线在右侧的打印需求挺常见。
pageSize
不只是 A4 一个选择,需要小票、凭条这种非标准尺寸时,也可以直接传毫米数组,例如
[80, 200]
表示 80mm 乘 200mm 的热敏纸规格。这个对做小票业务的场景非常有用,原生 CSS 里控制起来特别别扭,参数化之后就轻松很多。
4.4 第一个版本的预期效果
跑通这个最小示例之后,你会看到一个结构完整、边距正常、表格边框不丢失的 PDF。这一步验证的东西很关键:DOM 选择、样式注入、分页边界、PDF 生成链路已经全部打通。到了这一步,基础能力就算握在手里了,接下来才是真正解决“专业打印”的难点。
5. 表格分页与表头重复:打印排版的关键细节
5.1 表格打印叫“难”,通常难在两个点
第一个点是行内容被硬生生切断。跨页时,浏览器可能把一行
tr
从中间断开,上一页显示上半行,下一页显示下半行,阅读上非常割裂。第二个点是表头不会重复。一个十页的报表,只有第一页有表头,后面的数据页读者根本不知道这些列是什么意思。
原生情况下这两个问题都得靠 CSS 折腾。先说行内容切断,最常用的做法是给表格行加
break-inside: avoid
:
tr {
break-inside: avoid;
}
但这个属性对旧版浏览器的兼容性不稳定,而且一旦表格高度超过一页,这个规则反而可能导致整行被推到下一页,在上一页留下大片留白。在 web-print-pdf 里,这个问题被抽象成了配置项,你可以针对特定元素设规则:
printPDF({
content: '#printArea',
pageBreak: {
avoid: ['tr', 'td'],
after: ['table'],
},
});
avoid
表示这些元素尽量不要被切开,
after
表示某些元素之后强制换页。配置化相比手写 CSS 的好处是,库内部会对分页位置做重排计算,尽量避免那种“为了避开截断结果留白半页”的尴尬。
5.2 表头重复这件事,内置方案更稳
表头重复在 HTML 里有一个原生技巧:用
thead
包裹表头行,并且在打印样式里加:
thead {
display: table-header-group;
}
大多数现代浏览器都能在跨页时自动重复这个表头。但问题在于这条规则不是所有场景都生效,尤其是当你的表格容器设了
overflow: auto
或者外层高度受限时,浏览器可能根本不把它当作表格上下文。
web-print-pdf 的处理方式是,在打印渲染阶段自动识别
thead
,并在跨页时复制表头内容到每一页。不用额外配置,你只要保证语义化地用
thead
包住表头即可。这算是我用下来最省心的地方之一。
5.3 强制分页:让章节永远从新页开始
除了防止被切行,业务上还经常需要“某一块内容必须独立成页”,比如一个章节的封面、签名区、银行回单。这时候手动控制分页反而比“避免切断”更重要。
我的做法是在打印内容的 DOM 上配合类名来实现。库提供了基于选择器的分页控制,你不需要在几百行业务代码里找元素,而是给要分页的区块加一个统一类名,然后配置:
printPDF({
content: '#printArea',
pageBreak: {
before: ['.section-start'],
},
});
这样每个被标记为
.section-start
的块,都会自动从新的一页开始。从此“改版式”这件事就从改样式变成了改配置,页面结构调整时,打印逻辑不用跟着动。
5.4 长表格的分页性能
还有一个体会值得说:当表格特别长,比如几百上千行时,配置分页规则的粒度要谨慎。不要对每一行都设置复杂的避让规则,那样内部计算量会明显上升,PDF 生成的等待时间可能拉长到用户失去耐心。我的建议是只对表头、合计行、带边框的块级卡片做避让,普通数据行保持默认。
6. 在 Vue 3 和 React 里的接入方式与异步渲染处理
实际项目里很少有人直接写原生 JS,更多是在组件框架里用。这里把 Vue 和 React 两种常见写法都过一遍,重点不是贴代码,而是告诉大家最容易出 bug 的时机问题。
6.1 Vue 3 组件里怎么接入
假设你有一个报表组件,点击“打印”按钮时需要打印组件的 DOM。思路是:先确保 Vue 完成了当前 DOM 更新,再调用打印库。因为你在打印前改过数据、切过 tab、重新渲染过表格,如果 DOM 还没刷出来就打,PDF 里就是老内容。
<script setup>
import { ref, nextTick } from 'vue';
import { printPDF } from 'web-print-pdf';
const reportRef = ref(null);
async function handlePrint() {
await nextTick(); // 等待 Vue 完成 DOM 更新
const res = await printPDF({
content: reportRef.value,
pageSize: 'A4',
filename: '报表.pdf',
});
}
</script>
<template>
<div class="page">
<button @click="handlePrint">打印</button>
<div ref="reportRef" class="report">
<table>
<thead>
<tr><th>月份</th><th>销售额</th></tr>
</thead>
<tbody>
<tr><td>2025-01</td><td>12000</td></tr>
</tbody>
</table>
</div>
</div>
</template>
nextTick
这行是关键。Vue 的数据更新是异步批量渲染的,点击事件触发时,界面上的表格可能是旧数据。如果你刚替换完数据紧接着调
printPDF
,打印出来的可能还是上一轮的内容。接完
nextTick
,确保 DOM 和当前数据同步,问题就消失了。
6.2 React 组件里的接入方式
React 没有
nextTick
,但可以用
useRef
拿 DOM 节点,再在打印前配合
useEffect
或
flushSync
保证最新渲染落定。
import { useRef } from 'react';
import { printPDF } from 'web-print-pdf';
export default function ReportPage() {
const reportRef = useRef(null);
const handlePrint = async () => {
// 确保 React 的渲染已经同步到 DOM
await new Promise((resolve) => setTimeout(resolve, 0));
await printPDF({
content: reportRef.current,
pageSize: 'A4',
header: {
content: '<span class="pageNo"></span>',
},
});
};
return (
<div>
<button onClick={handlePrint}>打印</button>
<div ref={reportRef}>
<table>
<thead>
<tr><th>订单号</th><th>金额</th></tr>
</thead>
<tbody>
<tr><td>ORD-001</td><td>299</td></tr>
</tbody>
</table>
</div>
</div>
);
}
这只是最简单的接法。真实项目里,打印的表格往往依赖异步请求的数据,比如点击按钮后才发起接口请求,拿到数据后再打印。这种情况我的经验是:单独管理一个
loading
状态,在接口返回且渲染完成后,再进入打印流程,不要在请求和打印之间加任何竞态操作。
6.3 图表怎么办:canvas 渲染的内容要单独处理
如果打印区域里放了 ECharts、Chart.js 这类图表组件,情况会复杂一点。因为它们的渲染结果是 canvas 位图,PDF 引擎对 canvas 的支持程度取决于库的执行环境,不一定能自动纳入打印。
我的处理方案是:在打印之前,调用图表实例的
getDataURL()
方法,把图表导出成图片,然后用
img
标签替换或插入到打印区域内,再做打印。这里给一个 ECharts 的例子:
const chart = echarts.getInstanceByDom(document.getElementById('chart'));
const dataUrl = chart.getDataURL({
pixelRatio: 2,
backgroundColor: '#fff',
});
const img = new Image();
img.src = dataUrl;
img.style.width = '100%';
document.getElementById('chart').replaceWith(img);
之后再把
#printArea
传给
printPDF
。注意像素比调成 2,这样导出的图表在 PDF 放大时依然清晰。
6.4 图片懒加载的坑
如果打印区域里有直接使用
img
标签的图片,尤其那些用了 loading 懒加载的,最稳妥的做法是在打印前手动确保它们已经加载完成。一个简单的方案是把所有图片遍历一遍,等
decode()
完成后继续:
const images = Array.from(document.querySelectorAll('#printArea img'));
await Promise.all(images.map((img) => img.decode()));
没有
decode()
方法的旧浏览器,可以退化成
onload
事件写法。总之,宁可等图片彻底加载完再打印,也别让 PDF 里出现一片破碎图标的空白框。
7. 实际上线前的调试与常见问题处置记录
任何库都不可能覆盖所有业务场景,真正上线前必须自己过一遍调试。下面这些问题都是我亲测时遇到或者身边同事遇到过的,记录一下排查链路。
7.1 打印出来的 PDF 一片空白
出现空白,我的排查顺序是固定的:先看传进
content
的 DOM 节点是否存在,再看节点里的内容是否依赖前端 JS 动态渲染,最后检查样式是否正常注入。
第一个场景最常见:你传了一个还没挂载完成的 ref 节点。解决办法就是前面说的,打之前
nextTick
或
setTimeout 0
确保 DOM 存在。第二个场景是内容依赖异步接口,接口返回前 DOM 里根本没有东西可打印。第三个场景,如果确认 DOM 有内容、但打印出来的还是空白,那就要检查 CSS 里是否有把内容移出可视区的样式,比如
position: absolute
后挪到了屏幕外面。库在隐藏渲染时会克隆 DOM,一些极端定位属性可能会丢内容。
7.2 打印样式和屏幕样式不一致
屏幕上看得好好的,打印出来字体不对、间距不对、颜色不对,这种问题大多来自单位换算和字体加载。屏幕 CSS 常用 px,打印场景更合适用 pt、mm 这种物理单位。我的做法是在传给库的样式里覆盖默认字体,比如:
* {
font-family: "Microsoft YaHei", "PingFang SC", sans-serif !important;
}
字体这块尤其要小心:如果服务器部署了自定义字体,但加载是异步的,打印瞬间字体还没有下载完成,PDF 就会用默认字体替代。我的建议是把打印用的关键字体用
@font-face
提前声明,并在打印前确认字体已经加载过。
7.3 跨页表格出现“断头行”
这里的表现是:某行前半截在上一页底部,后半截在下一页顶部,读起来完全不知道这一行讲的是同一行数据。排查第一件事是确认有没有全局
break-inside: avoid
被穿透,比如某个子元素设了
position: relative
导致浏览器无法正确计算避让范围。
我在自己项目里遇到的更隐晦的原因是:表格行内用了 Flex 布局,浏览器的分页避让算法对 flex 容器内的行直接失效。解决办法是给
tr
里的内容改成块级布局,或者用上面提到的
pageBreak.avoid
配置强制库内重排。
7.4 长报表 PDF 生成时间过长
如果你的报表有一两千行,生成 PDF 时界面可能会卡住几秒。这里有几个实用的优化手段:打印前暂时隐藏掉大图、给打印区域设置一个固定宽度减少重排开销、避免对每一行都做复杂的分页避让配置。
如果你发现生成时间还是无法接受,可以考虑把长报表拆成多段分别生成 PDF 再合并,或者先渲染成一个轻量的打印专用 HTML,不套用项目的复杂全局样式。打印专用模板的思路也能让分页更可控,只是需要多维护一个模板文本。
7.5 浏览器兼容性观察
web-print-pdf 这类库在 Chromium 内核浏览器上的表现最稳定,Chrome 和 Edge 基本没有兼容问题。Firefox 对部分 CSS 分页属性的支持和 Chromium 不一致,需要在 Firefox 里额外过一遍。Safari 我对它的打印 PDF 生成能力一直持保留态度,如果用户群体里苹果设备占比高,建议在上线前用真机做一次预览测试,不要只看 Chrome 效果就放出去。
7.6 我自己现在的固定套路
踩过一遍坑之后,我在项目里总结了一套固定的落地模板:先把打印内容收敛到独立的组件或区块里,保证 DOM 结构语义化;打印前统一走一条 async 方法,把待加载图片、图表转换、数据刷新全部放到打印调用之前;给需要分页控制的区块加规范化的类名,用
pageBreak.before
统一控制分章;最后再调
printPDF
。这套流程下来,前端打印在我的项目里再也不是“看起来简单、做起来要命”的功能了。希望这篇踩坑记录也能帮你把打印需求一次做完,少加几天班。
233




被折叠的 条评论
为什么被折叠?



