React项目很容易变得一团糟。失去对文件位置的追踪是非常普遍的,这可能会导致开发过程中的效率大大降低。那么,你如何改善和组织你的React项目?通过在一个多层次的架构中组织你的项目的所有文件。
因此,你将永远知道如何放置--并找到--每个文件。这是一个游戏规则的改变。它不仅会使你成为一个更有效率的开发者,而且还会使你的项目更容易维护。
此外,通过将项目变成有组织的架构,你的整个团队也会受益。这是因为不同的人可以在不同的层工作,避免了重叠和潜在的开销。
让我们来看看为什么你会想在React中使用多层结构。
多层结构如何优化React应用程序
重用代码
想象一下,你所有的React项目都共享相同的多层文件结构,让你的时间最大化。这种共享的文件结构允许你从一个项目向另一个项目导入任何类型的文件或代码片段,而不是将从其他代码库导入的组件或文件调整到你的目标项目,就像一个简单的复制和粘贴操作。这鼓励了代码的重复使用,使你更容易和更少的时间来建立你的应用程序。
例如,假设你的一个项目中有几个数学问题。通过使用这种文件结构,你不会被诱惑在你的代码库中分散所有需要的实用功能。
相反,它激励你定义一个math-utils.js 文件,在那里你可以存储你所有的数学实用程序。此外,通过在同一个文件中把服务于同一目的的元素外部化,你使它们更容易重复使用。事实上,如果你在未来的项目中面临类似的问题,你可以简单地复制之前定义的math-utils.js 文件,并将其粘贴到你的utils 层。
避免代码重复
强加多层结构的后果是,每个文件、函数或代码片断都属于一个特定的地方。这意味着,在编写新的东西之前,你应该先看看它的假设文件夹,这样可以避免代码的重复,并减少构建的大小。
一个包含大量代码的捆绑包显然会比需要的大。这对浏览器来说是不利的,因为它们必须下载并渲染代码。
所以,避免代码重复会使你的应用程序更干净,更容易构建,并且在渲染时更快。例如,每当你需要检索从某一特定API返回的数据时,你可能会被告知要遵循这种方法。
function FooComponent1(props) {
// ...
// retrieving data
fetch(`/api/v1/foo`)
.then((resp) => resp.json())
.then(function(data) {
const results = data.results;
// handling data ...
})
.catch(function(error) {
console.log(error);
});
// component logic ...
}
export default FooComponent1
function FooComponent2(props) {
// ...
// retrieving data
fetch(`/api/v1/foo`)
.then((resp) => resp.json())
.then(function(data) {
const results = data.results;
// handling data in a different way than FooComponent1 ...
})
.catch(function(error) {
console.log(error);
});
// different component logic from FooComponent1 ...
}
export default FooComponent2
正如你所看到的,FooComponent1 和FooComponent2 受到了代码重复问题的影响。特别是,API的端点被重复了,迫使你在每次改变时都要更新它。
这不是一个好习惯。相反,你应该把API映射到API层,如下所示。
export const FooAPI = {
// ...
// mapping the API of interest
get: function() {
return axiosInstance.request({
method: "GET",
url: `/api/v1/foo`
});
},
// ...
}
然后在需要的地方使用它,以避免代码的重复。
import React from "react";
import {FooAPI} from "../../api/foo";
//...
function FooComponent1(props) {
// ...
// retrieving data
FooAPI
.get()
.then(function(response) {
const results = response.data.results;
// handling data ...
})
.catch(function(error) {
console.log(error);
});
// component logic ...
}
export default FooComponent1
import React from "react";
import {FooAPI} from "../../api/foo";
//...
function FooComponent2(props) {
// ...
// retrieving data
FooAPI
.get()
.then(function(response) {
const results = response.data.results;
// handling data in a different way than FooComponent1 ...
})
.catch(function(error) {
console.log(error);
});
// different component logic from FooComponent1 ...
}
export default FooComponent2
现在,API端点只保存在一个地方,因为它应该始终如此。
利用团队的协同作用
使用一个众所周知的文件结构意味着开发团队的成员都在同一起跑线上,并以同样的方式使用它。
每当一个团队成员映射了一个新的API,或创建了一个新的utils文件或一个新的组件,例如,任何团队成员都能立即使用它。而且,由于每个文件都被逻辑地放置在一个特定的文件夹中,每个人都知道如何(和在哪里)访问,通信开销减少或完全消除,从而简化了公司的开发流程。
构建多层结构的一个潜在副作用
创建npm模块可能很复杂
拥有这样一个有组织的文件架构有一个潜在的坏处:文件分布在许多文件夹中,这迫使你密集地使用相对导入。这代表了创建npm模块时一个众所周知的问题。
相对导入很容易被破坏,尤其是在试图封装部分代码库,使其可以作为独立的模块发布时。一方面,在试图创建一个npm包时,这当然会代表一个额外的挑战。
另一方面,这可以通过将相对导入转化为不可破坏的绝对导入来轻松避免,如这里所述。这样的技术可以让你把这个。
import UserComponent from "../../components/UserComponent";
import userDefaultImage from "../../../assets/images/user-default-image.png";
import {UserAPI} from "../../apis/user";
变成这样。
importUserComponentfrom "@components/UserComponent";
import userDefaultImage from "@assets/images/user-default-image.png";
import {UserAPI} from "@apis/user";
所以,使用许多相对导入不应该被看作是一个真正的问题。
现在,让我们看看如何在React和JavaScript中为你的项目设计一个高效的多层架构。
创建多层架构
首先,让我们看一下多层架构的最终结果。请记住,我们架构的每一层都应该被封闭在一个特定的文件夹中。正如预期的那样,强烈建议将这些文件夹的名称与架构所包含的层相同。这样一来,检索文件就变得直观而快速。你也将永远知道在哪里放置一个新的文件,这是一个很大的好处。
这就是最终结构的模样。

