在Vue项目中引入PDF文件有多种方法,下面我将介绍最简单实用的几种方案
前面的3种最简单了的,直接一行代码就可以了,并且不依靠依赖,占用为0kb
1=》使用<embed>
标签(最简单方法)
只需一行HTML标签,不需要安装任何额外库,
所有现代浏览器都支持,浏览器直接处理PDF渲染
但是呢,有利有弊,万事两难全:
PDF查看器样式直接是固定的,在小屏幕上可能显示不佳,
无法隐藏下载/打印按钮,只能加载同源或CORS允许的PDF(跨域限制)
部分截图:
代码:
<template>
<div class="pdf-container">
<h2>使用 <embed> 标签显示PDF</h2>
<embed
src="/docs/sample.pdf"
type="application/pdf"
width="100%"
height="600px"
/>
</div>
</template>
<style scoped>
.pdf-container {
max-width: 900px;
margin: 0 auto;
padding: 20px;
background: white;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
h2 {
color: #2c3e50;
margin-bottom: 20px;
text-align: center;
}
</style>
2=》使用<object>
标签
显示的效果基本上和上面一样的哈
和第一种相比较的话:
优点:
兼容性更好:支持更老的浏览器
提供后备内容:当无法显示时显示自定义内容
相对更标准:符合W3C规范
缺点:
与embed类似:存在相同的样式和功能限制
实现稍复杂:需要处理后备内容
部分截图:
代码:
<template>
<div class="pdf-container">
<h2>使用 <object> 标签显示PDF</h2>
<object
:data="pdfUrl"
type="application/pdf"
width="100%"
height="600px"
>
<p>无法显示PDF文件。请<a :href="pdfUrl">点击这里下载</a>。</p>
</object>
</div>
</template>
<script>
export default {
data() {
return {
pdfUrl: "/docs/sample.pdf"
}
}
}
</script>
<style scoped>
/* 同上 */
</style>
3=》使用<iframe>
标签
只需将PDF文件的URL作为<iframe>
的src
属性即可。
显示的效果基本上和上面两种一样的哈
限制:
- 如果设置有密码保护,则需要手动输入密码。
- 预览效果可能不如某些插件。
部分截图:
代码:
<template>
<div class="app-container">
<div>操作指南</div>
// 链接:
<!-- <iframe style="width: 100%; height: 100%;" src="http://xxx.pdf"></iframe> -->
//本地:
<iframe style="width: 100%; height: 100%;" src="/docs/sample.pdf"></iframe>
</div>
</template>
<script setup>
</script>
在使用 <iframe>
标签加载本地文件时,需要特别注意浏览器安全策略和路径处理问题。
4=》第三方包:
4.1 使用PDF.js
介绍:
pdfjs-dist
是 Mozilla 开发的 PDF.js 库的 NPM 发行版本,用于在浏览器或 Node.js 环境中渲染和处理 PDF 文件。它提供了强大的 API,使开发者能够在网页上实现 PDF 预览、文本提取、表单填写等功能,无需依赖浏览器内置的 PDF 插件。
特性
- 纯前端渲染:无需服务器支持,直接在浏览器中解析和渲染 PDF 文件。
- 跨平台兼容:支持现代浏览器(Chrome、Firefox、Safari、Edge 等)和 Node.js 环境。
- 高性能:使用 Web Workers 实现后台处理,避免阻塞主线程。
- 功能丰富:
- 文本提取与搜索
- 页面渲染与缩放
- 表单字段支持
- 注释与标记
- 书签与大纲解析
- 模块化设计:支持按需引入所需功能,减小打包体积。
安装:
#该命令会把 PDF.js 的最新稳定版本安装到项目里:
npm install pdfjs-dist
或者
# 特定版本 :以 3.4.120 版本为例
npm install pdfjs-dist@3.4.120
或者
#若仅在开发阶段需要使用 PDF.js,可将其作为开发依赖进行安装:
npm install pdfjs-dist --save-dev
基本用法
1,在浏览器中渲染 PDF
import * as pdfjsLib from 'pdfjs-dist';
// 配置 worker 路径(重要!)
pdfjsLib.GlobalWorkerOptions.workerSrc =
`//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.js`;
// 加载并渲染 PDF
const loadingTask = pdfjsLib.getDocument('path/to/your/pdf.pdf');
loadingTask.promise.then(pdf => {
pdf.getPage(1).then(page => {
const viewport = page.getViewport({ scale: 1.5 });
const canvas = document.getElementById('pdf-canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
});
});
2,提取 PDF 文本内容
page.getTextContent().then(textContent => {
const textItems = textContent.items;
const text = textItems.map(item => item.str).join(' ');
console.log('提取的文本:', text);
});
3,在 Node.js 中使用
const pdfjsLib = require('pdfjs-dist/legacy/build/pdf.js');
const fs = require('fs');
const path = require('path');
// 读取 PDF 文件(Buffer 格式)
const data = new Uint8Array(fs.readFileSync(path.join(__dirname, 'example.pdf')));
// 解析 PDF
pdfjsLib.getDocument(data).promise.then(pdf => {
// 处理 PDF...
});
一些需要知道的:
API:
getDocument
:加载 PDF 文件(支持 URL、ArrayBuffer、Base64 等格式)。pdf.getPage
:获取指定页码的页面。page.render
:渲染页面到 Canvas。page.getTextContent
:提取页面文本内容。pdf.getOutline
:获取 PDF 大纲(书签)。pdf.getMetadata
:获取 PDF 元数据(标题、作者等)。注意事项
Worker 配置:
- 必须通过
GlobalWorkerOptions.workerSrc
指定 worker 文件路径,否则渲染会变慢或报错。- 可使用 CDN 路径(如示例)或自行打包 worker 文件。
CORS 问题:
- 若从远程 URL 加载 PDF,需确保服务器设置了正确的 CORS 头。
TypeScript 支持:
- 包内已包含类型定义,无需额外安装。
安全限制:
- 浏览器环境中无法访问本地文件系统,需通过
<input type="file">
或拖放功能获取文件。
代码示例:
<template>
<div class="pdf-viewer">
<div class="header">
<h1>PDF查看器</h1>
<div class="controls">
<button @click="zoomOut" :disabled="scale <= 0.5">-</button>
<span>{{ Math.round(scale * 100) }}%</span>
<button @click="zoomIn" :disabled="scale >= 2.0">+</button>
<button @click="prevPage" :disabled="pageNum <= 1">上一页</button>
<input v-model.number="pageNum" type="number" min="1" :max="totalPages" @change="goToPage">
<span>/ {{ totalPages }}</span>
<button @click="nextPage" :disabled="pageNum >= totalPages">下一页</button>
<button @click="downloadPDF">下载PDF</button>
</div>
</div>
<div class="pdf-container" ref="pdfContainer">
<canvas ref="pdfCanvas"></canvas>
<div v-if="loading" class="loader">加载中...</div>
</div>
</div>
</template>
<script>
import * as pdfjsLib from 'pdfjs-dist/build/pdf';
import 'pdfjs-dist/build/pdf.worker.entry';
export default {
data() {
return {
pdfUrl: "/docs/sample.pdf",
pdfDoc: null,
pageNum: 1,
totalPages: 0,
scale: 1.0,
loading: true
}
},
mounted() {
this.loadPdf();
},
methods: {
async loadPdf() {
this.loading = true;
try {
pdfjsLib.GlobalWorkerOptions.workerSrc = window.pdfjsWorker;
const loadingTask = pdfjsLib.getDocument(this.pdfUrl);
this.pdfDoc = await loadingTask.promise;
this.totalPages = this.pdfDoc.numPages;
await this.renderPage();
} catch (err) {
console.error('PDF加载错误:', err);
alert('无法加载PDF文件');
} finally {
this.loading = false;
}
},
async renderPage() {
if (!this.pdfDoc) return;
const page = await this.pdfDoc.getPage(this.pageNum);
const canvas = this.$refs.pdfCanvas;
const ctx = canvas.getContext('2d');
const container = this.$refs.pdfContainer;
const viewport = page.getViewport({ scale: this.scale });
canvas.height = viewport.height;
canvas.width = viewport.width;
// 居中显示
canvas.style.margin = '0 auto';
canvas.style.display = 'block';
const renderContext = {
canvasContext: ctx,
viewport: viewport
};
await page.render(renderContext).promise;
},
prevPage() {
if (this.pageNum > 1) {
this.pageNum--;
this.renderPage();
}
},
nextPage() {
if (this.pageNum < this.totalPages) {
this.pageNum++;
this.renderPage();
}
},
goToPage() {
if (this.pageNum < 1) this.pageNum = 1;
if (this.pageNum > this.totalPages) this.pageNum = this.totalPages;
this.renderPage();
},
zoomIn() {
if (this.scale < 2.0) {
this.scale += 0.1;
this.renderPage();
}
},
zoomOut() {
if (this.scale > 0.5) {
this.scale -= 0.1;
this.renderPage();
}
},
downloadPDF() {
const link = document.createElement('a');
link.href = this.pdfUrl;
link.download = 'document.pdf';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
}
</script>
<style scoped>
.pdf-viewer {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1000px;
margin: 20px auto;
padding: 20px;
background: #f8f9fa;
border-radius: 12px;
box-shadow: 0 6px 18px rgba(0,0,0,0.1);
}
.header {
background: #2c3e50;
color: white;
padding: 15px 20px;
border-radius: 8px 8px 0 0;
margin-bottom: 20px;
}
h1 {
margin: 0 0 15px 0;
text-align: center;
font-weight: 500;
}
.controls {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
button {
padding: 8px 16px;
background: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s;
font-size: 14px;
}
button:hover {
background: #2980b9;
}
button:disabled {
background: #95a5a6;
cursor: not-allowed;
}
input {
width: 50px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
text-align: center;
}
.pdf-container {
position: relative;
min-height: 600px;
background: white;
border-radius: 8px;
overflow: auto;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
border: 1px solid #eee;
}
.loader {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 18px;
color: #7f8c8d;
}
canvas {
display: block;
margin: 0 auto;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
}
</style>