本文深入解析基于 React 和 PDF.js 构建 PDF 查看器的实现方案,该组件支持 PDF 渲染、图片打印和下载功能,并包含完整的加载状态与错误处理机制。
完整代码在最后
一个PDF 文件:
https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf
效果展示:

安装 PDF.js
要使用 pdf.js,你可以通过 npm 安装它。以下是安装和配置 pdf.js 的详细步骤:
1. 安装 pdfjs-dist
pdf.js 的官方 npm 包名为 pdfjs-dist。可以通过以下命令安装:
npm install pdfjs-dist
pdfjs-dist是 PDF.js 的分发版本,包含了核心功能和 Worker 文件。- 安装完成后,你可以在项目中直接引入并使用。
2. 设置 Worker 文件路径
PDF.js 需要一个 Worker 文件来处理 PDF 渲染任务。默认情况下,Worker 文件路径需要手动设置。
方法 1:使用 CDN
如果你不想将 Worker 文件打包到项目中,可以直接使用 CDN 提供的 Worker 文件:
import {
GlobalWorkerOptions } from 'pdfjs-dist';
// 设置 Worker 文件路径
GlobalWorkerOptions.workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';
方法 2:本地 Worker 文件
如果你希望将 Worker 文件打包到项目中,可以这样做:
-
在代码中引入 Worker 文件:
import { GlobalWorkerOptions, getDocument } from 'pdfjs-dist'; import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry'; // 设置 Worker 文件路径 GlobalWorkerOptions.workerSrc = pdfjsWorker; -
这种方式适合离线环境或需要完全控制依赖的情况。
技术架构解析
1. 核心依赖
- PDF.js:Mozilla 开源的 PDF 解析库,支持 Web 端 PDF 渲染
- React Hooks:使用 useState 管理状态,useEffect 处理副作用,useRef 操作 DOM
- Ant Design:采用 Button 组件构建操作界面
pdf.js 官网:https://mozilla.github.io/pdf.js/
2. 初始化配置
通过配置 Worker 文件实现 PDF 解析的 Web Worker 并行处理,避免阻塞主线程。
GlobalWorkerOptions.workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';
核心功能实现
1. PDF 渲染流程
这段代码的核心功能是加载一个 PDF 文件,并将其第一页渲染到 <canvas> 元素中。
// 加载 PDF 并渲染到 canvas
useEffect(() => {
const abortController = new AbortController();
if (!pdfUrl) return;
setIsLoading(true);
setError(null);
getDocument({
url: pdfUrl,
cMapUrl: '/cmaps/', // 引入字体映射文件
cMapPacked: true, // 启用字体映射文件
}).promise
.then((pdf) => {
if (abortController.signal.aborted) return;
console.log('PDF loaded');
// 获取第一页
pdf.getPage(1).then((page) => {
if (abortController.signal.aborted) {
page.cleanup();
return;
}
console.log('Page 1 loaded');
const scale = 1.5;
const viewport = page.getViewport({
scale });
if (!canvasRef.current) {
throw new Error('Canvas element not found');
}
const canvas = canvasRef.current;
const context = canvas.getContext('2d');
// 清除之前的画布内容
context?.clearRect(0, 0, canvas.width, canvas.height);
canvas.height = viewport.height;
canvas.width = viewport.width;
console.log('Canvas尺寸设置:', canvas.width, 'x', canvas.height);
// 渲染 PDF 页面到 canvas
const renderContext: any = {
canvasContext: context,
viewport: viewport,
};
const renderTask = page.render(renderContext);
renderTask.promise
.then(() => {
console.log('页面渲染完成');
setIsLoading(false);
})
.catch((renderError) => {
if (!abortController.signal.aborted) {
setError('页面渲染失败: ' + renderError.message);
}
});
});
})
.catch((error) => {
if (!abortController.signal.aborted) {
setError('PDF加载失败: ' + error.message);
console.error('Error loading PDF:', error);
}
})
.finally(() => {
if (!abortController.signal.aborted) {
setIsLoading(false);
}
});
return () => {
abortController.abort();
const canvas = canvasRef.current;
if (canvas) {
const context = canvas.getContext('2d');
context?.clearRect(0, 0, canvas.width, canvas.height);
}
};
}, [pdfUrl]);
- 初始化加载状态:设置加载提示和清理错误信息。
- 加载 PDF 文件:使用
getDocument加载 PDF,并获取第一页。 - 计算视口与画布尺寸:根据缩放比例调整
<canvas>的大小。 - 渲染 PDF 页面:将 PDF 页面绘制到
<canvas>上。 - 错误处理与清理:捕获加载和渲染中的错误,并在组件卸载时清理资源。
以下是核心实现原理与技术要点的详细解读:
1. useEffect 的作用
useEffect(()

最低0.47元/天 解锁文章
3962

被折叠的 条评论
为什么被折叠?



