Next.js 14.2中使用react-pdf遇CommonJS模块导入错误求助
Next.js 中 react-pdf 的 CommonJS 模块兼容错误解决
问题描述
在 Next.js 项目中使用 @react-pdf/renderer 时触发模块加载错误,提示需将对 react-pdf.js 的 require 调用替换为动态 import()。错误栈显示问题出现在服务器端渲染阶段,涉及 CreateAppointmentPdf.tsx 组件对 @react-pdf/renderer 的直接导入。
错误信息
⨯ Instead change the require of react-pdf.js in C:\Users\TCET ADMIN\Desktop\next\.next\server\app\(client)\page.js to a dynamic import() which is available in all CommonJS modules. at @react-pdf/renderer (C:\Users\TCET ADMIN\Desktop\next\.next\server\app\(client)\page.js:110:18) at __webpack_require__ (C:\Users\TCET ADMIN\Desktop\next\.next\server\webpack-runtime.js:33:43) at eval (_components/CreateAppointmentPdf.tsx:7:77) at (ssr)/./app/(client)/_components/CreateAppointmentPdf.tsx (C:\Users\TCET ADMIN\Desktop\next\.next\server\app\(client)\page.js:419:1) at __webpack_require__ (C:\Users\TCET ADMIN\Desktop\next\.next\server\webpack-runtime.js:33:43) at eval (_components/downloadbutton.tsx:10:79) at (ssr)/./app/(client)/_components/downloadbutton.tsx (C:\Users\TCET ADMIN\Desktop\next\.next\server\app\(client)\page.js:463:1) at __webpack_require__ (C:\Users\TCET ADMIN\Desktop\next\.next\server\webpack-runtime.js:33:43) at eval (_components/create-schedule-dialog.tsx:38:74) at (ssr)/./app/(client)/_components/create-schedule-dialog.tsx (C:\Users\TCET ADMIN\Desktop\next\.next\server\app\(client)\page.js:452:1) at __webpack_require__ (C:\Users\TCET ADMIN\Desktop\next\.next\server\webpack-runtime.js:33:43) at eval (_components/left-calendar.tsx:10:81) at (ssr)/./app/(client)/_components/left-calendar.tsx (C:\Users\TCET ADMIN\Desktop\next\.next\server\app\(client)\page.js:496:1) at __webpack_require__ (C:\Users\TCET ADMIN\Desktop\next\.next\server\webpack-runtime.js:33:43) at eval (page.tsx:9:83) at (ssr)/./app/(client)/page.tsx (C:\Users\TCET ADMIN\Desktop\next\.next\server\app\(client)\page.js:540:1) at Object.__webpack_require__ [as require] (C:\Users\TCET ADMIN\Desktop\next\.next\server\webpack-runtime.js:33:43) at JSON.parse (<anonymous>) digest: "2904845551"
原组件代码
MainComponent.tsx
<DownloadButton disabledButton={!confirmAgreement} onClick={form.handleSubmit(onSubmit)} isPending={isPending} form={form} /> </DialogClose>
DownloadButton.tsx
'use client' import React from 'react' import CreateDocumentPDF from './CreateAppointmentPdf' import dynamic from 'next/dynamic' const DownloadButton = ({ form, onClick, disabledButton, isPending }: any) => { const [isClient, setIsClient] = useState(false) const PDFDownloadLink = dynamic( () => import("@react-pdf/renderer").then((mod) => mod.PDFDownloadLink), { ssr: false, loading: () => <p>Loading...</p>, } ); useEffect(() => { setIsClient(true) }, []) return ( <PDFDownloadLink fileName='Appointment' document={ <CreateDocumentPDF form={form} /> } > <Button> </Button> </PDFDownloadLink> ) } export default DownloadButton
CreateAppointmentPdf.tsx
import { Document, Font, Page, Text, View } from '@react-pdf/renderer' const CreateDocumentPDF = ({ form }: any) => { return ( <Document> </Document> ) } export default CreateDocumentPDF
解决方法
问题根源是服务器端渲染时加载了仅兼容浏览器环境的 @react-pdf/renderer 模块。需要确保所有涉及 react-pdf 的组件都仅在客户端加载:
1. 修改 DownloadButton.tsx
将 CreateDocumentPDF 也改为动态导入,并添加客户端渲染判断:
'use client' import React, { useState, useEffect } from 'react' import dynamic from 'next/dynamic' import Button from 'your-button-component-path' // 替换为实际的Button组件路径 // 动态导入PDF文档组件,关闭SSR const CreateDocumentPDF = dynamic(() => import('./CreateAppointmentPdf'), { ssr: false, loading: () => <p>加载中...</p>, }) // 动态导入PDF下载链接组件,关闭SSR const PDFDownloadLink = dynamic( () => import("@react-pdf/renderer").then((mod) => mod.PDFDownloadLink), { ssr: false, loading: () => <p>加载中...</p>, } ); const DownloadButton = ({ form, onClick, disabledButton, isPending }: any) => { const [isClient, setIsClient] = useState(false) useEffect(() => { setIsClient(true) }, []) // 服务器端渲染时显示占位按钮 if (!isClient) { return <Button disabled={disabledButton || isPending}>生成PDF</Button> } return ( <PDFDownloadLink fileName='Appointment' document={<CreateDocumentPDF form={form} />} > {({ loading }) => ( <Button disabled={disabledButton || isPending || loading} onClick={onClick} > {loading ? '生成中...' : '下载PDF'} </Button> )} </PDFDownloadLink> ) } export default DownloadButton
2. 修改 CreateAppointmentPdf.tsx
添加 'use client' 指令,明确标记为客户端组件:
'use client' import { Document, Page, Text, View } from '@react-pdf/renderer' const CreateDocumentPDF = ({ form }: any) => { return ( <Document> <Page size="A4"> <View style={{ padding: 20 }}> <Text style={{ fontSize: 18, fontWeight: 'bold' }}>预约详情</Text> {/* 根据form数据渲染内容,示例: */} <Text>姓名: {form?.getValues('name')}</Text> <Text>日期: {form?.getValues('date')}</Text> </View> </Page> </Document> ) } export default CreateDocumentPDF
原理说明
Next.js 的 SSR 会在服务器端解析所有同步导入的模块,而 @react-pdf/renderer 依赖浏览器环境的 API(如 canvas),且其模块结构在 CommonJS 下需要动态导入才能避免加载错误。通过将所有 react-pdf 相关组件设为动态导入并关闭 SSR,确保这些代码仅在客户端执行,彻底解决模块兼容问题。
内容的提问来源于stack exchange,提问作者Stykgwar
相关产品推荐
相关产品推荐

