前端打印功能最简实现,不用插件
打印功能看似简单,实际上坑不少:打印样式和页面样式冲突、只打印部分内容不好控制、分页断裂、页眉页尾关不掉。很多人第一反应是装 vue-print-nb 之类的插件,但其实浏览器原生 window.print() 就能搞定90%的场景。今天不用任何插件,把前端打印一次讲透。
一、最基础的打印
// 一行代码,打印当前页面window.print()
提示:window.print() 会弹出浏览器打印对话框,用户可以选择打印机、份数、页边距等。前端能做的就是控制打印什么内容和打印长什么样。
二、只打印部分内容
实际项目中,通常只打印页面的某一块(如订单详情、合同),而不是整个页面。
方案1:CSS 控制(推荐)
用 @media print 隐藏不需要打印的部分,最简单也最可控。
<template><div><!-- 页面其他内容(不打印) --><navclass="no-print">导航栏</nav><headerclass="no-print">顶部</header><!-- 要打印的内容 --><divclass="print-area"id="printArea"><h1>订单详情</h1><p>订单号:20240701001</p><p>金额:¥299.00</p><table><tr><th>商品</th><th>数量</th><th>单价</th></tr><tr><td>商品A</td><td>2</td><td>99</td></tr><tr><td>商品B</td><td>1</td><td>101</td></tr></table></div><!-- 操作按钮(不打印) --><divclass="no-print"><button @click="handlePrint">打印订单</button></div></div></template><scriptsetup>const handlePrint = () => {window.print()}</script><style>/* 打印时隐藏不需要打印的元素 */@media print {.no-print {display: none !important;}.print-area {width: 100%;margin: 0;padding: 20px;}}</style>
方案2:新建窗口打印(适合复杂内容)
如果打印内容和当前页面差异很大,可以新开一个窗口专门做打印页。
/*** 打印指定内容* @param {string} htmlContent - 要打印的 HTML 内容* @param {string} title - 打印页面标题*/export function printContent(htmlContent, title = '打印') {const printWindow = window.open('', '_blank', 'width=800,height=600')printWindow.document.write(`<!DOCTYPE html><html><head><title>${title}</title><style>body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; }table { width: 100%; border-collapse: collapse; }th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }th { background-color: #f5f5f5; }h1 { text-align: center; }@media print {body { padding: 0; }}</style></head><body>${htmlContent}</body></html>`)printWindow.document.close()// 等页面加载完成再打印printWindow.onload = () => {printWindow.print()printWindow.close()}}// 使用const handlePrint = () => {const html = `<h1>订单详情</h1><p>订单号:20240701001</p><table><tr><th>商品</th><th>数量</th><th>单价</th><th>小计</th></tr><tr><td>商品A</td><td>2</td><td>¥99</td><td>¥198</td></tr><tr><td>商品B</td><td>1</td><td>¥101</td><td>¥101</td></tr><tr><thcolspan="3">合计</th><th>¥299</th></tr></table>`printContent(html, '订单打印')}
提示:新窗口方案的好处是打印样式完全独立,不影响当前页面。缺点是弹窗可能被浏览器拦截(需要用户授权)。
三、打印样式优化
打印出来的效果要好看,需要注意几个关键点。
@media print {/* 1. 去掉背景色和阴影(省墨水) */* {background: none !important;box-shadow: none !important;color: #000 !important; /* 文字强制黑色 */}/* 2. 去掉链接下划线,显示URL */a {text-decoration: none;}a::after {content: ' (' attr(href) ')';font-size: 12px;color: #666;}/* 3. 控制分页 */.page-break {page-break-after: always; /* 强制分页 */}.no-break {page-break-inside: avoid; /* 避免在元素内部分页 */}/* 4. 去掉页眉页尾(部分浏览器支持) */@page {margin: 1cm;size: A4;}}
四、Vue3 封装成通用组件
把打印逻辑封装成组件,任何页面都能用。
<!-- components/PrintArea.vue --><template><div><divref="printRef"><slot /></div><buttonv-if="showButton" @click="handlePrint"class="print-btn">打印</button></div></template><scriptsetup>import { ref } from 'vue'const props = defineProps({showButton: { type: Boolean, default: true },title: { type: String, default: '打印' },})const printRef = ref(null)const handlePrint = () => {const content = printRef.value.innerHTMLconst printWindow = window.open('', '_blank', 'width=800,height=600')printWindow.document.write(`<!DOCTYPE html><html><head><title>${props.title}</title><style>body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; font-size: 14px; }table { width: 100%; border-collapse: collapse; margin: 10px 0; }th, td { border: 1px solid #333; padding: 6px 10px; }th { background: #f0f0f0; }h1, h2, h3 { margin: 10px 0; }@page { margin: 1.5cm; size: A4; }@media print { body { padding: 0; } }</style></head><body>${content}</body></html>`)printWindow.document.close()printWindow.onload = () => {printWindow.print()printWindow.close()}}</script>
使用:
<template><PrintAreatitle="合同打印"><h1>服务合同</h1><p>甲方:xxx公司</p><p>乙方:xxx公司</p><divclass="page-break"></div><h2>第一条 服务内容</h2><p>...</p></PrintArea></template><scriptsetup>import PrintArea from '@/components/PrintArea.vue'</script>
五、一张表总结
window.print() | |||
六、常见坑
1. 打印时表格被截断
/* ❌ 表格行被分页切断 *//* ✅ 给表格行加 avoid */@media print {tr {page-break-inside: avoid;}table {page-break-inside: auto;}}
2. 背景色打印不出来
/* 浏览器默认不打印背景色,需要用户手动勾选"打印背景图形" *//* 代码层面无法强制,但可以提示用户 *//* ✅ 用边框代替背景色 */@media print {.header {background: #f5f5f5; /* 屏幕显示 */border-bottom: 2px solid #333; /* 打印时也能看到 */}}
3. 新窗口被浏览器拦截
// ❌ 在异步回调中 open 会被拦截setTimeout(() => {window.open('') // 被拦截}, 1000)// ✅ 在用户点击事件中直接 openbutton.onclick = () => {window.open('') // 正常}
结尾引导
打印功能不需要装任何插件。核心就两步:控制打印内容(CSS隐藏或新窗口)+ 优化打印样式(@media print)。简单场景用 @media print 隐藏不需要打印的部分,复杂场景新开窗口专门渲染打印内容。封装成 PrintArea 组件后,整个项目复用。
关注「前端实战派」,每天分享可落地的前端、小程序、Node.js 实战干货,一起做能赚钱、能解决问题的前端工程师 🚀
夜雨聆风