我们将介绍如何使用 pdf.js 在 React 应用中构建一个简约的 PDF 查看器。就是一张一张图片拼起来的。
上代码
import { useEffect, useRef, useState } from 'react';import * as PDFJS from 'pdfjs-dist';import 'react-pdf/dist/esm/Page/AnnotationLayer.css';PDFJS.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.6.82/pdf.worker.min.mjs';interface Props { fileUrl: string;}const PDFViewer = ({ fileUrl }: Props) => { const [numPages, setNumPages] = useState<number>(0); // 页数状态 const pdfRefs = useRef<(HTMLCanvasElement | null)[]>([]); // 用于存储每一页的canvas引用 const observer = useRef<IntersectionObserver | null>(null); // IntersectionObserver引用 // 渲染指定页面 const renderPage = (page: any, canvas: HTMLCanvasElement) => { const viewport = page.getViewport({ scale: 1 }); if (canvas) { // 确保canvas有效 canvas.width = viewport.width; canvas.height = viewport.height; const pdfCtx = canvas.getContext('2d'); if (pdfCtx) { page.render({ canvasContext: pdfCtx, viewport: viewport, }); } } }; // 处理页面加载 const loadPage = (index: number) => { if (index < numPages) { PDFJS.getDocument(fileUrl).promise.then((pdfDoc) => { pdfDoc.getPage(index + 1).then((page) => { const canvas = pdfRefs.current[index]; if (canvas) { // 确保canvas不为null renderPage(page, canvas); // 渲染页面 } }); }); } }; useEffect(() => { // 获取PDF文档 PDFJS.getDocument(fileUrl).promise.then((pdfDoc) => { setNumPages(pdfDoc.numPages); // 设置页面数量 // 创建Intersection Observer observer.current = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const index = Number(entry.target.getAttribute('data-index')); loadPage(index); // 加载页面 observer.current?.unobserve(entry.target); // 加载后取消观察 } }); }); // 观察每一页 pdfRefs.current.forEach((canvas, index) => { if (canvas) { canvas.setAttribute('data-index', index.toString()); observer.current?.observe(canvas); // 使用可选链确保不是null } }); }); return () => { observer.current?.disconnect(); // 使用可选链确保不是null }; }, [fileUrl, numPages]); return ( <div style={{ display: 'flex', flexDirection: 'column', width: '100%', height: '100%', overflowY: 'auto', }} > {Array.from({ length: numPages }, (_, index) => ( <canvas key={index} ref={(el) => (pdfRefs.current[index] = el)} // 存储每一页的canvas引用 style={{ marginBottom: '20px', display: 'block' }} // 添加每页之间的间隔 ></canvas> ))} </div> );};export default PDFViewer;
主要还是注意这段代码,在使用之前先看看这个链接能不能访问。无法访问的话就重新去pdf.js
官网重新拿一个,标记了框框的那个是pdfjs-dist的版本号。
为了避免一次性渲染所有 PDF 页面,我引入了 Intersection Observer。当用户滚动浏览 PDF 文档时,只有当前视口中的页面才会被渲染,这大大减少了不必要的资源消耗。
我也是第一次写预览pdf这个需求,有啥不好的地方大家给我点建议