为何自研npm包thomasop-form在React可用,Next.js中报ReferenceError错误?
解决
thomasop-form包在Next.js中ReferenceError: exports is not defined的问题 这个错误本质是模块格式不兼容:你的包目前应该是输出CommonJS格式,而Next.js的客户端组件环境(尤其是App Router)对CommonJS的支持有限,导致浏览器环境识别不了exports变量。下面是具体的修复方案:
一、调整npm包的核心配置(从根源解决)
1. 更新package.json的模块声明
打开你包根目录的package.json,添加/修改以下字段:
{ "type": "module", // 声明包使用ES模块 "main": "./dist/index.cjs", // CommonJS入口 "module": "./dist/index.js", // ES模块入口 "types": "./dist/index.d.ts", // 类型文件入口(如果用TS) "exports": { ".": { "import": "./dist/index.js", "require": "./dist/index.cjs" } } }
exports字段是Node.js推荐的模块入口配置,能让不同环境(ES模块/CommonJS)自动匹配对应的文件。
2. 调整打包工具配置(以Rollup为例)
如果用Rollup打包,修改rollup.config.js,同时输出ES和CommonJS两种格式:
import babel from '@rollup/plugin-babel'; import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import typescript from '@rollup/plugin-typescript'; export default { input: 'src/index.ts', // 你的源码入口 output: [ { file: 'dist/index.js', format: 'esm', // ES模块格式 sourcemap: true }, { file: 'dist/index.cjs', format: 'cjs', // CommonJS格式 sourcemap: true, exports: 'named' // 导出命名成员 } ], plugins: [ resolve(), commonjs(), typescript(), // 如果用TS,确保配置tsconfig.json babel({ exclude: 'node_modules/**', presets: ['@babel/preset-react', '@babel/preset-env'] }) ], external: ['react', 'react-dom'] // 排除React依赖,让用户自己提供 };
如果用Webpack,也要配置多输出格式,确保同时生成.js(ESM)和.cjs(CJS)文件。
3. 统一代码中的导出方式
检查你的组件代码,确保用ES模块的导出语法:
// 正确:ES模块导出 export const Container = () => { /* ... */ }; export const Form = () => { /* ... */ }; // 避免:CommonJS导出 // module.exports = { Container, Form };
二、Next.js项目中的临时兼容方案(无需改包)
如果暂时不想修改包的配置,可以在Next.js中用动态导入的方式加载你的包,禁用SSR:
'use client'; import { useState } from 'react'; import dynamic from 'next/dynamic'; // 动态导入,禁用服务端渲染 const { Container, Form, TextField } = dynamic( () => import('thomasop-form'), { ssr: false } ); export default function Page() { const [value, setValue] = useState(); return ( <> <Container> <Form method={"GET"} url={"https://jsonplaceholder.typicode.com/posts"} setValue={setValue} > <TextField name={"firstname"} required={true} regex={/^[0-9]$/} /> <TextField name={"lastname"} /> </Form> </Container> </> ); }
这种方式让组件只在浏览器端加载,避开服务端的CommonJS兼容性问题,但不是长久之计,还是建议从包本身调整配置。
三、验证修复效果
- 重新打包你的npm包:运行
npm run build(确保打包脚本指向你配置的打包工具) - 在本地测试:用
npm link把包链接到Next.js项目中,验证是否还会报错 - 发布新版本:修改
package.json的版本号后,运行npm publish
内容的提问来源于stack exchange,提问作者toto
相关产品推荐
相关产品推荐