正如你所看到的,架构的每一层都由一个文件夹代表。
现在,让我们深入了解所介绍的架构所包括的每一层。
1.API层
通过利用Axios等基于承诺的HTTP客户端,你可以为你的应用程序所依赖的每个API定义一个函数,封装调用它所需的所有逻辑。这样一来,你就可以把定义API请求的地方和实际使用的地方分开。让我们通过一个简单的例子来看看如何建立这样一个层。
const axiosInstance = axios.create({
baseURL: 'https://yourdomain.org'
});
export const UserAPI = {
getAll: function() {
return axiosInstance.request({
method: "GET",
url: `/api/v1/users`
});
},
getById: function(userId) {
return axiosInstance.request({
method: "GET",
url: `/api/v1/users/${userId}`
});
},
create: function(user) {
return axiosInstance.request({
method: "POST",
url: `/api/v1/users`,
data: user
});
},
update: function(userId, user) {
return axiosInstance.request({
method: "PUT",
url: `/api/v1/users/${userId}`,
data: user,
});
},
}
现在,你可以导入UserAPI 对象,在你需要的地方进行调用,就像这样。
import React, {useEffect, useState} from "react";
import {UserAPI} from "../../api/user";
//...
function UserComponent(props) {
const { userId } = props;
// ...
const [user, setUser] = useState(undefined)
// ...
useEffect(() => {
UserAPI.getById(userId).then(
function (response) {
// response handling
setUser(response.data.user)
}
).catch(function (error) {
// error handling
});
}, []);
// ...
}
export default UserComponent;
通过采用 [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)技术,你可以在同一个地方收集所有的API请求定义。但我们不要忘记目标。本文并不是要展示你可以通过API层实现什么,你可以按照这个和这个来进一步阅读。

将属于API层的所有文件组织在同一个文件夹中。
相反,我们要关注的是,通过这种方法,你将能够为API请求定义一个完全专用的层。这将使所有处理API请求的逻辑只保存在一个地方,而不是每次需要时都重复整个逻辑来调用API。
2.资产层
你的React项目可能依赖于那些严格意义上不属于你代码库的文件,比如多媒体文件。有一个地方来存储它们都是最好的做法,因为你应该把它们和你的代码明确地划分开来。请注意,这样的层可以根据类型或扩展来组织子文件夹,以保持其组织性。

