# React-PDF 功能总结文档 > **库版本**: react-pdf v9.2.1 > **基于**: Mozilla PDF.js > **测试Demo**: `/pdf-demo` > **创建时间**: 2025-11-22 --- ## 📋 目录 - [1. 库概述](#1-库概述) - [2. 核心功能](#2-核心功能) - [3. 内置功能详解](#3-内置功能详解) - [4. API参考](#4-api参考) - [5. 实践示例](#5-实践示例) - [6. 性能优化](#6-性能优化) - [7. 常见问题](#7-常见问题) --- ## 1. 库概述 ### 1.1 什么是 react-pdf? `react-pdf` 是一个用于在 React 应用中**显示和预览 PDF 文件**的库,基于 Mozilla 的 PDF.js 引擎构建。 ### 1.2 主要特性 - ✅ **查看 PDF** - 在浏览器中渲染和显示 PDF 文档 - ✅ **多页支持** - 支持渲染单页或所有页面 - ✅ **文本选择** - 启用文本层后可选择和复制文本 - ✅ **缩放控制** - 内置缩放和旋转功能 - ✅ **多种渲染模式** - Canvas 或 SVG 渲染 - ✅ **注释支持** - 显示 PDF 内置注释 - ✅ **响应式** - 适应不同屏幕尺寸 - ✅ **高性能** - 基于 WebWorker 的异步渲染 ### 1.3 与 @react-pdf/renderer 的区别 | 特性 | react-pdf | @react-pdf/renderer | |------|-----------|---------------------| | **用途** | 查看已有PDF | 生成新PDF | | **输入** | PDF文件 | React组件 | | **输出** | 浏览器渲染 | PDF文件 | | **文本选择** | ✅ 支持 | ❌ 不适用 | | **交互性** | ✅ 高 | ❌ 无 | --- ## 2. 核心功能 ### 2.1 基础文档渲染 #### 2.1.1 Document 组件 **⚠️ 第一步:导入必需的 CSS 文件** ```typescript import { Document, Page, pdfjs } from 'react-pdf'; // 🔑 必须导入CSS,否则文本层无法工作! import 'react-pdf/dist/Page/TextLayer.css'; import 'react-pdf/dist/Page/AnnotationLayer.css'; // 配置Worker pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.js'; ``` **基本使用:** ```typescript 加载中...} // 自定义加载组件 error={
加载失败
} // 自定义错误组件 noData={
无数据
} // 无数据组件 > {/* Page 组件 */}
``` **支持的文件格式**: - URL 字符串: `"http://example.com/file.pdf"` - File 对象: `new File([blob], "file.pdf")` - ArrayBuffer: 二进制数据 - Base64: `"data:application/pdf;base64,JVBERi0x..."` #### 2.1.2 Page 组件 ```typescript import { Page } from 'react-pdf'; ``` ### 2.2 事件回调 #### 2.2.1 Document 事件 ```typescript // 加载成功 function onDocumentLoadSuccess({ numPages }: { numPages: number }) { console.log('PDF总页数:', numPages); } // 加载失败 function onDocumentLoadError(error: Error) { console.error('PDF加载失败:', error.message); } // 加载进度 function onDocumentLoadProgress({ loaded, total }: { loaded: number; total: number }) { const progress = Math.round((loaded / total) * 100); console.log('加载进度:', progress + '%'); } ``` #### 2.2.2 Page 事件 ```typescript // 页面加载成功 function onPageLoadSuccess(page: any) { console.log('页面渲染成功:', page.pageNumber); console.log('页面尺寸:', page.width, page.height); } // 页面加载失败 function onPageLoadError(error: Error) { console.error('页面渲染失败:', error); } ``` --- ## 3. 内置功能详解 ### 3.1 缩放功能 (Scale) #### 3.1.1 基本使用 ```typescript const [scale, setScale] = useState(1.0); // 放大 const handleZoomIn = () => { setScale(prev => Math.min(prev + 0.25, 3.0)); // 最大300% }; // 缩小 const handleZoomOut = () => { setScale(prev => Math.max(prev - 0.25, 0.5)); // 最小50% }; // 应用缩放 ``` #### 3.1.2 缩放建议值 | 缩放级别 | scale值 | 使用场景 | |---------|---------|----------| | 极小 | 0.5 | 预览缩略图 | | 小 | 0.75 | 查看概览 | | 正常 | 1.0 | 默认阅读 | | 大 | 1.5 | 放大查看细节 | | 极大 | 2.0-3.0 | 精细查看文本 | #### 3.1.3 注意事项 - ⚠️ 缩放会影响性能,建议范围 0.5-3.0 - ⚠️ 缩放不会改变文本层坐标,需要手动调整高亮位置 - ✅ 使用 `transform: scale()` CSS 可以实现视觉缩放但不重新渲染 ### 3.2 旋转功能 (Rotate) #### 3.2.1 基本使用 ```typescript const [rotation, setRotation] = useState(0); // 左转90度 const handleRotateLeft = () => { setRotation(prev => (prev - 90) % 360); }; // 右转90度 const handleRotateRight = () => { setRotation(prev => (prev + 90) % 360); }; // 应用旋转 ``` #### 3.2.2 支持的旋转角度 - `0°` - 正常方向 - `90°` - 顺时针旋转90度 - `180°` - 倒置 - `270°` - 顺时针旋转270度(或逆时针90度) **注意**: 仅支持 90 度的倍数,其他角度无效。 ### 3.3 文本层 (Text Layer) #### 3.3.1 启用文本层 **⚠️ 重要:必须导入 CSS 文件!** ```typescript // 导入react-pdf的CSS样式(文本层必需) import 'react-pdf/dist/Page/TextLayer.css'; import 'react-pdf/dist/Page/AnnotationLayer.css'; ``` **常见错误**:如果不导入CSS,文本层虽然会渲染,但文本**无法被选择**! #### 3.3.2 文本层功能 启用后可实现: 1. **文本选择** - 用户可以用鼠标选中文本 2. **文本复制** - 选中后可以复制文本 3. **文本搜索** - 浏览器原生搜索功能 (Ctrl+F) 4. **屏幕阅读器** - 可访问性支持 #### 3.3.3 文本选择监听 ```typescript const handleTextSelection = () => { const selection = window.getSelection(); if (!selection || selection.isCollapsed) return; const selectedText = selection.toString(); console.log('选中文本:', selectedText); };
``` #### 3.3.4 文本层样式 默认文本层是**透明的**,覆盖在 Canvas 渲染的 PDF 上方。可以通过 CSS 自定义: ```css /* 文本层容器 */ .react-pdf__Page__textContent { /* 默认透明,文本不可见但可选择 */ opacity: 0.2; /* 调试时可以显示文本层 */ } /* 文本层中的文本 */ .react-pdf__Page__textContent span { color: transparent; /* 文本透明 */ position: absolute; white-space: pre; } ``` ### 3.4 注释层 (Annotation Layer) #### 3.4.1 启用注释层 ```typescript ``` #### 3.4.2 支持的注释类型 - 📝 文本注释 (Text Annotations) - 🔗 链接注释 (Link Annotations) - 📎 附件注释 (File Attachment Annotations) - 🎨 高亮/下划线 (Highlight/Underline) - 💬 弹出注释 (Popup Annotations) #### 3.4.3 注释交互 - ✅ 链接可点击(内部链接、外部URL) - ✅ 注释可以悬停显示提示 - ✅ 表单字段可交互(如果PDF包含表单) ### 3.5 渲染模式 (Render Mode) #### 3.5.1 Canvas 模式(默认) ```typescript ``` **特点**: - ✅ 性能最佳 - ✅ 内存占用低 - ✅ 适合大多数场景 - ❌ 缩放时可能模糊 - ❌ 打印质量一般 #### 3.5.2 SVG 模式 ```typescript ``` **特点**: - ✅ 矢量渲染,无限缩放不失真 - ✅ 打印质量极佳 - ✅ 支持复杂图形 - ❌ 性能较低 - ❌ 内存占用高 - ❌ 不适合大文档 #### 3.5.3 选择建议 | 场景 | 推荐模式 | 原因 | |------|---------|------| | 普通阅读 | Canvas | 性能好 | | 高清打印 | SVG | 质量高 | | 移动端 | Canvas | 省内存 | | 频繁缩放 | SVG | 不失真 | | 大文档(>50页) | Canvas | 性能 | ### 3.6 设备像素比 (Device Pixel Ratio) #### 3.6.1 基本使用 ```typescript ``` #### 3.6.2 作用 - 🖥️ 在高分辨率屏幕(Retina屏)上提高清晰度 - 📱 适配不同设备的像素密度 - ⚡ 值越大,渲染质量越高,但性能消耗也越大 **常见值**: - 普通屏幕: `1` - Retina屏: `2` - 4K屏: `3` 或更高 **建议**: 使用 `window.devicePixelRatio` 自动适配。 ### 3.7 Worker 配置 #### 3.7.1 设置 Worker 路径 ```typescript import { pdfjs } from 'react-pdf'; // 使用本地worker文件 pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.js'; // 或使用CDN pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`; ``` #### 3.7.2 Worker 作用 - ⚡ **异步渲染** - 在后台线程处理PDF解析 - 🚫 **不阻塞UI** - 主线程不会被卡住 - 📈 **性能提升** - 大文件加载更流畅 --- ## 4. API 参考 ### 4.1 Document Props | 属性 | 类型 | 默认值 | 说明 | |-----|------|--------|------| | `file` | `string \| File \| ArrayBuffer \| object` | **必填** | PDF文件源 | | `loading` | `ReactNode` | `"Loading PDF..."` | 加载中显示的内容 | | `error` | `ReactNode` | `"Failed to load PDF"` | 加载失败显示的内容 | | `noData` | `ReactNode` | `"No PDF file specified"` | 无数据时显示的内容 | | `onLoadSuccess` | `(pdf) => void` | - | 加载成功回调 | | `onLoadError` | `(error) => void` | - | 加载失败回调 | | `onLoadProgress` | `(progress) => void` | - | 加载进度回调 | | `onSourceSuccess` | `() => void` | - | 文件源加载成功回调 | | `onSourceError` | `(error) => void` | - | 文件源加载失败回调 | | `rotate` | `0 \| 90 \| 180 \| 270` | `0` | 旋转角度 | | `className` | `string` | - | CSS类名 | | `inputRef` | `RefObject` | - | 引用 | ### 4.2 Page Props | 属性 | 类型 | 默认值 | 说明 | |-----|------|--------|------| | `pageNumber` | `number` | **必填** | 页码(从1开始) | | `scale` | `number` | `1.0` | 缩放比例 | | `rotate` | `0 \| 90 \| 180 \| 270` | `0` | 旋转角度 | | `width` | `number` | - | 页面宽度(px) | | `height` | `number` | - | 页面高度(px) | | `renderTextLayer` | `boolean` | `true` | 是否渲染文本层 | | `renderAnnotationLayer` | `boolean` | `true` | 是否渲染注释层 | | `renderMode` | `"canvas" \| "svg"` | `"canvas"` | 渲染模式 | | `devicePixelRatio` | `number` | `1` | 设备像素比 | | `onLoadSuccess` | `(page) => void` | - | 页面加载成功回调 | | `onLoadError` | `(error) => void` | - | 页面加载失败回调 | | `onRenderSuccess` | `() => void` | - | 渲染成功回调 | | `onRenderError` | `(error) => void` | - | 渲染失败回调 | | `className` | `string` | - | CSS类名 | | `inputRef` | `RefObject` | - | 引用 | ### 4.3 回调函数参数 #### onDocumentLoadSuccess ```typescript interface PDFDocumentProxy { numPages: number; // 总页数 fingerprints: string[]; // 文档指纹 getPage(pageNumber: number): Promise; getMetadata(): Promise; // ... 更多方法 } ``` #### onPageLoadSuccess ```typescript interface PDFPageProxy { pageNumber: number; // 页码 rotate: number; // 旋转角度 ref: object; // 页面引用 userUnit: number; // 用户单位 view: number[]; // 视图矩形 [x1, y1, x2, y2] width: number; // 原始宽度 height: number; // 原始高度 // ... 更多属性和方法 } ``` --- ## 5. 实践示例 ### 5.1 完整的PDF查看器 ```typescript import { useState } from 'react'; import { Document, Page, pdfjs } from 'react-pdf'; pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.js'; function PdfViewer({ url }: { url: string }) { const [numPages, setNumPages] = useState(null); const [pageNumber, setPageNumber] = useState(1); const [scale, setScale] = useState(1.0); return (
{/* 工具栏 */}
缩放: {Math.round(scale * 100)}% 页码: {pageNumber} / {numPages}
{/* PDF渲染 */} setNumPages(numPages)} >
); } ``` ### 5.2 渲染所有页面 ```typescript function PdfAllPages({ url }: { url: string }) { const [numPages, setNumPages] = useState(null); return ( setNumPages(numPages)} > {numPages && Array.from({ length: numPages }, (_, i) => (

第 {i + 1} 页

))}
); } ``` ### 5.3 自定义高亮功能 ```typescript function PdfWithHighlight({ url }: { url: string }) { const [highlights, setHighlights] = useState([]); const handleTextSelection = () => { const selection = window.getSelection(); if (!selection || selection.isCollapsed) return; const range = selection.getRangeAt(0); const rects = Array.from(range.getClientRects()); // 计算高亮区域相对位置 const pageElement = range.startContainer.parentElement ?.closest('[data-page-number]'); if (!pageElement) return; const pageRect = pageElement.getBoundingClientRect(); const pageNumber = parseInt( pageElement.getAttribute('data-page-number') || '1' ); const newHighlight = { pageNumber, rects: rects.map(rect => ({ left: rect.left - pageRect.left, top: rect.top - pageRect.top, width: rect.width, height: rect.height })), text: selection.toString() }; setHighlights(prev => [...prev, newHighlight]); }; return (
{/* 渲染高亮层 */} {highlights.map((hl, idx) => (
{hl.rects.map((rect: any, i: number) => (
))}
))}
); } ``` ### 5.4 响应式PDF查看器 ```typescript import { useRef, useEffect, useState } from 'react'; function ResponsivePdfViewer({ url }: { url: string }) { const containerRef = useRef(null); const [width, setWidth] = useState(0); useEffect(() => { const updateWidth = () => { if (containerRef.current) { setWidth(containerRef.current.offsetWidth); } }; updateWidth(); window.addEventListener('resize', updateWidth); return () => window.removeEventListener('resize', updateWidth); }, []); return (
); } ``` --- ## 6. 性能优化 ### 6.1 延迟加载 (Lazy Loading) ```typescript import { lazy, Suspense } from 'react'; const Document = lazy(() => import('react-pdf').then(m => ({ default: m.Document }))); const Page = lazy(() => import('react-pdf').then(m => ({ default: m.Page }))); function LazyPdfViewer() { return ( 加载PDF组件...
}> ); } ``` ### 6.2 虚拟滚动 对于大文档(>100页),使用虚拟滚动只渲染可见页面: ```typescript import { FixedSizeList } from 'react-window'; function VirtualPdfViewer({ url, numPages }: any) { const Row = ({ index, style }: any) => (
); return ( {Row} ); } ``` ### 6.3 缓存策略 ```typescript // 使用缓存避免重复加载 const pdfCache = new Map(); async function loadPdfWithCache(url: string) { if (pdfCache.has(url)) { return pdfCache.get(url); } const response = await fetch(url); const blob = await response.blob(); pdfCache.set(url, blob); return blob; } ``` ### 6.4 优化建议 | 优化项 | 建议 | 影响 | |-------|------|------| | **缩放范围** | 限制在 0.5-3.0 | 性能 +++ | **按需渲染** | 只渲染可见页面 | 性能 +++++ | | **Worker** | 必须启用 | 性能 +++++ | | **文本层** | 按需启用 | 性能 +++ | | **SVG模式** | 仅在需要时使用 | 性能 ++ | | **devicePixelRatio** | 限制最大值为2 | 性能 +++ | --- ## 7. 常见问题 ### 7.1 Worker 相关 **Q: 为什么出现 "Cannot read property 'getDocument' of undefined" 错误?** A: 没有正确配置 Worker 路径。 ```typescript // ❌ 错误 import { Document, Page } from 'react-pdf'; // ✅ 正确 import { Document, Page, pdfjs } from 'react-pdf'; pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.js'; ``` **Q: Worker 文件从哪里获取?** A: 1. 从 `node_modules/pdfjs-dist/build/pdf.worker.js` 复制到 `public/` 目录 2. 或使用 CDN: `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js` ### 7.2 渲染问题 **Q: PDF 显示模糊怎么办?** A: 1. 提高 `scale` 值 2. 设置 `devicePixelRatio={window.devicePixelRatio}` 3. 使用 SVG 渲染模式 **Q: PDF 加载很慢怎么办?** A: 1. 压缩 PDF 文件 2. 使用虚拟滚动 3. 按需加载页面 4. 启用 Worker(默认启用) ### 7.3 文本选择问题 **Q: 文本层启用了但无法选择文本?** A: **最常见原因是没有导入 CSS 文件!** ```typescript // ✅ 正确:导入CSS import 'react-pdf/dist/Page/TextLayer.css'; import 'react-pdf/dist/Page/AnnotationLayer.css'; ``` 其他可能的原因: 1. 确认 `renderTextLayer={true}` 2. 检查 CSS 是否覆盖了文本层 (`pointer-events: none`) 3. 检查文本层是否被其他元素遮挡(`z-index`) 4. 确保容器允许文本选择(`user-select: text`) **Q: 文本层位置不准确?** A: 文本层坐标受缩放影响,需要在高亮时调整: ```typescript const adjustedRect = { left: rect.left / scale, top: rect.top / scale, width: rect.width / scale, height: rect.height / scale }; ``` ### 7.4 样式问题 **Q: 如何自定义 PDF 页面样式?** A: ```css /* 自定义页面边框 */ .react-pdf__Page { border: 1px solid #ccc; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } /* 自定义Canvas样式 */ .react-pdf__Page__canvas { max-width: 100%; height: auto !important; } /* 自定义文本层样式 */ .react-pdf__Page__textContent { /* 调试时显示文本层 */ opacity: 0.2; } ``` ### 7.5 CORS 问题 **Q: 加载外部 PDF 出现 CORS 错误?** A: 1. 使用代理服务器 2. 配置服务器 CORS 头 3. 使用 `fetch` 先下载再传递 Blob ```typescript async function loadPdfWithCors(url: string) { const response = await fetch(url, { mode: 'cors' }); const blob = await response.blob(); return blob; } ``` ### 7.6 TypeScript 类型问题 **Q: TypeScript 类型定义缺失?** A: ```bash npm install --save-dev @types/react-pdf ``` 或手动声明: ```typescript declare module 'react-pdf' { export const Document: any; export const Page: any; export const pdfjs: any; } ``` --- ## 8. 总结 ### 8.1 核心优势 1. ✅ **功能完善** - 支持缩放、旋转、文本选择等核心功能 2. ✅ **性能优异** - Worker 异步渲染,不阻塞UI 3. ✅ **灵活配置** - Canvas/SVG 双渲染模式 4. ✅ **易于集成** - React组件化,API简洁 5. ✅ **活跃维护** - 基于 PDF.js,持续更新 ### 8.2 适用场景 - ✅ 文档预览系统 - ✅ 在线阅读器 - ✅ 文档审核系统 - ✅ 电子签名应用 - ✅ 在线表单填写 ### 8.3 不适用场景 - ❌ 生成PDF(使用 @react-pdf/renderer) - ❌ 编辑PDF内容(使用 PDF.js API或其他库) - ❌ PDF合并/拆分(使用 pdf-lib) --- ## 9. 参考资源 - 📖 [官方文档](https://github.com/wojtekmaj/react-pdf) - 📖 [PDF.js 文档](https://mozilla.github.io/pdf.js/) - 📦 [NPM Package](https://www.npmjs.com/package/react-pdf) - 🎮 [在线Demo](http://projects.wojtekmaj.pl/react-pdf/) - 💬 [GitHub Issues](https://github.com/wojtekmaj/react-pdf/issues) --- **文档版本**: v1.0 **最后更新**: 2025-11-22 **维护者**: Claude Code 🤖