构建企业级 Angular 项目结构
建立现代前端项目的一项重要任务是定义可扩展、长期且面向未来的文件夹结构,以及每个不同编程实体的命名指南。
虽然有些人认为这是一个简单且次要的方面,但它往往隐藏着比表面更复杂的问题。尽管大多数情况下没有完美的解决方案,但我们可以探索一些行业最佳实践,以及一些根据我的经验最有意义的方法。
在本文中,我们将讨论:
- 定义构成我们项目的每个堆栈级别的实体
- 将我们的 Angular 和 Typescript 实体分布在文件夹中
- 状态管理作为服务模块的集合
Angular 实体
在设置新的代码库时,我通常做的第一件事就是思考并定义构成我的堆栈的编程实体。作为 Angular 开发人员,我们已经非常了解其中一些:
- 模块、组件、指令、服务、管道和守卫
正如框架文档所建议的,每次我们创建这些实体中的每一个时,我们都会在文件名后加上实体的名称。
因此 - 如果我们创建一个名为HighlightPipe 的管道,我们将其文件命名为highlights.pipe.ts,如果我们有一个名为DropdownComponent的组件,我们希望将其文件命名为dropdown.component.ts、dropdown.component.html和dropdown.component.scss。
功能模块
如果不先讨论 Angular 模块,我们就无法讨论 Angular 项目的结构。
由于 Angular 应用由可以导入其他模块的模块组成,因此它们自然而然地成为构成 Angular 项目的根文件夹。每个模块都将包含其各自文件夹中包含的所有其他 Angular 实体。
假设我们正在构建一个电子商务应用程序,并且我们创建了一个购物车功能模块,它的结构可能如下所示:
💡 你可能注意到了,我倾向于区分容器(智能)和组件(愚蠢),所以我把它们放在不同的文件夹中,但这并不是我提倡的
功能模块不应该导出除顶级组件之外的任何东西,因此我们在其中定义的任何东西都不会在其他地方使用。
共享模块
但是,如果需要在其他地方重复使用某些东西怎么办?
在这种情况下,我们创建一个共享模块SharedModule,它将托管提供给项目每个模块的所有共享实体。
共享模块通常由项目内不同模块共享的实体组成,但在项目外部通常不需要。当我们遇到可以在不同团队和项目之间重用,并且理想情况下不会经常更改的服务或组件时,我们可能需要构建一个Angular 库。
库、Monorepos 和微前端
当您使用高度可重用的服务或组件(可归类为服务模块和小部件模块)时,您可能希望将这些模块构建为Angular 库,这些库可以在自己的存储库中创建,也可以在更大的monorepo中创建 。
借助强大的 CLI,我们可以使用这个简单的命令轻松生成将在名为projects的文件夹中构建的 Angular 库:
ng generate library my-lib
有关 Angular 库的完整描述,请参阅Angular.io 上的官方文档。
与本地模块相比,使用库具有以下几个优点:
- 我们在思考和构建这些模块时考虑到了可重用性
- 我们可以轻松地发布这些库并与其他团队/项目共享
但也有一些缺点:
- 你需要将你的库链接到你的主项目,并为每个更改重建它
- 如果这是通过 NPM 分发的,并且在主项目之外构建,则需要保持项目与库的最新版本同步
示例:假设BigCompany使用所有团队都使用的消息传递系统 - 我们可能希望共享我们的抽象,以避免许多库本质上做通常的基础工作。
因此我们创建了一个名为messages 的库,并将其作为@big-company/messaging发布到 NPM 。
但是monorepos和microfrontends怎么样?
这可能需要一篇更长的文章,但如果不提及另外两种方式,我们就无法谈论企业级项目:
- 单一仓库是一种经过验证的策略,适用于大型(甚至巨型)代码库,所有代码都可以重用,本质上所有代码库都位于同一个仓库中。所有项目都将始终使用最新版本的代码。
- 微前端允许将大型应用程序拆分成更小的应用,这些应用拥有各自的代码库,甚至可以使用完全不同的技术栈进行组合。例如,你的登录页面可以用Vue编写,而应用程序的其余部分可以用Angular和React编写。值得一提的是,Nx Workspaces还允许使用不同的技术栈,例如React
💡 你可能想看看Nx Workspace
将 Angular 项目构建为包含更多项目和库的monorepo是一个有吸引力的解决方案,但对于大型科技公司来说,实际上很难实现,因为其中许多团队和项目都是独立且彼此相距甚远的。
那么图书馆应该建在哪里呢?
- 如果一家公司的所有开发人员都致力于同一个主要项目,无论项目规模有多大,monorepo 可能是一个很好的解决方案
- 如果开发人员被安排在不同的项目、不同的团队、不同的地点,更重要的是不同的代码库中工作,那么你可能希望在他们自己的存储库中构建每个库
Typescript 实体
如果您将 Angular 与 Typescript 结合使用 — — 我假设您是这样,那么您还必须考虑 Typescript 自身的强大实体,我们可以利用它们来创建结构化、编写良好的代码库。
以下是您在项目中最常使用的 Typescript 实体列表:
- 课程
- 枚举
- 接口(和类型)
我喜欢将这些实体分组到模块内的自己的文件夹中,我勉强称之为核心,但这完全取决于您和您的团队的决定。
我建议为每个后端实体创建一个匹配的 Typescript 文件。这包括枚举、DTO(用于请求和响应)以及数据类。
例如,有时我们需要开发公司内多个团队共享的微服务。在类似情况下,我认为构建一个 Angular 库来托管相应的类、接口和枚举,比在本地开发模块更有意义。
状态管理
无论你计划使用哪个状态管理库,我建议将业务逻辑与领域模块分离。我们可以利用服务模块模式,并将其导入到相应的功能模块中。
状态管理服务模块只需要导出两件事:
- 模块本身,以注册其提供者
- 一个外观服务,充当功能模块的 UI 组件和商店之间的桥梁
这种模式有什么优点?
- 如果我们从惰性加载的路由导入模块,那么只有当路由加载时才会导入。有时,你可能需要在一个特定的路由中导入多个功能模块,在这种情况下,你可能不得不从AppModule中导入它们。
- 更好地与 UI 分离/封装。组件不需要知道你正在使用什么状态管理
- 我们可以重构/改变状态管理
我喜欢将状态与功能模块分开,这是一种特别流行的做法,但仍然导致 Angular 社区相当分裂:
- 假设我们在根级别 有一个名为Dashboard的路由模块,其中包含其所有 UI 组件
- 同样在根级别——我们有一个名为store的文件夹,其中包含处理状态的所有状态服务模块
NGRX 编程实体
NGRX 有哪些编程实体?
- 减速器
- 行动
- 选择器
- 效果(来自@ngrx/effects)
- 适配器(来自@ngrx/entity)
让我们看下面图片中使用 NGRX 的简单示例,我将在另一篇文章中详细解释。
- 仪表板模块导入仪表板存储模块
- 仪表板模块内的组件将仅通过服务DashboardFacadeService与商店通信
💡 如果我们为每个文件创建一个测试,最好将它们放在单独的文件夹中
要点⭐
- 无论使用哪种堆栈,在设置新项目时首先要考虑的事情之一就是考虑要使用的编程实体
- 一些高度可重用的模块可能应该位于主应用程序之外:利用 Angular 库💪
- 考虑通过创建状态管理服务模块将功能模块与其状态分离
如果您需要任何澄清,或者您认为某些内容不清楚或错误,请发表评论!
希望你喜欢这篇文章!如果喜欢,请在Medium、Twitter或我的网站上关注我,获取更多关于软件开发、前端、RxJS、Typescript 等的文章!
鏂囩珷鏉ユ簮锛�https://dev.to/gc_psk/building-an-enterprise-grade-angular-project-struct-3ihk