You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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 禁止发布到npm
  • main/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加载

六、日常开发/构建流程

  1. 安装依赖:根目录执行pnpm install,pnpm自动安装所有子包依赖并关联内部模块
  2. 前端开发:执行pnpm dev:web,Vite自动热更,内部模块修改实时生效
  3. 编译libs:执行pnpm build:libs,批量编译所有*lib模块
  4. 构建服务/前端:执行对应build命令即可

内容的提问来源于stack exchange,提问作者Bülent Özden

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 18:00:42