各位观众老爷,晚上好!我是今晚的讲师,很高兴能跟大家聊聊 Nx
的 Monorepo
架构,以及它在代码共享和任务执行图中的应用。别担心,今天咱们不搞那些晦涩难懂的理论,力求用最接地气的方式,把 Nx
的精髓给扒个精光。
首先,咱们先来热热身,聊聊啥是 Monorepo
。
啥是 Monorepo?别被吓着,其实很简单!
Monorepo
,顾名思义,就是把多个项目(可以是库、应用等等)的代码都放在同一个代码仓库里。这跟传统的 Multirepo
(每个项目一个仓库)是相对的。
想象一下:你家有个大花园(Monorepo
),里面种了各种各样的花草树木(不同的项目)。你可以很方便地管理它们,修剪枝叶,施肥浇水,一览无余。而 Multirepo
就像你家有好几个小盆栽(每个项目一个仓库),你需要分别照顾,比较麻烦。
Monorepo 有啥好处?
- 代码共享更方便: 不同的项目可以轻松共享代码,避免重复造轮子。
- 依赖管理更简单: 所有项目都在一个地方,版本冲突更容易发现和解决。
- 重构更容易: 修改一个底层库,所有依赖它的项目都能立即更新。
- 协作更高效: 开发者可以更容易地了解整个项目的结构和依赖关系。
当然,Monorepo
也有缺点,比如仓库体积可能比较大,构建时间可能比较长。但这些问题都可以通过工具来解决,比如我们今天要讲的 Nx
。
Nx:Monorepo 的瑞士军刀
Nx
是一个强大的 Monorepo
构建工具,它可以帮助你更轻松地管理和构建 Monorepo
项目。它提供了很多开箱即用的功能,比如:
- 代码生成器: 快速创建项目和组件,提高开发效率。
- 任务执行图: 智能分析项目之间的依赖关系,并行构建和测试。
- 缓存: 缓存构建结果,避免重复构建,加速构建过程。
- 代码影响分析: 只构建和测试受影响的项目,提高构建效率。
简单来说,Nx
就像是 Monorepo
的瑞士军刀,帮你解决各种问题。
Nx 的 Monorepo 架构
Nx
的 Monorepo
架构主要包含以下几个核心概念:
- Workspace: 代表整个
Monorepo
的根目录,包含所有项目和配置文件。 - Projects: 指的是
Workspace
中的各个项目,可以是库、应用、工具等等。 - Libraries: 可共享的代码模块,可以被多个应用或其他库依赖。
- Applications: 可独立运行的应用程序,例如前端应用、后端服务等等。
- Tasks: 需要执行的操作,例如构建、测试、部署等等。
- Task Pipeline: 定义了任务之间的依赖关系,决定了任务的执行顺序。
- Affected Commands: 只执行受代码更改影响的任务,提高构建效率。
代码共享:Nx 如何让代码“串门”?
Nx
提供了多种方式来共享代码:
-
共享库 (Shareable Libraries): 这是最常见的代码共享方式。你可以创建一个
Library
,然后让其他项目依赖它。# 创建一个名为 "util" 的库 nx generate @nx/js:library util
这个命令会在
libs/util
目录下创建一个新的库。你可以在这个库里编写一些通用的工具函数,然后在其他项目中使用。// libs/util/src/lib/util.ts export function greet(name: string): string { return `Hello, ${name}!`; }
然后,在你的应用或者其他库中,你可以这样使用这个函数:
// apps/my-app/src/app/app.component.ts import { greet } from '@my-org/util'; // 注意这里的别名 @my-org/util export class AppComponent { title = greet('World'); }
Nx
会自动处理依赖关系,确保你的应用可以正确地找到并使用这个库。 -
路径映射 (Path Mapping):
Nx
使用tsconfig.base.json
文件来配置路径映射。这意味着你可以为你的库定义一个别名,然后在其他项目中使用这个别名来引用它。 这样,即使你移动了库的位置,也不需要修改所有引用它的代码。打开
tsconfig.base.json
文件,你会看到类似这样的配置:{ "compilerOptions": { "paths": { "@my-org/util": ["libs/util/src/index.ts"] } } }
这个配置告诉
TypeScript
编译器,当你使用@my-org/util
时,实际上是指向libs/util/src/index.ts
文件。 -
工具函数 (Utility Functions):
Nx
鼓励你将一些通用的工具函数放在一个单独的库里,然后在其他项目中使用。 这样可以避免代码重复,提高代码的可维护性。例如,你可以创建一个名为 "api-client" 的库,用于封装 API 请求。
nx generate @nx/js:library api-client
然后,在
libs/api-client/src/lib/api-client.ts
文件中,你可以编写一些通用的 API 请求函数:// libs/api-client/src/lib/api-client.ts import axios from 'axios'; export async function get(url: string): Promise<any> { try { const response = await axios.get(url); return response.data; } catch (error) { console.error(`Error fetching data from ${url}:`, error); throw error; } }
然后,在你的应用或者其他库中,你可以这样使用这个函数:
// apps/my-app/src/app/app.component.ts import { get } from '@my-org/api-client'; export class AppComponent { async ngOnInit() { const data = await get('/api/users'); console.log(data); } }
任务执行图:Nx 如何让任务“排队”?
Nx
的任务执行图 (Task Execution Graph) 是一个非常有用的功能。它可以分析项目之间的依赖关系,然后智能地安排任务的执行顺序。 这意味着 Nx
可以并行构建和测试你的项目,从而大大提高构建效率。
Nx
是怎么知道项目之间的依赖关系的呢? 主要通过以下几种方式:
-
package.json
文件:Nx
会分析package.json
文件中的dependencies
和devDependencies
字段,来确定项目之间的依赖关系。 -
tsconfig.json
文件:Nx
会分析tsconfig.json
文件中的paths
字段,来确定项目之间的依赖关系。 -
nx.json
文件:nx.json
文件是Nx
的配置文件。你可以在这个文件中定义任务之间的依赖关系。// nx.json { "tasksRunnerOptions": { "default": { "runner": "nx/tasks-runners/default", "options": { "cacheableOperations": ["build", "test", "lint", "e2e"], "dependsOn": [ "{workspaceRoot}/node_modules/.bin/nx affected --target=build --files={files}", "{workspaceRoot}/node_modules/.bin/nx affected --target=test --files={files}" ] } } }, "affected": { "defaultBase": "main" }, "targetDependencies": { "build": [ { "target": "lint", "projects": "dependencies" } ] } }
在这个例子中,
targetDependencies
字段定义了build
任务依赖于lint
任务。这意味着在执行build
任务之前,Nx
会先执行lint
任务。projects: "dependencies"
表示只对build
任务所依赖的项目执行lint
任务。 -
代码分析:
Nx
还可以通过分析代码来确定项目之间的依赖关系。 例如,如果一个项目导入了另一个项目的模块,Nx
就会认为这两个项目之间存在依赖关系。
有了这些依赖关系,Nx
就可以构建任务执行图了。 你可以使用 nx graph
命令来可视化任务执行图。
nx graph
这个命令会打开一个网页,显示你的 Monorepo
中所有项目和任务的依赖关系。
Affected Commands:只打“有鬼”的地方
Affected Commands
是 Nx
的另一个非常有用的功能。 它可以只执行受代码更改影响的任务。 这意味着如果你只修改了一个库的代码,Nx
就只会构建和测试这个库,以及依赖于这个库的项目。 这样可以大大提高构建效率。
你可以使用 nx affected
命令来执行受影响的任务。
# 构建受影响的项目
nx affected:build
# 测试受影响的项目
nx affected:test
# 运行 lint 受影响的项目
nx affected:lint
Nx
是怎么知道哪些项目受到代码更改的影响的呢? 主要通过以下几种方式:
-
Git:
Nx
会使用Git
来比较当前分支和基准分支之间的差异。 默认情况下,基准分支是main
分支。 你可以使用--base
参数来指定基准分支。nx affected:build --base=develop
这个命令会比较当前分支和
develop
分支之间的差异,然后构建受影响的项目。 -
文件列表: 你可以使用
--files
参数来指定一个文件列表。Nx
会根据这些文件来确定受影响的项目。nx affected:build --files=libs/util/src/lib/util.ts
这个命令会构建
libs/util
库,以及依赖于这个库的项目。 -
环境变量:
Nx
还可以从环境变量中读取文件列表。 这在 CI/CD 环境中非常有用。例如,你可以设置一个名为
NX_AFFECTED_FILES
的环境变量,然后在nx affected
命令中使用它。NX_AFFECTED_FILES=libs/util/src/lib/util.ts nx affected:build
一个简单的例子:
假设我们有一个 Monorepo
,包含以下项目:
libs/ui
: 一个 UI 组件库。libs/data
: 一个数据访问库。apps/admin
: 一个后台管理应用,依赖于libs/ui
和libs/data
。apps/store
: 一个电商应用,依赖于libs/ui
。
如果我修改了 libs/ui
的代码,那么 Nx
会自动构建 libs/ui
,apps/admin
和 apps/store
。 如果我修改了 libs/data
的代码,那么 Nx
会自动构建 libs/data
和 apps/admin
。 这就是 Affected Commands
的威力。
Nx 的一些高级用法:
- 自定义任务: 你可以定义自己的任务,并在
nx.json
文件中配置它们的依赖关系。 - 插件:
Nx
提供了很多插件,可以帮助你更轻松地管理你的Monorepo
。 例如,@nx/react
插件可以帮助你创建React
应用和库。 - 代码生成器: 你可以使用代码生成器来快速创建项目和组件。
总结:
Nx
是一个强大的 Monorepo
构建工具,它可以帮助你更轻松地管理和构建 Monorepo
项目。 它提供了很多开箱即用的功能,比如代码生成器、任务执行图、缓存和代码影响分析。 通过使用 Nx
,你可以提高开发效率,减少构建时间,并改善代码质量。
希望今天的讲座能对你有所帮助。 如果你还有其他问题,欢迎随时提问。 谢谢大家!
为了方便大家理解,我把今天讲的内容整理成了一个表格:
功能 | 描述 | 优点 | 缺点 |
---|---|---|---|
Monorepo | 将多个项目放在同一个代码仓库中。 | 代码共享方便、依赖管理简单、重构容易、协作高效。 | 仓库体积可能比较大、构建时间可能比较长。 |
Nx | 一个强大的 Monorepo 构建工具。 | 提供了很多开箱即用的功能,比如代码生成器、任务执行图、缓存和代码影响分析。 | 学习曲线稍陡峭。 |
代码共享 | 不同的项目可以轻松共享代码,避免重复造轮子。 | 提高代码复用率、减少代码冗余、降低维护成本。 | 需要仔细规划代码结构,避免循环依赖。 |
任务执行图 | 智能分析项目之间的依赖关系,并行构建和测试。 | 提高构建效率、减少构建时间。 | 需要正确配置任务之间的依赖关系,否则可能导致构建失败。 |
Affected Commands | 只执行受代码更改影响的任务,提高构建效率。 | 大幅提高构建效率、减少构建时间。 | 需要正确配置代码影响分析规则,否则可能导致漏构建或误构建。 |
共享库 | 可共享的代码模块,可以被多个应用或其他库依赖。 | 方便代码复用,减少代码冗余。 | 需要维护库的版本,避免版本冲突。 |
路径映射 | 为你的库定义一个别名,然后在其他项目中使用这个别名来引用它。 | 方便重构,即使你移动了库的位置,也不需要修改所有引用它的代码。 | 需要维护 tsconfig.base.json 文件,确保路径映射正确。 |
工具函数 | 将一些通用的工具函数放在一个单独的库里,然后在其他项目中使用。 | 避免代码重复,提高代码的可维护性。 | 需要仔细选择哪些函数应该放在工具库里。 |
nx graph |
可视化任务执行图。 | 方便理解项目之间的依赖关系,优化构建流程。 | 需要安装 nx 命令行工具。 |
nx affected |
执行受影响的任务。 | 提高构建效率、减少构建时间。 | 需要正确配置代码影响分析规则,否则可能导致漏构建或误构建。 |
希望这个表格能帮助大家更好地理解 Nx
的 Monorepo
架构。 再次感谢大家的参与!