React与Express共享代码的ES版本适配问题求助
解决Monorepo中共享包同时支持React(ESNext)和Express(CommonJS)的方案
核心思路是让共享包同时编译输出ES模块(ESM)和CommonJS(CJS)两种格式,让前端和后端各自读取对应格式的文件,彻底解决模块系统冲突问题。
1. 重构共享包的TypeScript配置
在shared目录下拆分出基础配置+两个环境专属配置,分别对应ESM和CJS:
shared/tsconfig.base.json(公共基础配置)
抽离所有环境通用的编译选项:
{ "compilerOptions": { "baseUrl": "../", "rootDir": "./src", "composite": true, "strict": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "declaration": true, "declarationDir": "./dist/types" }, "include": ["./src/**/*"], "exclude": ["node_modules", "./dist/**/*"] }
shared/tsconfig.esm.json(给React前端用)
继承基础配置,指定ES模块编译规则:
{ "extends": "./tsconfig.base.json", "compilerOptions": { "module": "ESNext", "target": "ESNext", "outDir": "./dist/esm", "moduleResolution": "NodeNext" } }
shared/tsconfig.cjs.json(给Express后端用)
继承基础配置,指定CommonJS编译规则:
{ "extends": "./tsconfig.base.json", "compilerOptions": { "module": "CommonJS", "target": "ES2016", "outDir": "./dist/cjs", "moduleResolution": "Node", "esModuleInterop": true } }
2. 修改共享包的package.json
配置多模块入口,让Node和打包工具自动匹配对应格式:
{ "name": "@your-project/shared", "version": "1.0.0", "main": "./dist/cjs/index.js", // CommonJS入口 "module": "./dist/esm/index.js", // ES模块入口 "types": "./dist/types/index.d.ts", // 统一类型定义入口 "scripts": { "build": "tsc -p tsconfig.esm.json && tsc -p tsconfig.cjs.json" // 同时编译两种格式 } }
3. 调整前端(React/Vite)的配置
更新frontend/tsconfig.json,指向共享包的ESM配置:
{ "compilerOptions": { // 保留原有配置 "target": "ESNext", "module": "ESNext", "moduleResolution": "NodeNext", "allowSyntheticDefaultImports": true, "esModuleInterop": false }, "include": ["src"], "references": [ { "path": "./tsconfig.node.json" }, { "path": "../shared/tsconfig.esm.json" } ] }
可选:在Vite配置中添加共享包优化,提升开发体验:
// frontend/vite.config.ts import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], optimizeDeps: { include: ['@your-project/shared'] } })
4. 调整后端(Express)的配置
更新backend/tsconfig.json,指向共享包的CJS配置:
{ "compilerOptions": { // 保留原有配置 "target": "es2016", "module": "commonjs", "moduleResolution": "node", "outDir": "./dist", "esModuleInterop": true }, "references": [ { "path": "../shared/tsconfig.cjs.json" } ] }
5. 验证解决方案
- 进入
shared目录执行npm run build,确认生成dist/cjs、dist/esm、dist/types三个目录 - 启动前端项目,验证共享包模块导入正常,无导出错误
- 启动后端项目,验证共享包模块导入正常,无
SyntaxError: Unexpected token 'export'错误
关键注意事项
- 共享包源码统一放在
shared/src目录,避免编译后文件路径混乱 - 开启
declaration: true保证前后端都能获取完整的TypeScript类型提示 - 前端
esModuleInterop设为false时,确保共享包导出符合ES模块规范(如export default或export const)
内容的提问来源于stack exchange,提问作者kidox
相关产品推荐
相关产品推荐

