Skip to content

🎯 目标:搭建一个 Monorepo

示例项目

我们将创建这样一个结构:

  1. packages/ui: 一个共享组件库(提供一个按钮)。
  2. apps/web: 一个 React 网站(使用上面的按钮)。
  3. turbo: 负责一键启动所有项目。

🛠️ 第一步:初始化地基 (Root)

找个空文件夹,打开终端。

  1. 初始化项目

    bash
    mkdir my-monorepo && cd my-monorepo
    npm init -y

    修改 package.json

json
{
  "name": "my-monorepo",
  "version": "1.0.0",
  // 👇 建议:加上 private: true,防止意外把整个根目录发布到 npm 仓库
  "private": true,
  "scripts": {
    "dev": "turbo dev"
  },
  "devDependencies": {
    "turbo": "^2.x.x"
  },
  //Turbo (尤其是 v2.0 版本后) 变得非常严格。它为了防止“在我的电脑上能跑,在你的电脑上跑不起来”这种问题,强制要求你在 
  //package.json 里显式声明你用的是什么包管理器(npm, yarn, pnpm 还是 bun),以及具体的版本号。
  // 👇 查到当前pnpm的版本号填进去 pnpm -v
  "packageManager": "pnpm@9.1.0"
}
  1. 安装 pnpm (如果你还没有安装):

    bash
    npm install -g pnpm
  2. 配置工作区 (核心): 在根目录新建文件 pnpm-workspace.yaml。这行代码告诉 pnpm:“appspackages 里的文件夹都是我的人,把它们连起来。” YAML

    yaml
    packages:
      - 'apps/*'
      - 'packages/*'

📦 第二步:创建共享 UI 库 (packages/ui)

我们要造一个“轮子”。

  1. 创建目录

    bash
    mkdir -p packages/ui
    cd packages/ui
    npm init -y
  2. 修改 packages/ui/package.json: 把 name 改个有组织的名字,比如 @repo/ui。这是我们以后引用的名字。

    json
    {
      "name": "@repo/ui", 
      "version": "1.0.0",
      "main": "./index.tsx",  
      "dependencies": {
        "react": "^18.0.0"
      },
      "devDependencies": {
        "@types/react": "^19.2.9"
      }
    }

    (注:为了演示方便,我们直接指向 tsx 源码,让消费端的 Vite 去编译它。生产环境通常会先编译成 JS。)

  3. 创建组件代码: 新建 packages/ui/index.tsxTypeScript

    tsx
    import React from 'react';
    
    export const Button = () => {
      return (
        <button style={{
          backgroundColor: 'blue',
          color: 'white',
          padding: '10px 20px',
          borderRadius: '5px',
          border: 'none',
          cursor: 'pointer'
        }}>
          我是共享组件按钮 🚀
        </button>
      );
    };

🚀 第三步:创建应用 (apps/web)

现在我们要创建一个网站来使用这个按钮。回到根目录。

  1. 回到根目录并创建 Vite 项目

    bash
    # 使用 pnpm 创建 vite 项目,放入 apps/web 目录
    pnpm create vite apps/web --template react-ts
  2. 关键步骤:建立依赖连接! 我们要在 apps/web 里安装刚才写的 @repo/ui。 在根目录下运行:

    bash
    # --workspace 参数告诉 pnpm:别去 npm 仓库找,去本地文件夹找!
    pnpm add @repo/ui --filter web --workspace

    你去看看 apps/web/package.json,会发现多了一行:"@repo/ui": "workspace:*"。这就是 Monorepo 的魔法链接。

我们把它拆成三部分看:
1. **`pnpm add @repo/ui`**:

   * **动作**:我要安装 `@repo/ui` 这个包。
2. **`--filter web`**:

   * **地点(Target)**:但我不想安装在根目录,我要安装到 **名字叫 `web` 的那个包**(即 `apps/web/package.json`)里。
3. **`--workspace`**:

   * **版本(Protocol)**:**关键点!** 这告诉 pnpm:“不要去 npm 仓库找版本号,直接用我本地 Monorepo 里的版本,并标记为 `workspace:*`”。
4. **在应用中使用组件**: 打开 **`apps/web/src/App.tsx`**,修改代码:
   **TypeScript**
  1. 直接引入我们的包!
tsx
   import { Button } from '@repo/ui'

   function App() {
     return (
       <div style={{ padding: 50 }}>
         <h1>Web 应用</h1>
         <p>下面这个按钮来自共享库:</p>
         <Button /> 
       </div>
     )
   }

   export default App

⚡️ 第四步:引入 Turborepo (提速)

现在我们有了两个项目。如果没有 Turbo,你需要分别去两个目录跑命令。有了 Turbo,我们在根目录指挥一切。

  1. 安装 Turbo (在根目录):

    bash
    pnpm add turbo -D -w

    (-w 表示安装在根目录 workspace root)

  2. 配置 Turbo: 在根目录新建 turbo.json。这是任务调度指挥中心。

    Turbo v2 刚刚发布不久,它做了一个重大的破坏性更新 (Breaking Change):为了让配置语义更清晰,它把原来的 "pipeline" 字段重命名为了 "tasks"。

    json
    {
      "$schema": "https://turbo.build/schema.json",
      //它把原来的 "pipeline" 字段重命名为了 "tasks"。
      "tasks": {
        "dev": {
          "cache": false,
          "persistent": true
        },
        "build": {
          "outputs": ["dist/**"]
        }
      }
    }
  3. 配置根目录启动命令: 修改根目录的 package.json,添加 scripts

    bash
    "scripts": {
      "dev": "turbo dev"
    }

✅ 验证时刻

现在,见证奇迹的时刻到了。

  1. 根目录运行:

    bash
    pnpm dev
  2. 观察终端: 你会看到 Turbo 启动了!它会同时启动 apps/webdev 命令(如果 ui 库有 dev 命令也会启动)。

  3. 打开浏览器。 你应该能看到一个蓝色的按钮 “我是共享组件按钮 🚀”

🧠 深度复盘

你刚才做的这几个步骤,其实模拟了大型前端团队的协作流:

  1. Workspace Protocol (workspace:*):
    • 你在 ui 文件夹里改了按钮颜色为 red,保存。
    • 你会发现 web 页面瞬间变红了。
    • 不需要npm publish不需要npm install。这就是 Monorepo 开发效率提升的核心。
  2. Turborepo:
    • 刚才只有一个 app,你可能感觉不明显。但如果你有 10 个 app,turbo dev 会并行启动它们。
    • 如果你运行 turbo build,第二次运行时会发现耗时接近 0ms,因为它读取了缓存(Cache)。

.gitignore

# 1. 依赖包 (自动覆盖根目录和所有子目录)
node_modules/
.pnpm-store/

# 2. 构建产物
dist/
build/
coverage/

# 3. Turborepo 缓存 (非常重要,不要提交缓存)
.turbo/

# 4. 环境变量 (包含敏感信息,绝对不能提交)
.env
.env.*.local

# 5. 系统文件 & 编辑器配置
.DS_Store
.vscode/
.idea/
*.log

# 6. (强烈不推荐忽略pnpm-lock.yaml❌)