NextJS在getStaticProps中引入fs模块提示Can't resolve 'fs'报错
报错原因
Module not found: Can't resolve 'fs' 是服务端代码被误打包到客户端侧bundle导致的典型错误:
fs、path是Node.js原生模块,仅能在服务端环境运行,浏览器环境不存在对应API。- 你把这两个模块的导入语句写在了文件顶层,Next.js构建客户端侧JS资源时,会扫描所有顶层导入的依赖并尝试打包,自然找不到只能在Node环境运行的
fs模块。 - 哪怕你只在
getStaticProps里使用这些模块,只要是顶层导入,客户端打包逻辑就会尝试解析该依赖,和getStaticProps本身运行在服务端的特性不冲突。
修复方案
核心原则是所有仅能在服务端运行的代码、Node原生模块导入,都不要放在文件顶层,具体修改点如下:
- 将
fs、path这类Node原生模块的导入移到getStaticProps内部,用动态导入的方式加载,Next.js会自动将getStaticProps内的代码从客户端bundle中剔除,不会再被客户端打包逻辑扫描到。 - 不需要额外配置
serverRuntimeConfig获取项目根目录,getStaticProps运行时的工作目录就是项目根目录,直接用process.cwd()即可拿到正确路径,减少多余配置。 - 补充空值兜底逻辑,避免组件首次渲染时props未注入导致的取值报错。
- 修正Markdown渲染逻辑:
marked.parse返回的是HTML字符串,直接放在JSX中会被转义为纯文本,需要用dangerouslySetInnerHTML才能正常渲染解析后的DOM内容。
修复后的完整代码如下:
import marked from 'marked'; import { useRouter } from 'next/router'; export async function getStaticProps() { // 服务端专用模块放在getStaticProps内部动态导入,不会进入客户端打包逻辑 const fs = (await import('fs')).default; const path = (await import('path')).default; const projectRoot = process.cwd(); function getLegalDocument(key: string, locale: string) { const filePath = path.join(projectRoot, './legal', `${key}.${locale}.md`); const fileContent = fs.readFileSync(filePath, 'utf8'); return marked.parse(fileContent); } const legalDocuments = { legalNotice: { de: getLegalDocument('legal_notice', 'de'), en: getLegalDocument('legal_notice', 'en'), es: getLegalDocument('legal_notice', 'es'), fr: getLegalDocument('legal_notice', 'fr'), it: getLegalDocument('legal_notice', 'it'), }, privacyPolicy: { de: getLegalDocument('privacy_policy', 'de'), en: getLegalDocument('privacy_policy', 'en'), es: getLegalDocument('privacy_policy', 'es'), fr: getLegalDocument('privacy_policy', 'fr'), it: getLegalDocument('privacy_policy', 'it'), }, termsOfService: { de: getLegalDocument('terms_of_service', 'de'), en: getLegalDocument('terms_of_service', 'en'), es: getLegalDocument('terms_of_service', 'es'), fr: getLegalDocument('terms_of_service', 'fr'), it: getLegalDocument('terms_of_service', 'it'), } } return { props: { legalDocuments }, } }; type Props = { legalDocuments?: Record<string, Record<string, string>>; documentType: string; } const LegalDocument = ({ legalDocuments, documentType }: Props) => { if (!legalDocuments || !legalDocuments[documentType]) { return <div>{documentType}</div> } const languages = legalDocuments[documentType]; const { locale, defaultLocale } = useRouter(); const targetContent = languages[locale] || languages[defaultLocale]; if (!targetContent) { return <div>{documentType}</div> } return <div dangerouslySetInnerHTML={{ __html: targetContent }} /> } export default LegalDocument
额外说明
- 如果使用Next.js 11及以上版本,也可以在顶层导入
fs,但要确保导入的模块仅在getStaticProps/getServerSideProps这类服务端执行的函数中使用,动态导入的写法兼容性更好,覆盖所有Next.js版本。 - 不要在客户端组件的顶层导入任何Node原生模块,只要代码可能在浏览器环境运行,就会触发同类报错。
内容的提问来源于stack exchange,提问作者Florian
相关产品推荐
相关产品推荐

