如何让Adobe PDF Embed API在NextJS中正常运行(可尝试禁用SSR)
问题
我基于Create React App开发的React应用中,某页面通过Adobe PDF Embed API渲染PDF文件完全正常,但迁移到Next.js后,页面其他组件均能正常工作,唯独PDF渲染器失效——仅生成空的pdf-div容器,API应填充的PDF内容未显示。我需要让这个带完整功能的PDF查看器正常运行,曾推测是服务端渲染(SSR)问题,尝试过用高阶组件和Next.js动态导入禁用SSR,但未解决。
相关代码(/pages/notes/[id].tsx)
import { Flex, Heading, HStack, Link, Stack, Text, useColorMode, VStack } from "@chakra-ui/react"; import ReactGA from "react-ga4"; import { useEffect } from "react"; import { toast } from "react-hot-toast"; import { useDispatch, useSelector } from "react-redux"; import Loader from "../../components/Layout/Loader"; import ViewSDKClient from "../../others/ViewSDKClient"; import { getNotes } from "../../redux/actions/subject"; import { useRouter } from "next/router"; const Notes = () => { const dispatch = useDispatch(); const { colorMode } = useColorMode(); const router = useRouter(); const { id } = router.query; const { notes, error, loading } = useSelector((state: any) => state.subjects); document.title = notes ? (notes.title + " - CollegeNotes") : "Loading - CollegeNotes"; useEffect(() => { ReactGA.send({ hitType: "pageview", page: document.title + window.location.pathname }); }, [notes]) useEffect(() => { dispatch(getNotes(id) as any); }, [dispatch, id]); useEffect(() => { if (error) { toast.error(error); dispatch({ type: "clearError" }); } }, [error, dispatch]); const loadPDF = () => { const viewSDKClient = new ViewSDKClient(); viewSDKClient.ready().then(() => { viewSDKClient.previewFile( "pdf-div", { embedMode: window.innerWidth > 800 ? "SIZED_CONTAINER" : "FULL_WINDOW", showAnnotationTools: false, showPageControls: true, showDownloadPDF: false, showPrintPDF: false, }, notes.document.url, notes.title ); }); }; return ( <> {loading || !notes ? ( <Loader /> ) : ( <Flex minH={"100vh"} width={"100%"} direction={"row"} justifyContent={"space-around"} > <Stack height={"100%"} marginX={"3"} marginY={["8", "10", "12", "12", "14"]} width={"container.md"} > <Heading size={["xs", "sm"]} fontFamily={"Roboto Condensed"} children={notes.subject} textTransform={"uppercase"} paddingBottom={"2px"} borderBottom={"3px solid"} display={"inline"} width={"fit-content"} /> <Heading as={"h1"} paddingTop={["1", "2"]} children={notes.title} fontFamily={"Source Serif Pro"} fontWeight={"extrabold"} size={["2xl", "3xl"]} /> <HStack fontSize={"13px"}> <Text fontFamily={"Inter"} children={"Contributed by: "} /> <Link target={"_blank"} fontFamily={"Inter"} color={ colorMode === "light" ? "teal.600" : "teal.300"} href={notes.contributorSocial} > {notes.contributor} </Link> </HStack> <Text fontSize={"md"} fontFamily={"Lusitana"} paddingTop={["4", "7"]} paddingBottom={["1", "3"]} children={notes.description} /> <VStack width={"100%"}> <Flex id="pdf-div" height={"98vh"} onLoad={loadPDF()!} /> </VStack> </Stack> </Flex> )} </> ); }; export default Notes;
解决方案
1. 修复onLoad的调用错误
当前代码中onLoad={loadPDF()!}是直接执行函数而非传递引用,会导致组件渲染时就调用loadPDF,此时DOM元素可能未挂载、notes数据也可能未就绪。应移除onLoad属性,改用useEffect控制加载时机。
2. 确保PDF加载逻辑仅在客户端执行
Adobe PDF Embed API依赖浏览器环境,将加载逻辑放在依赖notes的useEffect中,并添加客户端环境与DOM元素存在性判断:
useEffect(() => { if (!notes?.document?.url) return; if (typeof window === 'undefined') return; const viewSDKClient = new ViewSDKClient(); viewSDKClient.ready().then(() => { const pdfDiv = document.getElementById("pdf-div"); if (!pdfDiv) return; viewSDKClient.previewFile( "pdf-div", { embedMode: window.innerWidth > 800 ? "SIZED_CONTAINER" : "FULL_WINDOW", showAnnotationTools: false, showPageControls: true, showDownloadPDF: false, showPrintPDF: false, }, notes.document.url, notes.title ); }); }, [notes]);
3. 动态导入ViewSDKClient并禁用SSR
如果ViewSDKClient内部直接访问window对象,SSR时会报错,用Next.js的dynamic导入禁用SSR:
import dynamic from 'next/dynamic'; const ViewSDKClient = dynamic(() => import('../../others/ViewSDKClient'), { ssr: false, loading: () => <div>加载PDF查看器...</div> });
替换原有的直接import语句。
4. 修正页面标题设置方式
Next.js中直接修改document.title可能在SSR阶段出错,改用next/head组件设置标题:
import Head from 'next/head'; // 在组件return的最外层添加 <Head> <title>{notes ? `${notes.title} - CollegeNotes` : "Loading - CollegeNotes"}</title> </Head>
删除原代码中document.title = ...这一行。
内容的提问来源于stack exchange,提问作者Swapnil Sachan
相关产品推荐
相关产品推荐

