monorepo 是一种组织管理代码的方法,从字面上来理解就是把与一个项目关的 package、module、project等都放在一个仓库中进行管理。与之相对应的管理方式是 multirepo ,把与项目相关的业务拆分成不同的 module 或者 package 放在不同的仓库中,彼此之间独立维护。
随着一个项目的不断迭代更新,我们会根据公司复杂的业务将部分复用代码或者常用组件拆分成 module 或者 library。是将这些众多的独立的 module 放在不同的 repository 中,然后由划分了不同职责的小组来进行独立维护,还是将这些 module 放入一个 repository 中由所有人一起维护。这就就需要考虑到 monorepo 和 multirepo 各自的优缺点。在我接触公司业务之初,尝试使用过 mutirepo,当时我们使用私有的 npm 管理多个独立的业务模块,这样既保证了主项目代码的整洁程度,也在项目初期大幅度降低了代码耦合度。可是随之而来的问题是,每次独立模块的更改,都需要单独的打包、发布、编写 changelog。在人手不多的初期,这种做法增加了我们的维护成本。通过资料查阅,我知道了还有 monorepo 这种管理方式,并且随之了解到了 Nx 这款适配 Angular 框架的扩展工具,我们把所有 module 和 libaray 放在同一个 repository 下,这样我们可以使用同一套标准管理代码,独立模块中的修改能够立马展现,代码结构也能得到改善。
官网:nx.dev
Nx 是一个用来构建 monorepos 的开发工具。自己从 Angular 6.X 版本的时候开始尝试使用 Nx 管理项目代码,那时候的 Nx 还只支持对 Angular 框架的扩展,现在的 Nx 添加了对 React 框架的支持,并且可以集成使用 Cypress, Jest
, Prettier
, TypeScript 等现在构建工具,支持 NestJs
(一款 nodejs 后端框架),完成了对整个全栈生态的覆盖。非常推荐大家上手尝试一下。
本篇文章通过 Nx 创建一个 fullstack 项目,以最简单的 todo 为例,分别有两个 application,两个 library,通过这个简单的例子展示一下 Nx 强大的工作流,以及如何管理 monorepo,如何集成最新的构建工具的能力。
创建一个空的 Nx workSpace 项目非常简单,你可以使用以下命令安装
npx create-nx-workspace@latest todoappnpm init nx-workspace todoappyarn create nx-workspace todoapp如果你已经拥有了一个自己的 Angular 项目,也可以使用 ng add @nrwl/workspace 命令将原有项目,改造成 monorepos 。脚本会在保留项目原有代码的基础上,修改文件夹结构。一个通过 Nx 创建的 Angular monorepo 空项目文件结构如下图所示:
简单介绍一下几个特殊文件/文件夹:
添加前端项目,通过以下命令创建:
ng generate @nrwl/angular:application frontend
这条命令会在 apps 目录下生成 fronted 和 frontend-e2e(后续用来编写 e2e 测试) 两个项目,生成的项目和 Angular Cli 生成出来的没有任何区别
添加后端项目,通过以下命令创建:
ng generate @nrwl/nest:application backend --frontendProject=frontend
这条命令会在 apps 目录下生成 backend 项目,后端项目我选择了使用 Nest,Nx 本身也对 Nest 有着很好的支持,语法更符合 Angular 程序员
接下来我们简单编写一下项目逻辑,首先是后端服务器
apps/backend/src/app/app.service.ts
import { Injectable } from '@nestjs/common';
import { Todo } from '@todoapp/data';
@Injectable()
export class AppService {
/** 原始数据 */
todos: Todo[] = [{ title: 'Todo1' }, { title: 'Todo2' }];
/** 增加数据 */
addTodo(): void {
this.todos.push({
title: `Random ${ Math.floor(Math.random() * 100) }`
});
}
}
apps/backend/src/app/app.controller.ts
import { Controller, Get, Post } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
/** get 方法,用来拉取初始数据 */
@Get('todos')
getData() {
return this.appService.todos;
}
/** post 方法,用来增加数据 */
@Post('add')
add() {
return this.appService.addTodo();
}
}
编写前端
apps/frontend/src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Todo } from '@todoapp/data';
@Component({
selector: 'todoapp-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
todos: Todo[];
constructor(private http: HttpClient) {
this.fetch();
}
/** 请求数据 */
fetch(): void {
this.http.get('/api/todos')
.subscribe((t: any) => {
this.todos = t;
});
}
/** 增加数据 */
addTodo(): void {
this.http.post('/api/add', {})
.subscribe(() => {
this.fetch();
});
}
}
apps/frontend/src/app/app.component.html
<h1>Todo2</h1>
<todoapp-task-list [todos]="todos"></todoapp-task-list>
<button id="add-todo" (click)="addTodo()">Add Todo</button>
创建一个 data library,用来存放 frontend 和 backend 中共用的数据结构 ToDo
ng generate @nrwl/workspace:library data
libs/data/src/index.ts
export interface Todo { title: string };
创建一个 ui library,用来存放 frontend 中抽象的组件逻辑,此处我们创建一个 task-list 组件,用来显示 todo-list
ng generate @nrwl/angular:library ui
ng generate @schematics/angular:component task-list --project=ui --export
最后我们的 libs 文件夹中会是这样的结构:
这样我们的公用逻辑都被拆分到了 libs 文件夹中,可以由不同小组成员去负责和维护,降低了代码耦合度。使用这些 lib 也很简单,例如 import { Todo } from '@todoapp/data' import { UiModule } from '@todoapp/ui'
如果只是以上这些功能,可能会觉得比较没意思,只是满足了基本的需求,现代的前端工具应该更多的是智能酷炫,方便团队之间复杂的协作。接下来我展示一些实用的功能。
我们不希望后端团队导入前端团队编写的 UiModule 。我们可以借用 tslint 实现,nx.json 可以给不同 app 和 lib 打上特殊的 tag,tslint 会在 import 语句中检查是否在当前项目中导入了不属于自己的模块。
在 nx.json 中,我们给 UiModule 打上 "platform:frontend" 的标签,backend 打上 "platform:backend" 标签
在 tslint.json 中增加 "nx-enforce-module-boundaries" 规则,并且规定 "platform:backend" 标签下只能导入标签为 "platform:backend" 的模块。
配置完毕,重启编辑器,我们能够顺利看到 backend 项目中 module 导入 UiModule 会提示错误。
当项目越来越庞大时,我们可能需要查看不同项目之间的依赖关系,Nx 提供数据可视化的方案,可以以下命令查看:
nx affected:dep-graph
ng run frontend-e2e:e2e --watch
强大的功能还有很多,不一一介绍了,可以上官网查阅
Nx 可以用来以 monorepos 的方式构建全栈项目,比较像是 Angular Cli 的扩展,所有 Angular Cli 能做的它都能做,Nx 对 Angular 项目的兼容度很高,熟悉 Cli 命令的人能够快速上手。如果想要尝试以更加工程化的方式管理业务代码,推荐大家尝试
武汉格发信息技术有限公司,格发许可优化管理系统可以帮你评估贵公司软件许可的真实需求,再低成本合规性管理软件许可,帮助贵司提高软件投资回报率,为软件采购、使用提供科学决策依据。支持的软件有: CAD,CAE,PDM,PLM,Catia,Ugnx, AutoCAD, Pro/E, Solidworks ,Hyperworks, Protel,CAXA,OpenWorks LandMark,MATLAB,Enovia,Winchill,TeamCenter,MathCAD,Ansys, Abaqus,ls-dyna, Fluent, MSC,Bentley,License,UG,ug,catia,Dassault Systèmes,AutoDesk,Altair,autocad,PTC,SolidWorks,Ansys,Siemens PLM Software,Paradigm,Mathworks,Borland,AVEVA,ESRI,hP,Solibri,Progman,Leica,Cadence,IBM,SIMULIA,Citrix,Sybase,Schlumberger,MSC Products...