我如何组织 React 项目
我完全理解,讨论如何“正确”地组织一个 React 项目(或者,使用任何框架和任何语言的项目)有点像讨论如何“正确”地打理头发。(虽然我想我们都能同意,客观上“正确”的发型显然是莫霍克发型。)
项目布局虽然看似“基础”,但即便入行25年,我仍然发现自己需要不断调整和改进“默认”的项目结构。因此,在深入探讨我的Spotify Toolz项目(https://www.spotifytoolz.com)的更多细节之前,我想先快速写一篇关于我目前如何组织React项目的文章。
我也欢迎大家对这篇文章进行“互动”。即使现在,在写了这么多年代码之后,似乎每隔六个月左右,我还是会突然意识到“这些数据真的应该存在那里!” 所以我很想看看你们在项目组织方面的最佳实践。
就像“项目组织”这类主观性很强的问题一样,我可以百分之百保证,我目前的做法是经验上最佳的。我也可以保证,任何其他做法都是“错误的”。而且,六个月后,我将采用一种完全不同的项目组织方法。届时,我会对任何遵循本文所述组织方式的人嗤之以鼻,并轻蔑地告诉他们,我已经转向了一种远胜于此的组织方案。
如果你使用这种组织方案,最终却不满意,我将非常乐意退还你阅读本文所支付费用的 150%。
基本组织
(如果您无法理解上图的含义,我会尽量原谅您。简单来说,它类似于留声机或马鞭。)
我最近的大多数 React 项目的结构都与此非常接近:
/src
app.js
/shared
/classes
/components
/css
/functions
/hooks
/objects
/models
/routes
如果我的应用有任何用户界面方面的内容,我通常会假设我会使用 React Router。如果我使用了 React Router,那么目录/routes结构就会与用户在应用中导航时看到的(虚拟)目录结构一一对应。
因此,如果应用程序有一个users模块(/user),该模块又有单独的“页面”用于创建(/user-create)、编辑(/user-edit)和查看(/user-view)用户,那么我的项目看起来会像这样:
/src
app.js
/shared
/classes
/components
/css
/functions
/hooks
/objects
/models
/routes
/user
/create
/edit
/view
此外,当我创建映射到这些路由的组件时,它们会以 JS 文件的形式出现在相应的文件夹下。因此,一旦我们为每个路由填充了基础组件,组件树看起来就像这样:
/src
app.js
/shared
/classes
/components
/css
/functions
/hooks
/objects
/models
/routes
/user
/create
/components
create.user.js
/edit
/components
edit.user.js
/view
/components
view.user.js
请注意,文件夹下没有任何直接的/routes/{routeName}文件。这是因为定义路由的所有内容classes在逻辑上都应该位于`<route>` components、` css<return>`、` functions<return>`、`<return> hooks` 或 ` objects<return>` 文件夹下。
从实际层面来说,这意味着我的大部分路由逻辑都位于 `<route>` 下/src/routes/{routeName}/components/{route.name.js}。因为对于我的大部分路由来说,所有特定于路由的逻辑都封装在 `<route>` 中/src/routes/{routeName}/components/{route.name.js}。
现在假设组件view.user.js(即某个<ViewUser>元素)需要一个名为 `function` 的函数getLastUserLoginTimestamp()。当我创建这个函数时,我需要做出一个组织结构上的选择。这个选择取决于以下问题:
这个函数是否只会在这个组件中使用,并且只会在这个组件中使用?
如果答案是“是”(即,如果此功能完全独特且仅针对此组件),那么我会创建一个如下所示的结构:
/src
app.js
/shared
/classes
/components
/css
/functions
/hooks
/objects
/models
/routes
/user
/create
/components
create.user.js
/edit
/components
edit.user.js
/view
/components
view.user.js
/functions
get.last.user.login.timestamp.js
在这种情况下,我决定该getLastUserLoginTimestamp()函数只会在组件内部使用ViewUser。因此,我在主目录/functions下创建了一个单独的目录/src/routes/user/view。这意味着该函数getLastLoginTimestamp()只会在组件内部使用ViewUser。因此,/functions存放该函数的目录应该始终位于主目录下/src/routes/user/view。
但坦白说,上面的例子并不常见。通常情况下,当我创建辅助函数时,我已经知道它们会在应用程序的其他地方被使用。事实上,即使我不确定它们在应用程序中的具体用途,我通常也会假设我创建的函数最终会在其他地方被用到。
因此,我很少将函数放在特定的/src/routes/{routeName}目录下。通常情况下,我会将这些函数放在同一个/shared目录下。例如:
/src
app.js
/shared
/classes
/components
/css
/functions
get.last.user.login.timestamp.js
/hooks
/objects
/models
/routes
/user
/create
/components
create.user.js
/edit
/components
edit.user.js
/view
/components
view.user.js
分享即关爱
如果还不清楚的话,我的应用程序中的“/src/shared”目录包含了绝大部分的应用程序逻辑。这有两个原因:
-
许多类/组件/样式/函数/钩子/对象从一开始就被设计成“通用”的。即使我不知道某个文件将来会如何被重用,我通常也会以假设它们会被重用的方式来编写文件。因此,这些文件大多最终都放在了
/src/shared…… -
即使某个类/组件/样式/函数/钩子/对象看起来只会在单个路由中使用,
/src/shared除非我百分之百确定该文件永远不会在其他任何地方使用,否则我倾向于将文件保存到该位置。
这意味着我的/src/shared目录往往是一个不断增长的潜在可重用资源库。这也意味着我的/src/routes目录结构比较稀疏——但它们与用户在应用程序中的潜在路径之间存在相当简单的一一对应关系。
重要提示
目前,我通常将所有React 组件都编写成基于函数的组件。这意味着我不使用 ` <component> export class SomeComponent extends React.Component {...}`。相反,我会这样写export const SomeComponent = () => {...}。
所以,当你查看上面的目录结构时/src/shared/classes,你可能会认为这个目录存放的是基于类的组件。但事实并非如此。
在我选择的项目结构中,/src/shared/classes类仅用于存放实用辅助类。例如,我经常使用一个辅助类localStorage(您可以在这里阅读相关内容:https://dev.to/bytebodger/getting-more-out-of-and-into-storage-with-javascript-41li)和一个验证库(您可以在这里阅读相关内容:https://dev.to/bytebodger/better-typescript-with-javascript-4ke5 )。这是我在最近的 React 开发中唯一真正使用类的地方。
你会注意到,在 `<path>` 下/src/shared有一个/components目录。这个目录并非用于存放定义路由的“主”组件,而是用于存放我在整个应用程序中反复使用的所有“辅助”组件(例如,高阶组件)。
就我而言,这个/src/shared/css文件夹通常存放实际的 CSS 类。如果我在 JSX 中使用内联 CSS,那么它会在 `<style>` 标签内定义/src/shared/objects(因为内联 CSS 的样式是JavaScript 对象)。
我很少创建不位于 ` <head>`标签下的Hook /src/shared/hooks。在我看来,如果你的 Hook 永远不会在多个组件之间共享,那么为什么不直接在使用它的单个函数组件的主体中定义它呢?
最后,我使用 `is` 的方式/src/objects可能让一些人感到困惑。我在开发过程中发现了很多使用“普通 JS 对象”的场景。你可以在这里找到一个例子:https ://dev.to/bytebodger/hacking-react-hooks-shared-global-state-553b和这里:https://dev.to/bytebodger/why-is-this-an-anti-pattern-in-react-427p
至于我如何使用它/src/objects/models,我的验证库在这里有解释:https://dev.to/bytebodger/better-typescript-with-javascript-4ke5简而言之,它/src/objects/models帮助我验证传递给我的函数的对象的形状。
给我看看你的
这是我目前组织 React 项目的方式。(我相信大家都会认同这是正确的方式。)你们是如何组织项目的呢?我是否遗漏了什么?请告诉我……
文章来源:https://dev.to/bytebodger/how-i-organize-react-projects-1f36



