在 Vue 3 应用中显示 PDF
在 Vue 3 单页应用中嵌入 PDF 听起来很简单——直到你遇到第一个 CORS 错误,发现浏览器在 Safari 上的渲染效果不同,或者意识到你的 200 页文档正在阻塞主线程。本文介绍开发者实际使用的三种在 Vue 3 中显示 PDF 的实用方法,并诚实地分析每种方法的权衡取舍。
核心要点
- 在 Vue 3 中显示 PDF 的三种主要方法是:原生浏览器嵌入(
<iframe>/<embed>)、直接集成 PDF.js 以及 Vue 专用封装组件。 - 原生嵌入不占用打包体积,但无法控制样式、工具栏或跨浏览器一致性。
- PDF.js 提供完全的渲染控制,但会增加明显的客户端负载——使用动态
import()进行懒加载以保持快速的初始加载时间。 - 像
vue-pdf-embed这样的 Vue PDF 封装库可以显著减少样板代码,但会牺牲一些自定义灵活性。 - 正确的 worker 配置和懒加载是防止 Vue 应用中最常见 PDF 相关问题的两个关键步骤。
三种核心方法
1. 原生浏览器嵌入:<iframe> 或 <embed>
将 PDF 显示在屏幕上的最快方法是将其交给浏览器内置的 PDF 渲染器。
<template>
<iframe
:src="pdfUrl"
width="100%"
height="700px"
style="border: none"
/>
</template>
<script setup lang="ts">
const pdfUrl = '/documents/report.pdf'
</script>
适用场景: 内部工具、管理后台,或任何 UI 一致性不重要且 PDF 来自同源的场景。
实际限制:
- 无法控制工具栏、缩放控件或主题
- 不同浏览器的行为各异——Chrome、Firefox 和 Safari 的渲染方式各不相同
- 移动浏览器通常会下载文件而不是内联显示
- 加载状态对 Vue 组件不可见,因此无法优雅地显示加载动画或处理错误
如果从不同域加载 PDF,浏览器会阻止请求,除非服务器发送正确的 Access-Control-Allow-Origin 头。在后端设置同源代理端点是最干净的解决方案。
2. PDF.js 与 Vue 3:直接集成
PDF.js 是 Mozilla 的开源 PDF 渲染引擎。从 5.x 版本系列开始,它作为 ESM 优先的包发布。你从 build/pdf.mjs 导入,并加载一个单独的 worker(pdf.worker.mjs)以将渲染工作移出主线程。
npm install pdfjs-dist
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs'
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.mjs',
import.meta.url
).toString()
const canvasRef = ref<HTMLCanvasElement | null>(null)
onMounted(async () => {
const pdf = await pdfjsLib.getDocument('/documents/report.pdf').promise
const page = await pdf.getPage(1)
const viewport = page.getViewport({ scale: 1.5 })
const canvas = canvasRef.value!
const ctx = canvas.getContext('2d')!
canvas.height = viewport.height
canvas.width = viewport.width
await page.render({ canvasContext: ctx, viewport }).promise
})
</script>
<template>
<canvas ref="canvasRef" />
</template>
你获得的优势: 完全控制渲染、页面导航、缩放和主题。PDF.js 支持标准注释和 AcroForm 表单字段,尽管表单和注释行为可能取决于渲染配置。
你应该知道的:
pdfjs-dist包会为客户端打包增加明显的负载。使用动态import()进行懒加载。- 基于 XFA 的表单(在较旧的政府 PDF 中常见)支持有限,可能无法正确渲染。
- 对于大型文档,当服务器支持 HTTP 范围请求(
Accept-Ranges: bytes)时,PDF.js 可以分块获取 PDF,这可以提高大文件的感知性能。 - Worker 配置是 Vite 项目中最常见的设置问题。上面显示的
new URL(..., import.meta.url)模式可以在构建时正确解析 worker 路径。
Discover how at OpenReplay.com.
3. Vue PDF 封装库
几个维护良好的 Vue 3 组件封装了 PDF.js,并提供更简单的组件 API。vue-pdf-embed 是一个积极维护的选项,它为你处理 worker 设置、页面渲染和响应式 prop 更新。
<script setup lang="ts">
import VuePdfEmbed from 'vue-pdf-embed'
</script>
<template>
<VuePdfEmbed source="/documents/report.pdf" />
</template>
权衡是用更少的细粒度控制来换取显著减少的样板代码。当你需要快速获得一个可用的 Vue 3 PDF 查看器且不需要自定义渲染管道时,这些封装库是不错的选择。
注意: 一些较旧的包如
vue3-pdf-app不再积极维护。在采用任何封装库之前,请评估其维护状态以及与 Vite 等现代打包工具的兼容性。
选择正确的方法
| 方法 | 打包成本 | 自定义程度 | 最适合 |
|---|---|---|---|
<iframe> / <embed> | 0 KB | 无 | 快速嵌入、同源文件 |
| 直接使用 PDF.js | 客户端负载 | 完全 | 自定义查看器、大型文档 |
| Vue 封装库 | 与 PDF.js 相似 | 中等 | 快速设置、标准用例 |
总结
对于大多数 Vue 3 应用,决策很简单:对于简单的同源嵌入使用 <iframe>,当你需要一个无需太多设置的可用查看器时选择维护良好的 Vue 封装库,当你需要完全控制大型文档的渲染、导航或性能时直接集成 PDF.js。无论选择哪条路径,都要正确配置 PDF.js worker 并懒加载库。仅这两个步骤就能防止开发者在 Vue 应用中嵌入 PDF 时遇到的最常见问题。
常见问题
浏览器会阻止跨域 PDF 请求,除非服务器在响应中包含 Access-Control-Allow-Origin 头。最可靠的解决方案是在你自己的后端设置一个同源代理端点,该端点获取 PDF 并将其提供给你的 Vue 应用。这避免了依赖你可能无法控制的第三方服务器配置。
Vite 处理资源 URL 的方式与 Webpack 不同。使用 new URL 与 import.meta.url 模式在构建时解析 worker 路径。例如,将 workerSrc 设置为 new URL('pdfjs-dist/build/pdf.worker.mjs', import.meta.url),然后调用 toString()。这确保 Vite 在开发和生产构建期间正确处理 worker 文件。
可以。加载文档后,从 1 循环到 pdf.numPages,为每个页码调用 getPage,为每个页面创建一个 canvas 元素,然后按顺序或并行渲染它们。对于非常大的文档,考虑仅渲染可见页面并在滚动时加载其他页面,以避免高内存使用和缓慢的初始渲染时间。
如果你需要一个标准查看器且设置最少,并且不需要深度自定义,请使用像 vue-pdf-embed 这样的封装库。当你需要完全控制渲染、自定义导航、主题或性能优化(如懒加载页面)时,直接集成 PDF.js。封装库在 PDF.js 上添加了一个薄层,因此打包成本几乎相同。
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.