如何组织你的 React + Redux 代码库
介绍
React 是现存最不拘泥于框架的前端框架之一。从状态选择、路由到管理代码结构,React 本身并没有提供任何指导方针。相比之下,Angular 提供了更好的洞察力,可以了解构建块在代码中的位置和方式。这让 React 开发人员在项目开始时处于困境。无论我们的经验如何,我们都发现在新项目开始时制定完美的代码库结构极其困难。
一般来说,React 项目结构通常会随着项目范围和复杂性而不断迭代发展。当添加新库(例如 Redux 和 React Router)时,需要重构初始结构以适应增加的复杂性。由于项目完成期限的压力,重构工作陷入积压状态,直到项目完全无法维护。
在本指南中,我们将探讨生产级应用程序中使用的几种目录结构,并分析每种结构的优缺点。重要的是要记住,没有一种结构可以普遍适用于每个项目。根据项目的规模、范围、复杂性和未来方面,最合适的结构会有所不同。因此,本指南可以作为选择正确的初始结构的起点,以便将未来的重构降到最低。
让我们使用一个模型项目来评估我们不同的代码库组织。我们将研究博客前端的代码库,其中具有以下功能:
- 文章(或博客文章)、类别和用户
- 显示类别列表和文章列表的主页
- 类别页面,显示特定类别的信息和文章
虽然功能集乍一看很简单,但实际代码库将跨越多个文件和目录。我们还假设使用 Redux 来管理我们的应用程序状态。
扁平结构
首先,我们将探讨最常见且最容易使用的结构。我将其称为扁平结构,因为它的目录嵌套最少,而且非常简单。它遵循在根级别分离逻辑和视图,然后将 Redux 相关目录添加到组合中的原则。
└── src
├── actions
│ ├── articleActions.js
│ ├── categoryActions.js
│ └── userActions.js
├── api
│ ├── apiHandler.js
│ ├── articleApi.js
│ ├── categoryApi.js
│ └── userApi.js
├── components
│ ├── ArticleComponent.jsx
│ ├── ArticleListComponent.jsx
│ ├── CategoryComponent.jsx
│ ├── CategoryPageComponent.jsx
│ └── HomePageComponent.jsx
├── containers
│ ├── ArticleContainer.js
│ ├── CategoryPageContainer.js
│ └── HomePageContainer.js
├── index.js
├── reducers
│ ├── articleReducer.js
│ ├── categoryReducer.js
│ └── userReducer.js
├── routes.js
├── store.js
└── utils
└── authUtils.js
简而言之,目录功能包括以下内容:
- 组件——包含所有“哑”或展示组件,仅由 HTML 和样式组成。
- 容器 - 包含所有相应的组件及其逻辑。每个容器将有一个或多个组件,具体取决于容器所代表的视图。例如,HomePageContainer将有ArticleListComponent以及CategoryComponent。
- 动作 - 所有 Redux 动作
- Reducers - 所有 Redux Reducers
- API - API 连接相关代码。处理程序通常涉及集中设置 API 连接器以及身份验证和其他必要的标头。
- Utils - 其他非 React 特定的逻辑代码。例如,authUtils具有处理来自 API 的 JWT 令牌以确定用户范围的函数。
store.js 只是 Redux 存储,而 routes.js 将所有路由聚合在一起以方便访问。
注意:根据新的 React Router 文档,在单个文件中定义所有路由的做法已被弃用。它提倡将路由分成组件以提高可读性。查看React Router 文档以获得更好的理解。
有了上面的了解,我们来分析一下为什么要使用扁平结构以及为什么不使用扁平结构。
优点
- 扁平结构更易于阅读。您可以轻松进行文件名搜索。
- 开发人员入职很容易。
缺点
- 需要编辑多个文件/目录来添加新功能。假设我们需要一个评论功能。我们需要将commentAction添加到操作中,将 commentReducer添加到 Reducers 中,将 CommentComponent添加到组件中,将CommentContainer添加到容器中。
- Redux 状态无处不在。action、reducer 以及类型(有时)位于单独的目录中。
- 当代码库增长时,缺乏内部结构会使其难以维护。例如,乍一看,我们无法看到HomePageContainer使用的组件。
- 在某些情况下,例如页面,容器-组件拆分没有意义。
对于上述问题,我们可以通过引入页面目录来提供一些组织方式,从而做出一些改进。
└── src
├── actions
│ ├── articleActions.js
│ ├── categoryActions.js
│ └── userActions.js
├── api
│ ├── apiHandler.js
│ ├── articleApi.js
│ ├── categoryApi.js
│ └── userApi.js
├── components
│ └── ArticleComponent.jsx
├── containers
│ └── ArticleContainer.js
├── index.js
├── pages
│ ├── CategoryPage
│ │ ├── CategoryPageContainer.js
│ │ └── components
│ │ └── CategoryPageComponent.jsx
│ └── HomePage
│ ├── components
│ │ ├── ArticleListComponent.jsx
│ │ ├── CategoryComponent.jsx
│ │ └── HomePageComponent.jsx
│ └── HomePageContainer.js
├── reducers
│ ├── articleReducer.js
│ ├── categoryReducer.js
│ └── userReducer.js
├── routes.js
├── store.js
└── utils
└── authUtils.js
现在,通过上述改进,目录结构为应用程序中各个组件的实际定位提供了一些背景信息。一目了然,很明显HomePageComponent、ArticleListComponent和CategoryComponent是HomePage的一部分。作为一个重要的副作用,现在留在根级别的组件和容器目录中的内容是不直接属于任何一个页面的共享组件。因此,我们可以更进一步,将它们分组到一个公共目录中。
└── src
├── actions
│ └── ...
├── api
│ └── ...
├── common
│ ├── components
│ │ └── ArticleComponent.jsx
│ └── containers
│ └── ArticleContainer.js
├── index.js
├── pages
│ └── ...
├── reducers
│ └── ...
├── routes.js
├── store.js
└── utils
└── ...
这看起来好多了。如果你的应用没有庞大的应用状态,而是视图和逻辑繁重,那么上述结构应该可以工作。它提供了显著的清晰度和可维护性。但如果你的 Redux 代码也随着其他功能的增长而增长,你很快就会发现你也需要一个更好的状态组织。
视图状态分离
视图状态拆分改进了以前的结构,以便更好地组织状态。它将视图和逻辑密集型组件与状态组件分开,但在状态中引入了额外的结构。
└── src
├── api
│ ├── apiHandler.js
│ ├── articleApi.js
│ ├── categoryApi.js
│ └── userApi.js
├── common
│ ├── components
│ │ └── ArticleComponent.jsx
│ └── containers
│ └── ArticleContainer.js
├── index.js
├── pages
│ ├── CategoryPage
│ │ ├── CategoryPageContainer.js
│ │ └── components
│ │ └── CategoryPageComponent.jsx
│ └── HomePage
│ ├── components
│ │ ├── ArticleListComponent.jsx
│ │ ├── CategoryComponent.jsx
│ │ └── HomePageComponent.jsx
│ └── HomePageContainer.js
├── routes.js
├── state
│ ├── article
│ │ ├── articleActions.js
│ │ └── articleReducer.js
│ ├── category
│ │ ├── categoryActions.js
│ │ └── categoryReducer.js
│ ├── middleware.js
│ ├── store.js
│ └── user
│ ├── userActions.js
│ └── userReducer.js
└── utils
└── authUtils.js
上述结构的变化很简单。状态现在嵌套了一层,其中特定应用程序功能的操作和缩减器被分组。这样,可以立即找到需要对特定功能进行更改的位置。例如,如果您的 API 决定发送文章标签,而现在您想在文章组件中显示它们,则首先编辑 api ,然后编辑状态,最后更新ArticleComponent。
优点
- 添加新的应用程序功能和维护当前功能很容易。
- 国家组织有序,安置有序,没有混乱。
- 所有 Redux 代码都集中在一个位置,因此重构很容易。例如,如果你一段时间后决定使用 Redux Toolkit,你知道只需要对state目录中的文件进行更改。
缺点
- 视图和状态是分离的。如果你的应用有数百个功能,那么找到与特定视图相对应的状态会很麻烦。
- 没有为功能相关的逻辑代码找到合适的位置。任何不适合当前结构的内容都需要放在utils目录中,这又将功能代码部分与功能组件代码分开。
- 开发人员入职并不是一件简单的事情。
虽然这种结构存在一些问题,但它可以适应大多数项目用例。但如果项目有很多活动部件,开发团队规模很大,上述问题就会开始成为阻碍点——尤其是当项目是由分布式团队开发时(开源项目就是典型的例子)。那么我们需要一个更好的组织,让开发人员能够在不破坏整个代码库的情况下处理单个应用程序功能。
应用程序功能拆分
我第一次接触到基于应用特性的拆分是通过Yoni Goldberg的《Node 最佳实践》。它旨在为同样不固执己见的 nodejs 项目提供结构。它提供了一个可扩展的模型来克服在 node 后端使用 MVC 模式的常见问题。简而言之,它建议按应用特性而不是代码功能拆分目录。例如,在我们的应用中,我们有以下三个特性:
- 文章
- 类别
- 用户
这些也被称为域。通过这样拆分它们,我们可以将与应用程序功能相关的所有功能代码分组到一个目录中,以便开发人员可以只专注于特定目录。让我们探索如何将模式融入我们的应用程序:
├── api
│ ├── apiHandler.js
│ ├── articleApi.js
│ ├── categoryApi.js
│ └── userApi.js
├── article
│ ├── Article.jsx
│ ├── ArticleList.jsx
│ └── state
│ ├── articleActions.js
│ └── articleReducer.js
├── category
│ ├── Category.js
│ └── state
│ ├── categoryActions.js
│ └── categoryReducer.js
├── category-page
│ └── CategoryPage.jsx
├── common
│ └── state
│ ├── commonActions.js
│ └── commonReducers.js
├── home-page
│ ├── HomePageContainer.js
│ └── HomePage.jsx
├── index.js
├── middleware.js
├── routes.js
├── store.js
└── user
├── authUtils.js
└── state
├── userActions.js
└── userReducer.js
在上述结构中,做了以下修改:
- 每个应用程序功能都保存在单独的目录中
- 特定功能的所有视图、逻辑和状态都分组放在相应的目录中(API 也可以带入其中)
- 容器和组件不是一分为二,而是聚合在一起。据观察,除非视图被其他逻辑重用,否则拆分视图和逻辑不会带来显著的好处。因此Article.js是ArticleContainer和ArticleComponent的组合。
优点
- 应用程序功能是分离的。
- 代码库具有更好的可扩展性、可维护性和可读性。
- 开发人员直观地知道从哪里导入特定功能。如果您需要访问与文章相关的操作,它们位于文章目录中。
缺点
- 何时应该将一组代码单独作为应用程序功能,何时不应该这样做,这一点并不十分清楚。例如,评论可以作为文章功能的一部分,因为评论只会出现在文章中。但评论也可以作为单独的目录。
- 与上述其他结构相比,开发人员入职更加困难。
即使存在上述问题,这种结构似乎是指南中讨论的选项中功能最强大的。在使用这些选项重构了多个代码库之后,我已经将其应用于任何具有相当复杂性的项目。在生产级应用程序中,需要许多较小的应用程序功能,包括通知、错误反馈、集中加载、身份验证处理等,并且使用基于功能的结构,添加和删除它们很容易。
结论
对于大多数前端开发人员来说,一开始就构建 React + Redux 代码库是一项令人困惑的任务。由于框架本身没有提供严格的指导方针,我们不得不使用基于试错的方法来找到最适合项目的结构。在本指南中,我们探讨了几种常见的组织方法,分析了每种方法的优缺点。虽然一种结构并不适合所有不同的项目要求,但我们可以参考上述结构作为起点。这大大减少了将来的重构工作量。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~