基于pnpm monorepo+TypeScript+Vite+Rollup的配置问题求助
解决方案:pnpm Monorepo 配置规范与问题修复
一、先统一项目结构
捋顺混乱的目录,采用标准pnpm workspace结构:
monorepo-root/ ├── package.json # 根依赖、workspace配置 ├── tsconfig.json # 全局TS配置、项目引用 ├── packages/ │ ├── commonlib/ # 通用工具库 │ │ ├── src/ │ │ ├── package.json │ │ └── tsconfig.json │ ├── serverlib/ # 后端通用库 │ │ ├── src/ │ │ ├── package.json │ │ └── tsconfig.json │ ├── weblib/ # 前端通用组件/工具库 │ │ ├── src/ │ │ ├── package.json │ │ └── vite.config.ts │ ├── service-auth/ # 后端微服务1 │ │ ├── src/ │ │ ├── package.json │ │ └── rollup.config.ts │ └── web-app/ # 前端应用 │ ├── src/ │ ├── package.json │ └── vite.config.ts
二、根package.json配置(统一依赖+workspace)
核心配置:
{ "name": "@myapp/monorepo", "private": true, "workspaces": ["packages/*"], "scripts": { "build:libs": "pnpm -r --filter \"@myapp/*lib\" build", "build:services": "pnpm -r --filter \"@myapp/service-*\" build", "build:web": "pnpm -r --filter \"@myapp/web-*\" build", "dev:web": "pnpm --filter @myapp/web-app dev" }, "dependencies": { // 所有子包共用的业务依赖,如lodash、axios等 }, "devDependencies": { "typescript": "^5.0.0", "pnpm": "^8.0.0", "vite": "^4.0.0", "rollup": "^3.0.0", "@rollup/plugin-typescript": "^11.0.0", "@rollup/plugin-node-resolve": "^15.0.0" // 其他共用开发依赖 } }
private: true防止整个monorepo被误发布workspaces指定子包路径,pnpm自动关联所有内部模块- 用
pnpm -r+--filter批量执行子包命令,避免重复配置
三、内部*lib模块的处理:要不要编译?怎么引入?
1. 是否需要编译?
- 前端weblib:开发阶段可直接引入TS源码(Vite天然支持TS),生产阶段可编译为ES模块,或让前端应用打包时直接嵌入源码
- 后端commonlib/serverlib:必须编译为CJS/ES双格式,Node环境默认不识别TS,且后端微服务打包需依赖编译后的JS文件
2. 每个*lib的package.json配置
以commonlib为例:
{ "name": "@myapp/commonlib", "private": true, "main": "./dist/cjs/index.js", "module": "./dist/es/index.js", "types": "./dist/types/index.d.ts", "scripts": { "build": "tsc -p tsconfig.json && tsc -p tsconfig.es.json" } }
private: true禁止发布到npmmain/module/types指定不同格式的输出文件和类型定义路径
3. TS配置(用Project References统一管理)
根tsconfig.json:
{ "compilerOptions": { "target": "ESNext", "module": "ESNext", "strict": true, "declaration": true, "declarationMap": true, "skipLibCheck": true, "esModuleInterop": true, "baseUrl": ".", "paths": { "@myapp/*": ["packages/*/src"] // 开发时直接指向源码,支持热更新 } }, "references": [ { "path": "./packages/commonlib" }, { "path": "./packages/serverlib" }, { "path": "./packages/weblib" } ] }
子包commonlib/tsconfig.json(CJS格式):
{ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "./dist/cjs", "module": "CommonJS" }, "include": ["src/**/*"], "references": [] }
子包commonlib/tsconfig.es.json(ES格式):
{ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./dist/es", "module": "ESNext" } }
- Project References让TS自动处理子包依赖顺序,避免编译报错
- 根
paths配置让所有子包可直接用@myapp/commonlib引入源码
四、Vite配置修复(解决前端空白问题)
问题根源:rollupOptions.external排除了所有包,包括内部模块和Vue/React等必要前端依赖。正确配置如下(以web-app/vite.config.ts为例):
import { defineConfig } from 'vite' import path from 'path' export default defineConfig({ resolve: { alias: { // 让Vite识别内部模块路径 '@myapp/commonlib': path.resolve(__dirname, '../commonlib/src'), '@myapp/weblib': path.resolve(__dirname, '../weblib/src') } }, build: { rollupOptions: { // 只排除node_modules中的第三方依赖,内部模块不排除 external: (id) => { return !id.startsWith('@myapp/') && !id.startsWith('.') && !id.startsWith('/') } // 也可手动指定排除的第三方依赖,如:external: ['vue', 'react-router-dom'] } } })
- 前端空白核心原因:把需要打包进应用的Vue/React等依赖排除,导致浏览器找不到对应代码
- 内部模块必须让Vite解析到源码或编译后的文件,不能排除
五、后端微服务Rollup配置
以service-auth/rollup.config.ts为例,支持CJS/ES双输出:
import { defineConfig } from 'rollup' import typescript from '@rollup/plugin-typescript' import resolve from '@rollup/plugin-node-resolve' export default defineConfig({ input: 'src/index.ts', output: [ { file: 'dist/cjs/index.js', format: 'cjs', sourcemap: true }, { file: 'dist/es/index.js', format: 'es', sourcemap: true } ], plugins: [ resolve(), typescript({ tsconfig: './tsconfig.json' }) ], external: (id) => { // 排除node_modules依赖和内部已编译的lib模块 return id.startsWith('@myapp/') || (!id.startsWith('.') && !id.startsWith('/')) } })
- 内部模块
@myapp/*设为external,因为已提前编译,无需重复打包进服务代码 - 第三方依赖同样排除,让Node运行时直接从node_modules加载
六、日常开发/构建流程
- 安装依赖:根目录执行
pnpm install,pnpm自动安装所有子包依赖并关联内部模块 - 前端开发:执行
pnpm dev:web,Vite自动热更,内部模块修改实时生效 - 编译libs:执行
pnpm build:libs,批量编译所有*lib模块 - 构建服务/前端:执行对应build命令即可
内容的提问来源于stack exchange,提问作者Bülent Özden
相关产品推荐
相关产品推荐

