Vue3项目怎么实现带水印可打印的PDF预览+导出?
先理清楚Vue3项目做PDF相关功能的核心需求
我们在做Vue3后台、企业官网、甚至个人工具站的时候,经常会碰到这几个和PDF相关的高频场景:比如合同预览、报表导出、简历/产品手册在线生成、涉密资料只看不能复制打印之类的,但不同场景侧重不一样,有的只需要快速渲染别人上传的PDF就行,有的要前端自己写HTML生成带特定格式的复杂PDF,还有的要额外加安全限制——像刚才标题里说的水印、打印控制,所以在选技术方案之前,得先把自己项目的核心需求抠准,别上来就随便塞一个库,不然后期改起来麻烦得很。
Vue3做PDF常用的三个技术方案,分别适合什么场景?
目前在Vue3生态里,用得最顺手、社区维护也比较好的方案其实就三个,我们一个个说,你看完就能直接对应自己的需求选了。
纯渲染已有PDF,可选加安全限制
如果你只需要上传或者后端传一个PDF文件流过来,在页面上展示,偶尔加个水印、禁止右键复制打印这种小需求,那直接用 vue-pdf-embed 就行,这个库是专门为Vue3做PDF渲染轻量封装的,上手超级快,包体积也小。
纯渲染的步骤其实很简单:先npm install vue-pdf-embed pdfjs-dist,然后pdfjs-dist记得要配worker,不然在有些浏览器会报错——pdfjs-dist自带了两个worker,一个是渲染用的,一个是文字层(如果不需要选文字复制的话可以关了省点事),配置worker的话,在Vue3的main.js或者main.ts里直接引入worker的CDN或者本地路径就行,
import VuePdfEmbed from 'vue-pdf-embed'
import * as pdfjsLib from 'pdfjs-dist'
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.js`
app.component('VuePdfEmbed', VuePdfEmbed)
如果是本地打包部署怕CDN挂的话,也可以把node_modules/pdfjs-dist/build里的pdf.worker.min.js和pdf.worker.min.js.map复制到public文件夹下,然后路径写'/pdf.worker.min.js'。
然后在页面里用组件就好了,src可以是本地的静态资源路径、后端返回的文件流Blob、或者PDF的URL,禁止右键复制打印的话,可以给组件外面包个div,加个@contextmenu.prevent,然后加css的user-select: none; pointer-events: none;(pointer-events: none会连滚动条和翻页都禁用,如果需要滚动的话,记得单独给滚动容器开pointer-events),水印的话,有两种方法:一种是给vue-pdf-embed组件加一层绝对定位的透明div,然后用canvas或者CSS生成重复的水印文字/图片;另一种是用pdfjs-dist直接操作PDF原始数据,给每页都加上水印图层再渲染——后者更稳,因为就算用户把外层div的样式改了,水印还是在PDF本身里,导出的话也能带水印。
前端自己写HTML生成复杂PDF,支持样式自定义
如果你的PDF内容是动态的,比如后台的销售报表、用户的个人简历、带图片和表格的产品报价单,后端没法直接生成,或者不想给后端加压力,那用 html2pdf.js 就对了,这个库的原理是先把页面上的HTML或者指定的DOM元素转成canvas,然后再把canvas转成PDF,所以不管你用Vue3的插槽、Tailwind CSS、Element Plus还是Ant Design Vue写的样式,只要浏览器能渲染出来,它基本上都能生成出来——不过要注意一些兼容性问题,比如Flex布局在老版本浏览器上可能会有错位,还有阴影、渐变这些CSS3属性要设置html2pdf.js的options里的useCORS为true(如果图片是跨域的),useWebWorker为true(不然大文件会卡死页面)。
用html2pdf.js和Vue3结合也不复杂,甚至不需要专门封装Vue组件,直接在需要导出的地方引入就行,首先npm install html2pdf.js,然后在页面里写一个ref,指向你要生成PDF的那个DOM元素,
<template>
<div class="pdf-container">
<!-- 这部分是要生成PDF的内容,别把按钮、导航栏放进来 -->
<div ref="pdfContentRef" class="pdf-content">
<!-- 这里放你的Vue组件、HTML、样式 -->
</div>
<button @click="exportPdf">导出PDF</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import html2pdf from 'html2pdf.js'
const pdfContentRef = ref(null)
const exportPdf = () => {
const options = {
margin: 10,
filename: 'test.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, useCORS: true },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
}
html2pdf().set(options).from(pdfContentRef.value).save()
}
</script>
这里的options可以根据自己的需求调整,比如margin可以设成数组(上右下左),jsPDF的orientation可以设成'landscape'(横向),scale设成2或者3的话,生成的PDF文字会更清晰,但包体积也会更大。
前端直接操作PDF原始数据,适合高级需求
如果你需要做更高级的操作,比如合并多个PDF、拆分PDF、给PDF加密码、修改已有PDF的文字(虽然比较麻烦),那前面两个库就不够用了,得用 pdf-lib,这个库是用纯JavaScript写的,完全在前端运行,不需要后端,功能超级强大,而且社区维护得很好,最近两年更新挺频繁的。
用pdf-lib和Vue3结合的话,稍微复杂一点,因为它没有直接的渲染组件,要是需要预览的话,还得结合vue-pdf-embed或者pdfjs-dist来用——也就是先用pdf-lib处理PDF(比如加密码、合并、加水印),然后生成Blob,再传给vue-pdf-embed渲染,或者直接导出,举个简单的合并两个PDF的例子:首先npm install pdf-lib,然后引入,用fetch获取两个PDF的URL或者文件流,转成ArrayBuffer,然后用pdf-lib的PDFDocument.load()加载,再用PDFDocument.create()创建一个新的PDF,把加载好的PDF的每页都复制到新PDF里,最后saveAsBase64或者saveAsBlob,传给vue-pdf-embed或者下载就行。
三个方案的优缺点对比,帮你快速做选择
可能光看上面的描述你还是有点懵,那我们列个简单的对比表,直观一点:
| 技术方案 | 核心功能 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|---|
| vue-pdf-embed | 纯渲染PDF,可选简单安全限制 | 轻量、上手快、Vue3原生支持好 | 不能生成PDF,高级功能少 | 合同预览、资料展示、只读场景 |
| html2pdf.js | HTML转PDF,支持样式自定义 | 不需要懂PDF底层,写HTML就行,样式还原度高 | 大文件容易卡死页面,Flex/阴影等兼容性偶尔有问题 | 动态报表、简历、报价单生成 |
| pdf-lib | 直接操作PDF原始数据,高级功能 | 功能最全,纯前端运行,性能好 | 上手慢,需要懂一点PDF底层,没有直接渲染组件 | 合并拆分PDF、加密码、修改已有PDF |
几个常见的坑,你一定要避开
不管你选哪个方案,做Vue3 PDF相关功能的时候,都很容易踩这几个坑,我给你提前说一下,省得你浪费时间:
坑一:pdfjs-dist的worker配置不对
这个是用vue-pdf-embed最常见的坑,要是worker没配好,页面会直接报错“Failed to load PDF document”或者“Worker not loaded”,解决方法刚才说了,要么用CDN(注意版本要和你下载的pdfjs-dist一致,不然可能也会有问题),要么把worker文件放到public文件夹下。
坑二:html2pdf.js导出的PDF图片跨域
如果你的PDF内容里有跨域的图片,html2canvas(html2pdf.js的底层依赖之一)会渲染不出来,变成空白,解决方法有两个:一个是给图片的服务器加CORS头,允许你的域名访问;另一个是给图片标签加crossOrigin="anonymous"属性,同时把html2pdf.js的options里的html2canvas.useCORS设为true。
坑三:html2pdf.js导出的PDF文字太小或者太模糊
这个问题主要是因为html2canvas的scale设置得太小,默认是1,也就是和屏幕分辨率一样,打印出来会很糊,解决方法是把scale设成2或者3,但不要设得太大,不然包体积会超级大,而且生成时间会很长,jsPDF的unit最好设成mm或者cm,format设成a4或者你需要的纸张大小,这样文字的位置会更准确。
坑四:vue-pdf-embed的文字层不显示或者错位
如果你需要让用户选文字复制的话,记得要开vue-pdf-embed的show-text属性(默认是开的),如果文字层错位的话,可能是因为你的PDF容器的宽度或者高度设得不对,或者是PDF本身的问题,可以试试给vue-pdf-embed组件加一个固定的宽度,比如100%,然后让它自适应高度。
根据自己的需求选对方案就行
其实做Vue3 PDF相关功能,核心就是选对技术方案,然后避开那几个常见的坑就行了,如果只是纯渲染,用vue-pdf-embed;如果是动态生成复杂PDF,用html2pdf.js;如果是高级需求,用pdf-lib,要是需要同时实现预览、导出、加安全限制,那可以把几个库结合起来用,比如先用html2pdf.js生成PDF的Blob,然后用pdf-lib给它加密码和水印,再用vue-pdf-embed渲染,最后导出。
好了,今天的分享就到这里,你要是还有什么具体的问题,可以在评论区留言,我会尽量帮你解答。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网


