你的CSS就是打印模板:这款前端PDF生成打印工具,让样式控制回归直觉
为什么明明用Tailwind写得工工整整的订单页面,一到打印就字体变大、表格错位、背景全无?为什么每次改打印模板,都要拉着后端同学重新生成PDF,或者让UI设计师再出一套专用样式?——因为大多数Web打印方案,根本不认你写好的CSS。两年前,我也被困在这个怪圈里。直到我发现了一个叫 web-print-pdf 的 npm 包,它的理念简单到让人怀疑:“直接用你前端的HTML和CSS,原样打印,原样
为什么明明用Tailwind写得工工整整的订单页面,一到打印就字体变大、表格错位、背景全无?
为什么每次改打印模板,都要拉着后端同学重新生成PDF,或者让UI设计师再出一套专用样式?
——因为大多数Web打印方案,根本不认你写好的CSS。
两年前,我也被困在这个怪圈里。直到我发现了一个叫 web-print-pdf 的 npm 包,它的理念简单到让人怀疑:“直接用你前端的HTML和CSS,原样打印,原样生成PDF。”
今天这篇文章,不聊晦涩的内核原理,不吹嘘企业级架构,只聊一件事:如何用最舒服的方式,把网页变成高质量的PDF或纸质文档。
01 你需要的不是新模板,而是“尊重CSS”的打印工具
先复盘一下,你现在是怎么处理打印需求的?
方案A:window.print() + @media print
@media print {
.invoice {
width: 100%;
background: white;
font-size: 12pt;
}
}
问题:
· 样式经常“抽风”——明明屏幕上完美,打印出来边距全乱
· 无法生成PDF保存,只能硬打印
· 依然要弹窗,无法静默
方案B:jsPDF + html2canvas
const canvas = await html2canvas(element);
const pdf = new jsPDF();
pdf.addImage(canvas.toDataURL(), 'PNG', 0, 0, 210, 297);
问题:
· 生成的是图片PDF,文字不可选中,放大全是锯齿
· 内存暴涨,长文档容易崩溃
· 完全抛弃了你精心编写的语义化HTML结构
方案C:后端无头浏览器(Puppeteer/Playwright)
const page = await browser.newPage();
await page.setContent(html);
await page.pdf({ path: 'output.pdf' });
问题:
· 需要维护一个后端服务,或者嵌入Node.js环境
· 前端改了样式,还要通知后端同步更新——协作成本陡增
发现共性了吗?
所有方案都逼你为打印单独维护一套逻辑。要么牺牲控制力,要么牺牲开发效率。
02 web-print-pdf:让打印回归“所见即所得”
第一次看到 web-print-pdf 的示例代码,我被它的简单惊到了:
import { printHtml } from 'web-print-pdf';
// 打印当前页面某个区域
await printHtml({
content: document.getElementById('invoice').innerHTML,
// 其他参数都是可选的——打印机、纸张、静默…
});
不需要额外定义打印模板,不需要学习新的DSL,不需要后端参与。
你的Vue组件、React组件、原生HTML——直接传进去,打印出来是什么样,就是你在浏览器里看到的样。
🔥 完全CSS控制,不是“尽力还原”
web-print-pdf 基于 Playwright 内核,它对CSS的支持度与最新版Chrome完全一致。
这意味着:
· ✅ Flex/Grid布局 —— 原封不动
· ✅ 自定义字体 —— 不会回退成系统默认
· ✅ 背景色、渐变色 —— 不会莫名消失
· ✅ 圆角、阴影 —— 保留全部细节
· ✅ 媒体查询 —— @media print 依然有效
你甚至可以直接用Tailwind、UnoCSS这类原子化框架,打印样式和屏幕样式共用一套类名。
<div class="p-4 bg-white shadow rounded border border-gray-200">
<!-- 这套类名,屏幕和打印通用 -->
</div>
无需“打印版”和“屏幕版”两套皮肤,这是前端工程师最舒服的工作流。
03 不止打印:PDF预览与生成,一样CSS优先
很多场景不需要直接打印,而是先生成PDF预览,确认无误后再输出。
web-print-pdf 的 previewHtml 方法,会返回一个真正的PDF文件URL:
import { previewHtml } from 'web-print-pdf';
const pdfUrl = await previewHtml({
content: document.getElementById('report').innerHTML,
options: {
paperSize: 'A4',
margins: { top: 20, right: 20, bottom: 20, left: 20 }
}
});
// 嵌入 iframe 预览,或直接下载
window.open(pdfUrl);
这个PDF不是截图,文字是可选的、可搜索的,矢量元素无限清晰。
而这一切,只需要你提供一段HTML——所有样式控制权,依然在你手里。
04 对比一下:为什么它更“简单易用”?
工具/方案 是否需额外学习 是否维护两套样式 文字可选PDF 前端直接调用 指定打印机
window.print() 否 是 ❌ ✅ ❌
Print.js 轻微 是 ❌ ✅ ❌
jsPDF+html2canvas 中等 是 ❌ ✅ ❌
后端Puppeteer 高 是 ✅ ❌ ❌
web-print-pdf 极低 否 ✅ ✅ ✅
核心差异只有一条:
其他工具要你适应它的规则;
web-print-pdf 是它适应你的规则。
05 真实案例:一个Vue订单打印,从30分钟到30秒
之前帮一家电商公司改造后台打印模块。他们的旧流程:
- 前端写好订单详情 → 2. 传给后端 → 3. 后端用模板引擎再渲染一遍 → 4. 生成PDF → 5. 返回前端 → 6. 前端调起打印预览
6个环节,只要前端样式微调,后端模板就必须同步修改。
用 web-print-pdf 之后:
<template>
<div ref="invoiceContainer">
<!-- 完全复用现有的订单组件样式 -->
<OrderDetails :order="currentOrder" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import { printHtml } from 'web-print-pdf';
import OrderDetails from './OrderDetails.vue';
const invoiceContainer = ref();
const handlePrint = async () => {
await printHtml({
content: invoiceContainer.value.innerHTML,
silent: true, // 静默打印,不弹窗
printer: 'Invoice_Printer'
});
};
</script>
前端独立,后端0改动。
一个上午,整个打印模块重构完成。
06 适用场景:哪些项目最适合?
· ✅ 电商/ERP后台 —— 订单、发货单、发票打印,样式复杂且频繁变更
· ✅ 医疗/政务系统 —— 检验报告、凭证打印,要求排版绝对精准
· ✅ 财务报表系统 —— 长表格、多分页,需生成高保真PDF存档
· ✅ 任何使用现代前端框架(Vue/React)的项目 —— 组件即模板,零成本复用
一句话总结:
只要你会写HTML和CSS,你就已经会用它。
07 开始使用:从npm install到首次打印,只需3分钟
npm install web-print-pdf
打印一段HTML:
import { printHtml } from 'web-print-pdf';
await printHtml({
content: '<h1 style="color:red;">测试打印</h1>',
silent: false // 设为true可静默
});
生成PDF预览:
import { previewHtml } from 'web-print-pdf';
const url = await previewHtml({
content: '<div class="invoice">...</div>',
options: { paperSize: 'A4' }
});
⚠️ 注意:web-print-pdf 需要搭配一个轻量级本地服务使用。
这个服务只需安装一次,所有Web项目共享。
具体安装方式见项目 README,全程图形化,普通运维即可操作。
Web打印不应该成为前端的“畏途”。
你已经在UI和UX上投入了大量心血,为什么到了打印环节,就要被迫妥协、重新发明轮子?
web-print-pdf 不是要教你如何打印,而是把你早已掌握的能力——HTML、CSS、JavaScript——无缝延伸到打印机。
它没有改变前端的工作流,只是移除了“打印需要特殊处理”这个陈旧假设。
npm i web-print-pdf
GitHub / npm 搜索直达
你的CSS,值得被认真对待。
更多推荐


所有评论(0)