使用TypeScript的Monorepo:代码组织实战

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

使用TypeScript的Monorepo:代码组织实战

\n\n

在现代Web开发中,尤其是在使用TypeScript时,开发者经常面临一个两难选择:将所有代码存储在一个仓库(monorepo)中,还是将其分割成多个小型仓库(多仓库)。对于中大型项目,当代码复用和统一配置至关重要时,monorepo已成为事实上的标准。在本文中,我们将探讨如何正确组织基于TypeScript的monorepo,从基本结构到高级技术。

\n\n

什么是Monorepo,为什么需要它?

\n\n

Monorepo是一种将多个项目(包、库、应用)的代码存储在一个仓库中的方法。在TypeScript的上下文中,这尤其方便,因为它允许:

\n\n
    \n
  • 重用类型和接口,无需发布到npm即可在系统的不同部分之间共享。
  • \n
  • 简化重构:一个包中的更改会自动在所有依赖包中验证。
  • 拥有统一的配置,用于linter、格式化程序和构建工具。
  • 简化CI/CD:一个仓库对应一个流水线。
\n\n

在TypeScript中处理monorepo的流行工具包括Nx、Turborepo和npm/yarn/pnpm workspaces。我们将采用pnpm workspaces和TypeScript项目引用的方法,因为这是一套最小且灵活的方案。

\n\n

Monorepo结构:基本配置

\n\n

让我们为TypeScript项目创建一个典型的monorepo结构。假设我们有三个包:core(通用类型和工具)、server(后端)和web(前端)。

\n\n

1. 根配置

\n\n

从项目根目录开始。创建一个包含workspace的package.json文件:

\n\n
{\n  "name": "my-monorepo",\n  "private": true,\n  "workspaces": [\n    "packages/*"\n  ],\n  "scripts": {\n    "build": "tsc -b",\n    "clean": "tsc -b --clean",\n    "lint": "eslint packages/*/src"\n  }\n}\n
\n\n

然后配置根tsconfig.json。使用项目引用——这是TypeScript针对monorepo的关键特性:

\n\n
{\n  "compilerOptions": {\n    "target": "ES2020",\n    "module": "commonjs",\n    "strict": true,\n    "composite": true,\n    "declaration": true,\n    "declarationMap": true,\n    "sourceMap": true,\n    "outDir": "./dist",\n    "rootDir": ".",\n    "skipLibCheck": true\n  },\n  "references": [\n    { "path": "./packages/core" },\n    { "path": "./packages/server" },\n    { "path": "./packages/web" }\n  ]\n}\n
\n\n

重要提示:对于其他包引用的包,composite: true选项是必需的。它启用增量构建并生成.d.ts文件。

\n\n

2. 配置core包

\n\n

进入packages/core目录。创建package.json:

\n\n
{\n  "name": "@my/core",\n  "version": "1.0.0",\n  "main": "dist/index.js",\n  "types": "dist/index.d.ts",\n  "scripts": {\n    "build": "tsc -b"\n  }\n}\n
\n\n

以及该包的tsconfig.json:

\n\n
{\n  "extends": "../../tsconfig.json",\n  "compilerOptions": {\n    "outDir": "./dist",\n    "rootDir": "./src"\n  },\n  "include": ["src"]\n}\n
\n\n

创建一个简单的模块src/index.ts:

\n\n
export interface User {\n  id: string;\n  name: string;\n  email: string;\n}\n\nexport function greet(user: User): string {\n  return `Hello, ${user.name}!`;\n}\n
\n\n

现在包

博客

书籍推荐