勇哥

勇哥开发之路

I build things on web.

【翻译】现代前端应用程序架构

翻译自:Architectures of modern Front-end applications

在开发过程中,业务需求可能各不相同且经常变化,因此创建一个灵活、可扩展和可维护的架构非常重要。同样关键的是,团队成员和客户等每个人都要清楚地了解项目。为了避免大量文件、频繁开会和不断改进,我们采用了下面的架构方法。本文章将探讨流行架构的优势,并帮助您选择最佳解决方案来满足您的独特需求。

很多人认为下面列出的架构只是 "文件夹结构",这只是一种说法。事实上,如果深入研究,它们有几个重要方面:

  • 模块合作:应用程序中不同模块 / 组件之间的高效通信和互动(使用基于组件的架构、可重复使用的代码)。
  • 改进项目导航:以易于导航和维护的方式构建项目(清晰的文件夹层次结构、命名约定、关注点分离)。
  • 业务逻辑与用户界面组件分离:将业务逻辑(数据获取、状态管理)与用户界面组件分离,以提高可维护性和可重用性(例如,使用 Redux、Context API 等服务或存储)。
  • DRY: Don’t repeat yourself 不要重复
  • DAC: Divide and conquer 分而治之

请看下图 -- 我们的重点是右下角的区域,这里是这些原则最有效的结合点:

Ideal result

理想结果(示例)

我们希望借助架构来实现这些方面。现在,让我们对每一种架构进行详细分析,并根据具体情况选择最适合的架构。

经典架构(无架构)- Classic architecture (without architecture)#

经典架构是许多人已经在使用的一种方法。我们通常专注于基本概念,将项目划分为 “页面”、“组件”、“助手” 等。然而,问题在于随着应用程序的增长,结构开始瓦解,找到正确的组件或其业务逻辑变得更加困难。让我们举例说明:

Components overuse

组件过度使用(示例)

在这个例子中,我们有 3 个页面,显然没有被过度使用。但是,它们可能包含下面显示的所有组件。如果我们观察一下这些组件,就会发现真正的 "混乱"🤯:每个组件都会主动使用其他组件,从而在它们之间产生依赖关系。这使得它们难以扩展和重用。

下面是另一个使用 Redux 状态管理器的示例:

Scattered app logic — Redux state manager

分散的应用程序逻辑 - Redux 状态管理器(示例)

在我们的设置中,每个组件都由指定的 "还原器" 管理,该还原器负责处理其特定的逻辑。然而,有些组件逻辑被错误地放在了不正确的还原器中。出现这种情况的原因可能是开发人员没有注意到现有的文件,或者由于当时需要的逻辑数量有限而没有考虑创建一个新文件。因此,一些组件的逻辑现在分散在整个项目中,使其不够清晰,也更难维护。

下图展示了一个缺失的架构:

Destructive decoupling —diagram

破坏性解耦示意图(示例)

这种方法(或者更正确地说,缺乏架构)往往会造成难以跟踪依赖关系的混乱环境,从而导致混乱,难以为项目提供支持。不过,这种方法在一些特殊情况下可能还是有用的,例如:

  • 小型团队(1-2 名开发人员)
  • MVP 项目
  • 不是长期支助项目
  • 学习项目或模板

模块化结构 - Modular architecture#

模块化架构是一种将应用程序划分为若干层( pages 、 modules 、 components 、 UI 等)的方法,在这些层中,已经有独立的模块,它们有自己的逻辑和责任范围。

Modular architecture — layer structure

模块化架构 -- 层结构(示例)

在这个例子中,可以看到应用层的排列方向是一致的: pages → modules → components → ui (如果从另一侧看,则相反)。这意味着层数越高(例如 pages ),可以使用的下层层数就越少 -- 组件不能使用模块,但可以使用用户界面层的所有内容,而模块可以使用组件,但不能使用页面。而页面已经只能使用模块了。

Modular architecture — modul & public API

模块化架构 -- 模块和公共应用程序接口(示例)

如前所述,每个模块都有自己的职责范围。同样重要的是,每个模块都应该有自己的公共 API( index.ts 文件),它封装了模块的所有内部逻辑,只提供外部需要的部分。(这与 OOP 原理非常相似:当一个类有很多私有方法时,这些方法不能从外部访问,但可以在类本身内部使用)说到 pages 文件,其实很简单:理想情况下,它应该只是模块和组件的封装,应用程序的所有业务逻辑都应该放在模块和组件的层面上。

(⚠️) 重要:一个模块不应使用另一个模块,一个组件不应包含复杂的逻辑。如果仍然需要逻辑,则应尽可能简单和易于维护,否则 -- 它就是一个模块!

请看下图:

Almost Ideal— diagram

近似理想图(示例)

但是我们仍然有 components/ 和 ui/ 这样的全局目录,它们可能会被过度使用。在某些情况下,逻辑可能会增长,不再总是很清楚什么是组件,什么是模块。此外,我们还经常发现,随着应用程序的增长,开发人员开始在其他模块中使用模块,这就破坏了该架构的原则,并造成不必要的依赖。尽管如此,我们的架构提供了以下功能:

  • 单线程
  • 在不同层级重复使用组件的能力
  • 近乎完美的层次感
  • 封装

功能分片设计(FSD)架构 - Feature Sliced Design#

FSD-Overview

概述 - https://feature-sliced.design/

功能切片设计(FSD)架构 -- 与模块化架构有很多相似之处,但也避免了我们上面讨论过的情况。这种方法是按功能区域(特性)而不是按层来构建项目。这种组织方式有助于避免全局目录增长(如模块化架构中的 components 、 UI ),并在组件、模块和层之间提供了明确的职责分工。

FSD — layers example

FSD - 图层示例

架构的构建方式是,顶层 pages 整合并组织所有子模块和组件的工作,而每一层则提供更详细、更具体的功能和元素。是的,我们在这里遵循同样的规则 -- 层级越高(例如 pages ),可以使用的下层层级就越少:

  • 页面 - 顶层包含应用程序中显示的 pages 。
  • 进程 - 在此架构中已被弃用,因此我们可以跳过它。
  • 功能 / 小工具 - pages 下面是主要的 features 块,这些块构建了页面的核心功能,使其具有可管理性和独立性。
  • 实体(Entities)-- 功能模块下面的 entities 是由 "共享" 层中可用的较简单用户界面组件编译而成的。
  • 共享 - 底层包含通用的用户界面组件,可用于应用程序的不同部分。

(⚠️) 重要:与模块化架构一样,各层之间不要过度使用。

Overview (slices segments)

概述(片 / 段)- https://feature-sliced.design/

FSD 体系结构以模块化元素为特色,称为 "片" 和 "段"。"切片" 指的是每一层中的模块,每个模块代表一个不同的业务实体。同时,"段" 包含各种结构组件,如 api/components/config/ constants/ 等,将架构组织成更清晰、更易于管理的部分。

最后,我们达到了目标结果:

Ideal — diagram (example)

理想 - 图表(示例)

将这种方法集成到项目中既不容易也不快速。它至少需要最低限度的架构知识,而且你必须牢记这可能需要时间。但是,掌握 FSD 的使用方法可以:

  • 结构清晰
  • 层次分明
  • 灵活组件
  • 独立模块
  • 平衡重用性

顺便说一句,这种架构提倡在文件命名中使用 "kebab case"👀。

product-description.vue / shopping-cart.tsx / get-base-url.ts / 等等。

使用 "模块化架构" 的实例:

使用 "FSD 架构" 的实例:

结论#

在本文中,我们探讨了经典架构、模块化架构和 FSD 架构之间的区别,并讨论了它们的用途。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。