目录
- 一、引言:Lerna 一年,我踩过的坑
- 二、shadcn/ui 的双 components.json 设计拆解
- 三、workspace alias:跨包引用的核心机制
- 四、pnpm 排除模式:让依赖树更干净
- 五、Turbo 拓扑构建:按依赖关系编排任务
- 六、Lerna vs Turborepo:真实对比
- 七、踩坑经验五条
- 八、总结
一、引言:Lerna 一年,我踩过的坑
先交代一下背景:我在一家金融科技公司做 KMS 知识管理平台的前端开发,技术栈是 React 18 + TypeScript + Ant Design + MobX,代码仓库用 Lerna 管理 Monorepo 已经一年多。
起初选 Lerna,是因为它"开箱即用"——lerna init 一把梭,lerna bootstrap 自动 link 依赖,lerna run build 串行跑所有包的构建。对于一个只有三四个包的 Monorepo 来说,这些足够了。
但随着业务膨胀,问题接踵而至:
痛点一:构建速度越来越慢。 五个包变成十五个包后,每次 lerna run build 要等 3-5 分钟。更致命的是,Lerna 默认按字母顺序串行构建,哪怕包 A 和包 B 毫无依赖关系,也得一个一个来。
痛点二:依赖提升(hoisting)带来的幽灵依赖。 Lerna 默认把子包的依赖提升到根 node_modules,导致 package.json 里没声明 dayjs,代码里却可以 import dayjs from 'dayjs'——这在本地跑没问题,一上 CI 就炸。
痛点三:任务编排能力弱。 你没法声明"包 C 的 test 依赖包 A 的 build",只能手动 lerna run build --scope=@kms/shared && lerna run test --scope=@kms/web,脚本越写越长。
痛点四:增量构建缺失。 改了一行注释,整个仓库重跑一遍构建,缓存形同虚设。
这些都是 Monorepo 规模扩大后的典型困境。恰好,shadcn/ui 最近发布了它的 Monorepo 模板,其中用 Turborepo + pnpm workspace 实现了非常精巧的架构设计——特别是"双 components.json"的配置模式。本文就以 shadcn/ui 的项目布局为案例,手把手拆解大仓治理的核心思路。
二、shadcn/ui 的双 components.json 设计拆解
2.1 先看项目结构
shadcn/ui Monorepo 模板的项目结构大致如下:
my-turborepo/
├── apps/
│ └── web/ # Next.js 应用
│ ├── components.json # ← 第一个 components.json
│ ├── package.json # depends on @workspace/ui
│ ├── next.config.ts
│ └── src/
│ └── app/
│ └── page.tsx # import { Button } from '@workspace/ui'
├── packages/
│ └── ui/ # 共享 UI 组件包
│ ├── components.json # ← 第二个 components.json
│ ├── package.json # name: @workspace/ui
│ └── src/
│ ├── components/ # shadcn CLI 写入目标
│ │ └── ui/
│ │ └── button.tsx
│ ├── lib/
│ │ └── utils.ts # cn() 工具函数
│ └── styles/
│ └── globals.css # Tailwind CSS 全局样式
├── packages/
│ ├── eslint-config/
│ └── typescript-config/
├── pnpm-workspace.yaml
├── turbo.json
├── package.json # 根 package.json
└── sync-templates.sh # 模板同步脚本
2.2 两份 components.json 各司其职
shadcn/ui CLI 工具(npx shadcn@latest add button)在安装组件时,会读取当前目录下的 components.json 来决定:
- 组件写入哪个目录
utils导入路径怎么拼- Tailwind CSS 配置文件在哪
在这个 Monorepo 里,你有两份 components.json,每份的职责完全不同。
apps/web/components.json —— 应用的"消费视角":
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "../../packages/ui/tailwind.config.ts",
"css": "../../packages/ui/src/styles/globals.css",
"baseColor": "zinc",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@workspace/ui/lib/utils",
"ui": "@workspace/ui/components",
"lib": "@workspace/ui/lib",
"hooks": "@workspace/ui/hooks"


383

被折叠的 条评论
为什么被折叠?



