基于 Nx 的 Monorepo

176 阅读4分钟

什么是 Monorepo

Monorepo(单一代码库)是一种将多个项目代码存储在一个仓库里的软件开发策略。相较于每个项目一个仓库的 MultiRepo 策略,Monorepo 更适合管理多个相互依赖的项目。这种策略常用于大型组织和开源项目,例如 Google、Facebook、Babel、Vue3 等都采用了 Monorepo 方案。

优劣势

优势:

  • 易于代码复用和依赖管理
  • 统一代码风格和规范
  • 便于代码重构
  • 促进开放、透明、共享的组织文化

劣势:

  • 权限管理复杂
  • 额外的学习成本
  • 需要强大的工具链和自动构建工具的支持

基于 Nx 的 Monorepo 方案

初始化 Nx 工作区

首先,你需要安装 Nx 工具并初始化一个新的工作区。可以使用以下命令:

npx create-nx-workspace@latest my-workspace

选择适合的预设(例如 empty 预设)并提供工作区名称(如 my-workspace)。

创建应用和库

在 Nx 中,可以创建多个应用和库。应用是可独立运行的项目,而库是可以在多个项目之间共享的模块。以下是创建应用和库的示例:

nx g @nrwl/react:app my-app
nx g @nrwl/react:lib my-lib

组织工作区结构

在 Monorepo 中,组织代码结构非常重要。通常会有以下目录结构:

my-workspace/
  apps/
    my-app/
      src/
      .eslintrc.json
      ...
  libs/
    my-lib/
      src/
      .eslintrc.json
      ...
  tools/
  package.json
  tsconfig.base.json
  .eslintrc.base.json
  commitlint.config.js
  ...
  • apps/ 目录包含所有应用。
  • libs/ 目录包含所有共享库。
  • tools/ 目录用于放置自定义工具和脚本。

配置 ESLint

为了统一代码风格和规范,可以在工作区根目录中创建一个共享的 ESLint 配置文件 .eslintrc.base.json

{
  "root": true,
  "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "rules": {
    "semi": ["error", "always"],
    "quotes": ["error", "double"]
  }
}

然后,在每个子项目的 ESLint 配置文件中引用这个共享配置。例如,在 my-appmy-lib 中:

{
  "extends": "../../.eslintrc.base.json",
  "parserOptions": {
    "project": ["./tsconfig.json"]
  },
  "rules": {
    // 可以为每个子项目添加特定的规则
  }
}

统一配置 TypeScript 和 Babel

同样的,可以在工作区根目录中创建共享的 TypeScript 和 Babel 配置文件:

  • tsconfig.base.json
  • .babelrc

在各个子项目中引用这些共享配置:

// 在子项目的 tsconfig.json 中
{
  "extends": "../../tsconfig.base.json",
  ...
}

构建和测试

使用 Nx 提供的命令进行构建和测试:

nx build my-app
nx test my-lib

Nx 支持并行构建和增量构建,这意味着只会构建发生变化的部分,从而提高效率。

管理依赖

Nx 能自动化管理项目之间的依赖关系。可以使用以下命令来查看受影响的项目并进行构建:

nx affected:build

该命令将检测哪些项目受到了代码变更的影响,并且只构建这些项目,从而节省时间。

利用 Lerna 统一包管理

Lerna 可以帮助解决多个子项目的版本控制和依赖管理问题。例如,可以在工作区根目录中初始化 Lerna:

npx lerna init

Lerna 提供了许多有用的命令,例如:

  • lerna bootstrap:安装所有依赖包并创建符号链接。
  • lerna run:在所有子项目中运行 npm script 脚本。
  • lerna publish:发布代码变动的包。

设置 commitlint

为了规范化提交信息,可以使用 commitlint。首先,安装 commitlint 及其依赖:

npm install --save-dev @commitlint/{config-conventional,cli}

在项目根目录创建 commitlint.config.js 文件:

module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [2, 'always', [
      'build',
      'chore',
      'ci',
      'docs',
      'feat',
      'fix',
      'perf',
      'refactor',
      'revert',
      'style',
      'test'
    ]],
    'type-case': [2, 'always', 'lower-case'],
    'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']]
  }
};

配置 Husky

接下来,使用 Husky 设置 Git hooks,以便在每次提交代码时自动运行 commitlint。首先,初始化 Husky:

npx husky install

然后,在项目根目录下创建一个 husky 文件夹,创建一个 commit-msg 钩子脚本:

npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

示例 commit 信息

commitlint 强制约束提交信息的格式,例如以下提交信息:

feat(auth): add login functionality
  • feat 是提交类型,表示这是一个新特性。
  • (auth) 是可选的范围,表示这是与认证相关的更改。
  • add login functionality 是提交的描述。

验证提交信息

可以在命令行中手动验证提交信息是否符合规范:

echo "feat(auth): add login functionality" | npx commitlint

综合示例

以下是一个完整的项目目录结构示例,包括 commitlint 和 Husky 的配置:

my-workspace/
  .husky/
    commit-msg
  apps/
    my-app/
      src/
      .eslintrc.json
      ...
  libs/
    my-lib/
      src/
      .eslintrc.json
      ...
  tools/
  package.json
  tsconfig.base.json
  .eslintrc.base.json
  commitlint.config.js
  ...

package.json 文件中配置 Husky 钩子:

{
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
      "pre-commit": "eslint . --ext .ts,.tsx,.js,.jsx"
    }
  }
}

希望这个综合的说明对你有帮助!这样你就可以在 Monorepo 项目中既使用 commitlint 来规范提交信息,又使用 ESLint 来确保代码质量。如果你有任何具体的问题或需要进一步的说明,随时告诉我。