一个按类型组织的资产层样本。
此外,这也是一个很好的地方,可以存储你在一个采用国际化框架的国际应用中所需要的所有翻译文件,比如说 [i18next](https://www.i18next.com/).

将所有的i18next 翻译文件放在翻译文件夹中。
3.组件层
这是你应该放置所有组件的地方。你应该为每个组件建立一个文件夹,并以与组件本身相同的方式命名。这样一个文件夹通常只包含两个文件:index.js 和index.css 。它们分别代表React组件被定义的地方和它的样式被包含的地方。

一个正在运行的组件层样本。
因为你的组件可能很大,你应该考虑将它们组织在一个嵌套结构中。找到一个好的标准来把它们分配到子文件夹中并不总是一个简单的任务。
同时,请记住,你可以随时改变它,按照你的意愿重新安排你的组件的存储结构。
4.恒定层
一个常见的做法是在一个单一的constans.js 文件中定义你的应用程序中使用的所有常量。在一个小项目中,这种方法可能看起来是最好的解决方案。但不可避免的是,当你的文件变大时,它将慢慢变成一个混乱的文件。
这就是为什么你应该把你的常量分成多个文件。例如,在一个国际化的应用程序中,你可以考虑将所有的自定义键存储在一个特定的文件中。 [i18next](https://www.npmjs.com/package/i18next)所用的所有自定义键都存储在一个特定的文件中。
这样一来,所有与i18n相关的常量都会在同一个文件中,使它们更容易被管理。同样,在其他情况下,你也可以遵循后一种方法,比如下面的例子。

将所有应用程序的常量分割成四个不同的文件。
5.Redux层
这是一个可选的层,只有当你的应用程序使用Redux时你才应该定义它。正如官方文档中所说,Redux是一个可预测的JavaScript应用程序的状态容器。如果你曾经使用过Redux,你应该知道它需要多少个文件。
迷失在众多的Redux还原器和Redux动作中是很常见的,尤其是后者。另外,定义动作涉及到模板代码。这就是为什么使用一个库,如 [redux-actions](https://www.npmjs.com/package/redux-actions)来管理你所有的动作。然后,你将能够简单地获得一个有组织的结构,将你的文件安排如下。

一个样本的Redux层由两个文件夹和一个文件组成。
如上图所示,你可以把你所有的Redux还原器放在reducers 文件夹里,你的Redux动作放在actions 文件夹里,并定义一个包含你的存储和坚持逻辑的顶层index.js 文件。如果需要,你甚至可以在子文件夹中组织上述两个文件夹中包含的文件。
6.路由层
你的项目可能已经将你的应用程序所涵盖的所有路由存储在一个routes.js 文件中。定义一个包含你所有路由的文件,将它们聚集在一个地方,并将它们与实际使用时分开。
同样,对于小项目来说,这种方法可能已经足够了。问题在于,你无法预测你的应用程序会增长到多大。这就是为什么你应该定义一个旨在包含你所有路由文件和逻辑的层。将你的routes.js 文件轻松分割成许多文件的诀窍是为你的应用程序所使用的每一层路由路径创建一个。

一个由四个文件组成的路由层样本。
7.实用层
这是一个存储所有你的整个代码库所依赖的自定义实用功能的地方。虽然你可能把你的函数存储在一个utilis.js 文件中,但随着你的项目规模的扩大,你可能被迫把它分成多个文件。
这就是为什么有一个专门的分层,这可能是不可避免的。与其等待这种情况的发生,不如抢先一步,建立一个新的层,如下所示。

将一个开始的utils.js 文件分割成两个不同的文件。
8.视图层
这是最上面的一层,你将在这里存储你的应用程序所包含的所有页面。请注意,这是唯一可能需要从前面提到的任何其他层导入文件的层。
如果你的项目有大量的页面,你应该把它们组织成子文件夹。实现这一目标的一个好标准是复制路由结构,就像在PHP项目中一样。例如,所有共享 [https://yourdomain.dom/users/](https://yourdomain.dom/users/)基本路径的页面应该放在Users 文件夹中。

例子中提出的标准的视图层结构样本。
多层结构中剩余的文件
请记住,这样的结构可能不足以覆盖构成你的代码库的所有文件。这就是为什么总是会有一些额外的文件,你可能不知道该放在哪里。
遵循这样一个有组织的结构,你可以把它们减少到几个地方。一个好的经验法则是,如果你有少于三个文件,你应该把它们留在你项目的顶层文件夹里,或者你认为它们属于的地方。否则,你可以随时设计新的图层来满足你的需要。
总结
在这篇文章中,我们看了如何(以及为什么你应该)组织一个React项目以使其更有效率。通过将你的文件放在代表多层架构的文件夹中,你可以将你的项目变成一个更有组织的结构。
采用这样的方法,每个文件都会有自己的位置。这将使你和你的团队能够设置架构标准,使你的整个代码库更加健壮和可维护,并简化你的开发过程。
谢谢你的阅读!我希望你觉得这篇文章对你有帮助。如果有任何问题、评论或建议,请随时与我联系。
The postOptimize React apps using a multi-layer structureappeared first onLogRocket Blog.