如何用tsup构建含React组件与Express路由的共享库?
解决方案:通过tsup配置分离前后端代码,解决Node.js模块解析错误
问题根源在于你将依赖Node.js API的Express路由与React组件打包在同一模块中,Next.js在浏览器环境加载时会尝试解析所有依赖(包括async_hooks、fs等Node内置模块),而这些模块在浏览器中不存在,因此报错。以下是通过tsup实现需求的具体方案:
1. 拆分前后端专用入口(最可靠方案)
将组件库拆分为前端纯组件入口和后端组件+路由入口,让Next.js仅导入不含Node代码的前端入口。
修改tsup配置
import { defineConfig } from "tsup"; export default defineConfig({ entry: { // 前端专用入口:仅导出React组件,不包含任何路由/Node相关代码 "frontend/index": "src/frontend-exports.ts", // 后端专用入口:导出组件+路由的组合结构 "backend/index": "src/backend-exports.ts", // 保留原有入口(按需保留) index: "src/index.ts", "apps/index": "src/applications/index.ts", "surprises/index": "src/surprises/index.ts", }, format: ["esm"], splitting: true, // 将Node相关依赖标记为外部,让后端项目自行安装处理 external: ["express", "mongodb", "async_hooks", "fs", "dns"], // 针对不同入口做差异化配置 esbuildOptions: (options, context) => { if (context.entryNames.includes("frontend")) { // 适配浏览器环境,忽略Node内置模块解析 options.mainFields = ["browser", "module", "main"]; options.define = { ...options.define, "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV), }; } }, // 生成类型声明 dts: true, });
编写拆分后的入口文件
- 前端入口文件
src/frontend-exports.ts:仅导出React组件
// 只暴露组件,不涉及任何路由/Node逻辑 export { AppComponent } from "./applications/index"; export { SurpriseComponent } from "./surprises/index";
- 后端入口文件
src/backend-exports.ts:导出组件+路由的组合
import { Router } from "express"; import { FC } from "react"; import { AppComponent } from "./applications/index"; import { appRouter } from "./routes/app-router"; // 你的Express路由文件 export interface IExportedMember { component: FC; router: Router; } export const appExport: IExportedMember = { component: AppComponent, router: appRouter, }; // 同理导出其他组件+路由组合
在项目中导入
- Next.js前端项目导入纯组件:
import { AppComponent } from "@your-component-lib/frontend/index";
- Express后端项目导入组合结构:
import { appExport } from "@your-component-lib/backend/index";
2. 配合package.json条件导出优化导入体验
在组件库的package.json中配置条件导出,让不同环境自动匹配对应入口:
"exports": { ".": { "node": "./dist/backend/index.js", "browser": "./dist/frontend/index.js", "types": "./dist/index.d.ts" }, "./frontend": "./dist/frontend/index.js", "./backend": "./dist/backend/index.js" }
配置后,Next.js会自动加载browser入口,无需手动指定路径。
3. 备选方案:Next.js侧临时兼容(不推荐)
如果暂时无法修改组件库打包逻辑,可以在Next.js的next.config.js中配置忽略Node内置模块解析,但可能引发其他潜在问题:
module.exports = { webpack: (config) => { config.resolve.fallback = { ...config.resolve.fallback, async_hooks: false, fs: false, dns: false, // 其他报错的Node内置模块 }; return config; }, };
内容的提问来源于stack exchange,提问作者Panhaboth K
相关产品推荐
相关产品推荐

