乐于分享
好东西不私藏

前端打印功能最简实现,不用插件

前端打印功能最简实现,不用插件

前端打印功能最简实现,不用插件

打印功能看似简单,实际上坑不少:打印样式和页面样式冲突、只打印部分内容不好控制、分页断裂、页眉页尾关不掉。很多人第一反应是装 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 {    width100%;    margin0;    padding20px;  }}</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; padding20px; }        table { width100%border-collapse: collapse; }        thtd { border1px solid #dddpadding8pxtext-align: left; }        th { background-color#f5f5f5; }        h1 { text-align: center; }        @media print {          body { padding0; }        }      </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-size12px;    color#666;  }  /* 3. 控制分页 */  .page-break {    page-break-after: always;  /* 强制分页 */  }  .no-break {    page-break-inside: avoid;  /* 避免在元素内部分页 */  }  /* 4. 去掉页眉页尾(部分浏览器支持) */  @page {    margin1cm;    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.innerHTML  const 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; padding20pxfont-size14px; }        table { width100%border-collapse: collapse; margin10px 0; }        thtd { border1px solid #333padding6px 10px; }        th { background#f0f0f0; }        h1h2h3 { margin10px 0; }        @page { margin1.5cm; size: A4; }        @media print { body { padding0; } }      </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()
 + CSS
简单页面
零代码、可控
样式要单独写
新窗口打印
内容差异大
样式独立
可能被拦截
PrintArea 组件
多个打印场景
复用方便
多一个组件

六、常见坑

1. 打印时表格被截断

/* ❌ 表格行被分页切断 *//* ✅ 给表格行加 avoid */@media print {  tr {    page-break-inside: avoid;  }  table {    page-break-inside: auto;  }}

2. 背景色打印不出来

/* 浏览器默认不打印背景色,需要用户手动勾选"打印背景图形" *//* 代码层面无法强制,但可以提示用户 *//* ✅ 用边框代替背景色 */@media print {  .header {    background#f5f5f5;       /* 屏幕显示 */    border-bottom2px solid #333/* 打印时也能看到 */  }}

3. 新窗口被浏览器拦截

// ❌ 在异步回调中 open 会被拦截setTimeout(() => {  window.open('')  // 被拦截}, 1000)// ✅ 在用户点击事件中直接 openbutton.onclick = () => {  window.open('')  // 正常}

结尾引导

打印功能不需要装任何插件。核心就两步:控制打印内容(CSS隐藏或新窗口)+ 优化打印样式(@media print)。简单场景用 @media print 隐藏不需要打印的部分,复杂场景新开窗口专门渲染打印内容。封装成 PrintArea 组件后,整个项目复用。

关注「前端实战派」,每天分享可落地的前端、小程序、Node.js 实战干货,一起做能赚钱、能解决问题的前端工程师 🚀