前言
在实际的前端开发中,经常会碰到需要在线预览PDF
文件的场景,比如后台管理系统查看合同、教育平台展示试卷、审批系统预览发票等等。这类需求的核心目标就是:用户不需要下载,就能直接在页面中查看 PDF 文档内容。
最近,我在公司的Vue3
项目中刚好负责了一块PDF
预览功能的开发,趁热打铁把这块功能的实现过程整理出来,希望能帮到有类似需求的朋友。
📌 常见的 PDF 文件预览方式有哪些?
在查阅了一些资料,也踩了几个坑之后,我发现目前主流的三种方式大概如下:
实现方式 | 简介 | 优点 | 缺点 |
---|---|---|---|
iframe / embed 标签 |
直接嵌入浏览器内核的渲染功能 | 简单,不需要引入额外依赖 | 样式和交互无法自定义,兼容性差 |
PDF.js(pdfjs-dist ) |
由 Mozilla 出品的开源 PDF 渲染器,支持 canvas / svg 渲染 | 功能强大,完全前端渲染,可高度定制 | 实现略复杂,需要自己处理分页、缩放等逻辑 |
后端转图片 | 将 PDF 转成图片给前端展示 | 前端负担小,兼容性好 | 没有文字层,不能选中、搜索、复制文字,服务端压力大 |
我最终选用的是 PDF.js(pdfjs-dist) ,原因很简单:其由 Mozilla 维护,稳定性强,功能完善,支持多页分页、缩放、搜索、高亮等能力。还有就是需求需要的是可以交互、可以选中文本的预览组件,而且尽量不依赖后端处理。
🔧 开始动手:安装并配置 pdfjs-dist
先安装它:
npm install pdfjs-dist
然后,在我们自己的工具函数里,引入并配置好Web Worker
路径(为了性能,PDF.js
会用 worker
异步解析):
typescript
import * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs'
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${(pdfjsLib as any).version}/build/pdf.worker.mjs`
⚠️ 注意:如果你用的是Vite,pdfjs
的worker
不好自动打包,用CDN
路径是目前最稳妥的方式。当然你也可以自行下载后配置本地路径。
🧠 核心功能封装:纯 JS 函数渲染PDF到DOM中
以下是我封装好的一个核心函数,接收PDF
文件地址和容器DOM
,然后渲染出完整PDF
页内容。
typescript
// utils/pdfRenderer.ts
import * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs'
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${(pdfjsLib as any).version}/build/pdf.worker.mjs`
interface RenderPdfOptions {
scale?: number
page?: number
}
export async function renderPdfToContainer(
container: HTMLElement,
url: string,
options?: RenderPdfOptions
) {
container.innerHTML = ''
const scale = options?.scale ?? 1.5
const targetPage = options?.page
try {
const loadingTask = pdfjsLib.getDocument(url)
const pdf = await loadingTask.promise
const pages = targetPage
? [targetPage]
: Array.from({ length: pdf.numPages }, (_, i) => i + 1)
for (const pageNum of pages) {
const page = await pdf.getPage(pageNum)
const viewport = page.getViewport({ scale })
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
if (!context) continue
canvas.width = viewport.width
canvas.height = viewport.height
container.appendChild(canvas)
const renderContext = {
canvasContext: context,
viewport
}
await page.render(renderContext).promise
}
} catch (error) {
console.error('PDF 加载失败:', error)
container.innerHTML = '<p style="color:red">加载失败,请检查文件格式或地址是否正确。</p>'
}
}
🧩 在 Vue3 组件中如何使用?
在Vue3
项目中写了一个简单组件,效果就是加载PDF
文件并将其渲染到页面中:
xml
<template>
<div ref="pdfWrapper" class="pdf-container" />
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { renderPdfToContainer } from '@/utils/pdfRenderer'
const pdfWrapper = ref<HTMLElement>()
const pdfUrl = '/files/sample.pdf' // 替换成你的文件地址
onMounted(() => {
if (pdfWrapper.value) {
renderPdfToContainer(pdfWrapper.value, pdfUrl, { scale: 1.25 })
}
})
</script>
<style scoped>
.pdf-container {
width: 100%;
overflow: auto;
background: #f6f6f6;
padding: 16px;
}
</style>
✅ 小提醒:为了避免渲染异常,
renderPdfToContainer
必须在onMounted
后 DOM 挂载后调用。
🚧 遇到的问题和一些解决方案
这里是开发过程中真实踩过的几个坑,顺手整理一下,希望你少走弯路。
❌ 问题一:加载失败,控制台报错 "Unexpected token '<'..."
原因 :PDF
地址可能不对,返回的不是PDF
,而是HTML
错误页。 解决方式:
- 检查地址是不是
404
- 用
fetch(url)
看看实际返回内容是不是PDF
❌ 问题二:PDF渲染模糊,不清晰
原因 :canvas
分辨率低或者devicePixelRatio
没处理。 优化方法:
ini
const dpr = window.devicePixelRatio || 1
canvas.width = viewport.width * dpr
canvas.height = viewport.height * dpr
canvas.style.width = `${viewport.width}px`
canvas.style.height = `${viewport.height}px`
context.setTransform(dpr, 0, 0, dpr, 0, 0)
这样处理后,高清屏下也能展示得更清晰。
❌ 问题三:只想加载某一页怎么办?
直接传 page
参数:
scss
renderPdfToContainer(dom, url, { page: 1 })
我自己在做分页加载的时候也用到了这个逻辑。
❌ 问题四:怎么支持本地文件预览(用户上传后立即预览)?
javascript
const reader = new FileReader()
reader.onload = async () => {
const typedArray = new Uint8Array(reader.result as ArrayBuffer)
const pdf = await pdfjsLib.getDocument({ data: typedArray }).promise
// 同样调用 page.render 即可渲染
}
reader.readAsArrayBuffer(file)
这个写法可以用在 <input type="file">
的上传场景。
🧾 小结一下
整个PDF
文件解析功能,其实可以拆成三步:
- 引入
pdfjs-dist
并设置好worker
- 封装一个
renderPdfToContainer
函数做PDF
渲染 - 在
Vue3
项目中按需调用
整个过程其实不复杂,但很多资料都散着,或者讲得太官方,不太实用。我这次是花了些时间整理,并亲自踩坑测试的,如果你也有这类需求,希望这篇文章能让你少走几步弯路。
结语
Vue3
项目中实现PDF
预览并不难,但要实现 高性能、强交互、低资源占用 的用户体验,需要我们不断抽象渲染逻辑、优化性能瓶颈并兼顾跨平台适配。希望本文能帮助你快速掌握在Vue3
中集成PDF.js
的实战方案。
后语
小伙伴们,如果觉得本文对你有些许帮助,点个👍或者➕个关注再走吧^_^ 。另外如果本文章有问题或有不理解的部分,欢迎大家在评论区评论指出,我们一起讨论共勉。