Vue.js调用window.print()新开打印页无法显示PDF问题
问题描述
在page.vue页面中使用基于vue-pdf封装的自定义PDF Reader组件嵌入PDF文档,页面设置导出按钮,点击按钮后通过window.print()方法在新开页面中导出PDF。当前原页面PDF可正常展示,但点击导出后新开的打印页面无法显示PDF内容,呈空白状态。
现有实现代码
page.vue 核心逻辑
<template> <button class="btn ml-1" @click="handleWindowPrint()">Export PDF</button> .... <div class="pdf-export" v-for="record in pdfRecords" :key="record.key"> <PdfReader :documentUrl="record" @onclick-remove-pdf-btn="removePdfRecord" /> </div> .... </template>
handleWindowPrint() { const printPage = window.open('', '_blank') const pdf = document.querySelector('.pdf-export') const view = { ... }; const html = Mustache.render(`${this.templateUsed}`, view); if (printPage) { printPage.document.head.innerHTML = '<meta charset="utf-8">\n' + ' <meta name="format-detection" content="telephone=no">\n' + ' <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">\n' + ' <meta name="viewport" content="width=device-width,initial-scale=1.0">\n' printPage.document.body.innerHTML = html + pdf?.outerHTML setTimeout(() => { printPage.print(); }, 500); } }
自定义PdfReader组件代码
<template> <div> <label><small>(Perview Three Page)</small></label> <figure class="image-item pdf-item"> <a > <pdf :key="i" :src="url" v-for="i in numPages" :page="i"></pdf> </a> </figure> </div> </template> <script> import pdf from "vue-pdf"; export default { name: "PdfReader", props: ["documentUrl", "removeable"], components: { pdf, }, data: function () { return { url: this.documentUrl, numPages: 3, }; }, // 此处存在语法笔误:原代码误写为</style>,应为</script> </script>
问题根因
现有代码存在4个核心逻辑错误,直接导致打印页PDF空白:
- 仅拷贝静态DOM结构,丢失Canvas绘制内容:
vue-pdf是基于PDF.js将PDF分页绘制到<canvas>元素上实现预览的,而outerHTML只能拿到DOM标签本身,不会携带canvas上已经渲染好的像素数据,新页面拿到的canvas标签只是空壳。 - 新页面缺失运行依赖:新开的是空白页,仅插入了基础meta标签,没有引入Vue、vue-pdf、PDF.js相关的运行资源,就算DOM结构完整,也无法重新执行组件渲染逻辑。
- 触发打印时机错误:500ms的固定延时无法保证内容渲染完成,就算补全依赖,PDF加载、分页绘制是异步过程,未等内容绘制完成就触发打印,依然会拿到空白内容。
- 节点获取逻辑bug:页面上
.pdf-export是v-for循环生成的多元素节点,document.querySelector('.pdf-export')只会获取第一个匹配的节点,多PDF场景下会丢失后续PDF内容。
可落地修复方案
方案1:转图片插入打印页(改造成本最低,兼容性最好)
不需要在新页面重新渲染PDF组件,直接把原页面已经渲染好的PDF内容转成图片插入,彻底避免依赖缺失、渲染时机问题:
- 原页面PDF渲染完成后,遍历PdfReader组件内所有渲染好的canvas元素,调用
canvas.toDataURL('image/png')获取每页PDF的base64位图地址 - 生成打印页内容时,把原来的pdf组件DOM替换成对应base64地址的
<img>标签 - 监听所有img标签的
onload事件,等全部图片加载完成后再调用printPage.print(),不需要额外引入任何依赖
核心参考代码:
handleWindowPrint() { const printPage = window.open('', '_blank') // 修复:获取所有PDF导出区域 const pdfContainers = document.querySelectorAll('.pdf-export') const view = { /* 原有模板参数 */ } const html = Mustache.render(`${this.templateUsed}`, view); // 把每个容器里的canvas转成img let pdfHtml = '' pdfContainers.forEach(container => { const canvases = container.querySelectorAll('canvas') let containerHtml = '<div class="pdf-export">' canvases.forEach(canvas => { const imgSrc = canvas.toDataURL('image/png') containerHtml += `<img src="${imgSrc}" style="width:100%;page-break-after:always;">` }) containerHtml += '</div>' pdfHtml += containerHtml }) if (printPage) { printPage.document.head.innerHTML = ` <meta charset="utf-8"> <meta name="format-detection" content="telephone=no"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <style>img{max-width:100%;margin:10px 0;}</style> ` printPage.document.body.innerHTML = html + pdfHtml // 等所有图片加载完再打印 const imgs = printPage.document.querySelectorAll('img') let loadedCount = 0 imgs.forEach(img => { img.onload = () => { loadedCount++ if (loadedCount === imgs.length) { setTimeout(() => printPage.print(), 100) } } }) // 没有图片的情况直接打印 if (imgs.length === 0) { setTimeout(() => printPage.print(), 100) } } }
方案2:当前页直接打印(体验最好,无需新开页)
不需要新开空白页,通过打印样式表控制打印范围,完全避免跨页面DOM拷贝的问题:
- 新增打印专用CSS,通过
@media print媒体查询,隐藏页面上按钮、导航、侧边栏等不需要打印的元素,只保留.pdf-export区域为可见状态 - 确认页面上所有PDF分页都渲染完成(可监听vue-pdf的
@rendered事件计数,所有页渲染完成后再触发打印) - 直接调用
window.print()调起打印即可,不需要做DOM拷贝操作
额外注意事项
- 先修复PdfReader组件末尾的标签笔误,将误写的
</style>改为</script>,保证组件基础运行正常 - 如果PDF存在跨域问题,canvas转图片时会出现跨域污染报错,需要提前给PDF资源配置跨域允许头
内容的提问来源于stack exchange,提问作者Lucas Ho
相关产品推荐
相关产品推荐

