创建react应用程序
Is there a perfect method for structuring a React project? The official documentation won’t help you much. React is a library and not a framework, which is why it doesn’t have opinions on how you should build your app.
是否有构建React项目的完美方法? 官方文档对您没有太大帮助。 React是一个库而不是一个框架,这就是为什么它对如何构建应用没有意见的原因。
This is nice because it gives us the freedom to structure our app as we want. Experienced developers won’t find a disadvantage to this, but junior developers might find it difficult to find a way to build a neat structure.
很好,因为它使我们能够自由地构造我们想要的应用程序。 经验丰富的开发人员不会发现此缺点,但是初级开发人员可能会发现很难找到一种构建整洁结构的方法。
Each project answers a specific need and therefore needs a specific structure and rules. There are as many bad ways as there are good ways of structuring your app. The approach I will describe in this article might not be the perfect answer to your project’s specific needs.
每个项目都满足特定的需求,因此需要特定的结构和规则。 构造应用程序的方法有很多坏方法。 我将在本文中介绍的方法可能不是对项目的特定需求的完美答案。
With that said, I’ve been working with React for a while on projects of different sizes. The structure I’m using today is really different from the one I was using a few years ago. Indeed, I’ve been improving my way of structuring an app for every project I’ve created. Since I’m pretty happy with the structure I’m using these days, I decided to share it.
话虽如此,我已经与React进行了一段时间的不同规模的项目。 我今天使用的结构与几年前的结构确实不同。 实际上,我一直在为自己创建的每个项目改进构建应用程序的方式。 由于我对这些天使用的结构感到非常满意,因此我决定共享它。
Sure, the structure you will discover below might look like too much depending on your needs (app size, objective, etc.), but understand that it is one approach that allows your project to scale, which pays in the long run.
当然,根据您的需求(应用程序大小,目标等),您将在下面发现的结构看起来太多了,但是请理解,这是一种允许您的项目进行扩展的方法,从长远来看是值得的。
Unless you are completely certain of where you’re going, I wouldn’t say you need to apply a strict structure to your app from day one. Code can always be refactored later on the go. That’s a matter of choice.
除非您完全确定前进的方向,否则我不会说您从一开始就需要对应用程序应用严格的结构。 随时可以在以后重构代码。 这是一个选择的问题。
I always find refactoring to be super satisfying, but at the same time, I don’t like to work in a messy codebase to begin with either.
我总是觉得重构非常令人满意,但是同时,我也不喜欢从凌乱的代码库开始。
Thus, I try to apply my structure needs as soon as possible, as I consider it good for my productivity (which doesn't prevent potential refactorization in the future, obviously).
因此,我尝试尽快应用我的结构需求,因为我认为这对我的生产率有好处(显然,这不会阻止将来可能的重构)。
Let’s get into it!
让我们开始吧!
Photo by Dragos Gontariu on Unsplash. Dragos Gontariu在 Unsplash上的 照片 。Alright! The root directory is quite the same for every project and there is not much debate about it since it’s only about configuration stuff. So I’ll go straight to the ./src folder, where folks have opinions and tastes!
好的! 根目录对于每个项目都是相同的,并且关于它的争论不多,因为它仅与配置有关。 因此,我将直接转到./src文件夹,那里的人们有自己的见解和口味!
Here is how I structure my ./src folder:
这是我构造./src文件夹的方式:
/src │ ├── assets │ └── ... │ ├── components │ └── ... │ ├── containers │ └── ... │ ├── core │ └── ... │ ├── app.tsx ├── index.tsx └── router.tsxSo what’s happening here?
那么这是怎么回事?
Yes, I love TypeScript and you will deal with it in this article. TSX is the file extension for TypeScript files containing JSX code.
是的,我喜欢TypeScript,您将在本文中处理它。 TSX是包含JSX代码的TypeScript文件的文件扩展名。
./index.tsx: The entry point of the project. This is where I initialize the libraries I use, such as the theme provider (from styled-components), the store (Redux, Apollo, or your library of choice), the router (React Router) and I also include my <App /> component, obviously.
./index.tsx :项目的入口点。 我在这里初始化我使用的库,例如主题提供程序(来自styled-components ),商店( Redux , Apollo或您选择的库),路由器( React Router ),并且还包括我的<App />组件,显然。
./app.tsx: Contains the <Router /> component. This is also where I implement my features (mostly containers, but we will learn about that later) that are used across the whole app, such as the modal system, the notification container, service workers, etc.
./app.tsx :包含<Router />组件。 这也是我实现在整个应用程序中使用的功能(主要是容器,但我们稍后会学习的地方)的地方,例如模式系统,通知容器,服务人员等。
./router.tsx: Composed of a <Switch /> and the main <Route /> components of my project. If I need sub-routes, they are handled by the containers using them.
./router.tsx :由项目的<Switch />和主要的<Route />组件组成。 如果需要子路线,则由使用它们的集装箱来处理。
No big surprise. This folder obviously contains images, icons, fonts, and more.
没什么大惊喜。 该文件夹显然包含图像,图标,字体等。
But since I don’t want it to become a huge trash bin where the team just throws stuff randomly as the app grows, assets are sorted by context.
但是,由于我不希望它成为一个巨大的垃圾箱,因此随着应用程序的增长,团队只是在其中随机抛出一些东西,因此资产是按上下文分类的。
/assets │ ├── auth // ◀️ Assets specific to the auth container │ ├── connected.mp3 │ └── background.png │ ├── fonts │ └── ... │ ├── ionicon // ◀️ An icon Library │ └── ... │ ├── profile // ◀️ Assets specific to the profile container │ └── background.png │ ├── logo.svg └── ...The rule is as follows: If an asset is used only in a specific container, it belongs to a folder with the container’s name.
规则如下:如果仅在特定容器中使用资产,则该资产属于具有该容器名称的文件夹。
For instance, if my app uses a logo, it can probably be used anywhere in my project. For this reason, this one belongs to the root of the ./assets folder. But a background image that is only used in the Auth container belongs to the ./auth sub-folder.
例如,如果我的应用程序使用徽标,则它可以在项目的任何地方使用。 因此,该目录属于./assets文件夹的根目录。 但是仅在Auth容器中使用的背景图像属于./auth子文件夹。
I find it extremely simple to browse my assets this way.
我发现以这种方式浏览我的资产非常简单。
This method also makes it easier to find assets that are not used anymore. It shouldn’t be a problem in the first place, but let’s be honest: We often forget to clean this folder.
此方法还使查找不再使用的资产变得更加容易。 首先,这不应该是一个问题,但老实说:我们经常忘记清理此文件夹。
How should you group your components? A lot of different tastes here. React’s documentation suggests two solutions:
您应该如何对组件进行分组? 这里有很多不同的口味。 React的文档提出了两种解决方案:
Group files by feature or routes. 按功能或路由对文件进行分组。 Or group files by type (CSS, components, tests, etc.). 或按类型(CSS,组件,测试等)对文件进行分组。If I was working on a project with around 100 components, the second solution would make me crazy.
如果我正在开发一个包含约100个组件的项目,那么第二种解决方案会让我发疯。
I prefer to use the first option — or at least an improved version of it.
我更喜欢使用第一个选项,或者至少使用它的改进版本。
/src │ ├── components │ └── ... │ ├── containers │ └── ... │ └── ... // ◀️ other folders we saw aboveLet’s tackle the components folder first.
让我们首先解决components文件夹。
In order for a React component to earn its place in the ./components folder, it must obey two rules:
为了使React组件在./components文件夹中./components ,它必须遵守两个规则:
It must be a Presentational component, which means that it is not connected to the app’s state and certainly doesn’t fetch or post data.
它必须是Presentational组件 ,这意味着它未连接到应用程序的状态,并且肯定不会获取或发布数据。
It can interact with the parent container (by triggering a function that was passed in Props, for example). I am fine with that, but that’s all.
它可以与父容器进行交互(例如,通过触发在Props中传递的函数)。 我对此表示满意,但仅此而已。
It must be used across multiple components or containers. 必须在多个组件或容器中使用它。Components make things look good, while containers make things work.
组件使事物看起来不错,而容器使事物起作用。
Depending on what your app achieves, it can be a page or a module of a page. I like to call them features. The header is a feature. The auth page is a feature as well. They accomplish something specific.
根据您的应用程序实现的效果,它可以是页面,也可以是页面的模块。 我喜欢称它们为功能 。 标头是一个功能。 身份验证页面也是一个功能。 他们完成一些特定的事情。
Containers are stateful, which means that they can:
容器是有状态的,这意味着它们可以:
Subscribe to the store. 订阅商店。 Trigger side effects, (interact with the store, fetch or post data, etc.). 触发副作用(与存储交互,获取或发布数据等)。 Handle the dispatching of analytics events. 处理分析事件的调度。 Provide state, data, and actions to child components via props. 通过道具向子组件提供状态,数据和动作。This makes it clear in my file structure where the logic is and where the presentational part is.
这在我的文件结构中清楚说明了逻辑所在的位置和表示部分的位置。
My core folder could also be named Commons or Shared. It contains everything that is used across the app.
我的核心文件夹也可以命名为Commons或Shared 。 它包含整个应用程序中使用的所有内容。
/core │ ├── models │ ├── notification.model.ts │ ├── user.model.ts │ └── ... │ ├── services │ ├── notification.ts │ ├── notification.test.ts │ ├── user.ts │ ├── user.test.ts │ └── ... │ ├── store │ ├── middlewares │ │ └── ... │ │ │ ├── auth │ │ ├── actions.ts │ │ ├── epics │ │ │ ├── some-side-effect.ts │ │ │ ├── fetch-stuff.ts │ │ │ └── ... │ │ │ │ │ ├── reducer.ts │ │ └── selectors.ts │ │ │ ├── index.ts │ └── state.ts │ └── theme ├── animations.ts ├── global-state.ts └── index.tsSince I use TypeScript for my projects, it means I need a place to store all my types and interfaces.
由于我在项目中使用TypeScript,这意味着我需要一个地方来存储所有类型和接口。
There is one file per type of data (User, Product, Notification, etc.).
每种数据类型( User , Product , Notification等)只有一个文件。
A container’s job is to enable the feature. To keep my containers as lean as possible, services handle the business logic that would make a lot of unneeded noise in the component. Services are often responsible for validating inputs, logging errors, doing HTTP requests, manipulating data, etc.
容器的工作是启用该功能。 为了使我的容器尽可能地精简,服务会处理业务逻辑,这些业务逻辑会在组件中产生很多不必要的噪音。 服务通常负责验证输入,记录错误,执行HTTP请求,处理数据等。
There are as many services as there are concerns in the app, so it is easy to find where the stuff I need is.
应用程序中涉及的服务数量很多,因此很容易找到我需要的东西在哪里。
The store folder contains the store configuration and its middlewares — nothing fancy. It also contains all the reducers, their actions, their epics, and their selectors.
store文件夹包含商店配置及其中间件-没什么花哨的。 它还包含所有化简器,它们的动作,它们的史诗和它们的选择器。
Where we should put reducers is often a debate. Should we put them next to the container where they are mainly used or in a separate directory with all the other reducers?
我们应该在哪里放置减速器通常是一场辩论。 我们应该将它们放在主要使用它们的容器旁边,还是与所有其他reducer放在单独的目录中?
Both options work. I prefer to group them up in the ./core/store folder for the simple fact that the state is often used across multiple components, and so are actions.
这两个选项都可以。 我更喜欢将它们./core/store到./core/store文件夹中,因为这样一个简单的事实:状态经常用于多个组件,动作也是如此。
I’m using Redux-Observable to deal with my app’s side effects. That’s the equivalent of the NgRx way of handling side effects for Angular, and I find it very convenient. That’s why you see an epic folder. Since they can be triggered by multiple containers, I put them in their respective folder in the store.
我正在使用Redux-Observable处理我的应用程序的副作用。 这等效于NgRx处理Angular副作用的方法,我发现它非常方便。 这就是为什么您看到一个史诗般的文件夹。 由于它们可以由多个容器触发,因此我将它们放在商店中各自的文件夹中。
Finally, the ./theme folder. Like many of us, I use the popular styled-components library to design my components. The theme folder is where I put the global styles of my project as well as my CSS animations.
最后,。/ ./theme文件夹。 像我们许多人一样,我使用流行的样式组件库来设计组件。 在主题文件夹中,我放置了项目的全局样式以及CSS动画。
I also store the theme’s variables of my project in the index.ts file, as described in the documentation.
如文档中所述,我还将项目主题的变量存储在index.ts文件中。
Containers are responsible for more stuff than presentational components, but I structure them the same way. Thus, the content below is true for containers and components. Here is how you can structure a component’s folder:
容器比表示组件负责更多的工作,但是我以相同的方式构造它们。 因此,以下内容适用于容器和组件。 这是构造组件文件夹的方式:
/my-component │ ├── components // ◀️ Only for a Container! │ ├── sub-componentA │ │ ├── index.test.tsx │ │ ├── index.tsx │ │ └── styled.ts │ │ │ └── sub-componentB │ └── ... │ ├── service // ◀️ Optional, mostly for Containers │ ├── index.test.tsx │ └── index.ts │ ├── index.test.tsx ├── index.tsx └── styled.ts./index.tsx: Where my component is defined. Some people prefer to name it after the name of the component it exports. That’s a matter of taste. Both methods have their pros and cons. I don’t mind which one is used in my projects.
./index.tsx :定义我的组件的位置。 有些人喜欢用它导出的组件名称来命名。 那是一个品味问题。 两种方法各有利弊。 我不介意在我的项目中使用哪一个。
./styled.ts: Where the design of my component resides.
./styled.ts :组件设计所在的位置。
All components created in this file follow the same convention. They are named
在此文件中创建的所有组件都遵循相同的约定。 他们被命名
StyledXxx and are used as follows: <StyledXxx />. As a component can use a lot of child components, this is an easy way of spotting if a component is only responsible for design or not.
和StyledXxx的用法如下: <StyledXxx /> 。 由于一个组件可以使用许多子组件,因此这是一种简单的方法,可以确定一个组件是否仅负责设计。
./index.test.tsx: Where the tests live.
./index.test.tsx :测试所在的位置。
A container often uses its own presentational components. By “own,” I mean that those presentational components are only used inside this container.
容器通常使用其自己的表示组件。 “拥有”是指那些表示性组件仅在此容器内使用。
As I want to keep the code close to where it is used, it makes sense to let those components live in the container.
由于我想使代码保持在使用位置附近,因此让这些组件驻留在容器中是很有意义的。
The ./components sub-folder is capped at a depth of 2, which leaves the container easy to browse. A deeper depth would needlessly increase the complexity.
./components子文件夹的深度为2,这使容器易于浏览。 更深的深度会不必要地增加复杂性。
In the example above, <SubComponentB /> could be a child of the <SubComponentA />, but that doesn’t matter. They live at the same level.
在上面的示例中, <SubComponentB />可以是<SubComponentB />的子<SubComponentA /> ,但这并不重要。 他们生活在同一水平。
The service helps me to outsource the specific heavy business logic from my container. Not every container needs its own service since “common” logic might already exist in the core folder, but it might be needed sometime.
该服务帮助我从容器中外包特定的繁重业务逻辑。 并非每个容器都需要自己的服务,因为核心文件夹中可能已经存在“通用”逻辑,但有时需要使用。
Let’s take an example: I have an analytics event to dispatch that requires ten fields (I swear I have encountered this case) in a specific way. This means it’s going to add ten-plus lines of noise in my container. That’s a lot of noise for an analytic event, but I want the Business Intelligence team to be happy, so I provide what they want!
让我们举个例子:我有一个要分派的分析事件,它需要以特定方式需要十个字段(我发誓我遇到过这种情况)。 这意味着它将在我的容器中添加十多行噪声。 对于分析事件而言,这是很大的麻烦,但是我希望商业智能团队感到高兴,所以我提供他们想要的!
As I want to keep my container as lean as possible so it can be understood at first glance, I put this logic in a service file next to my component. This method also makes testing cleaner.
由于我想使容器尽可能地精简,以便乍看之下可以理解,因此我将此逻辑放在组件旁边的服务文件中。 这种方法也使测试更清洁。
Some might say that it actually makes it harder to understand what is going on since they need to open the service to check the logic. I don’t see why this is a problem. Moreover, since I use TypeScript, hovering the method tells me what I get back.
有人可能会说,实际上这使人们很难理解发生了什么,因为他们需要打开服务来检查逻辑。 我不明白为什么这是一个问题。 此外,由于我使用TypeScript,所以将鼠标悬停在该方法上可以告诉我获得了什么。
Having a good file structure is crucial, but let’s also focus on the React component itself since that is where we spend most of our time.
拥有一个好的文件结构是至关重要的,但是让我们也专注于React组件本身,因为那是我们花费大部分时间的地方。
Here are some rules and how I structure my React components:
以下是一些规则以及我如何构造我的React组件:
A <Profile /> container example <Profile />容器示例The name should be unique and clear about what that component achieves.
名称应唯一,并且清楚该组件可以实现的功能。
Why is it important to name your components? I like to call my colleagues by their names — the same goes for my components.
为什么命名组件很重要? 我喜欢用同事的名字来称呼我的同事。
More seriously, it helps with debugging. Here is what happens with an anonymous component:
更严重的是,它有助于调试。 这是匿名组件发生的情况:
export default (props) => { return <p>Don't do this</p>;}If you inspect your app with the React dev tool, everything appears as Anonymous, which is really frustrating. The same goes for runtime errors…
如果您使用React开发工具检查您的应用程序,那么所有内容都会显示为Anonymous ,这实在令人沮丧。 运行时错误也是如此...
Debugging Anonymous components with React Dev Tools 使用React Dev Tools调试匿名组件 Really? Thanks, Mr. React! 真? 谢谢,React先生!Naming your components makes your life way easier!
命名组件使您的生活更轻松!
const Better = (props) => { return <p>Do This instead</p>}export default Better; Debugging named components with React Dev Tools 使用React Dev Tools调试命名组件 Way faster to know where to go! 更快捷地知道去哪里!In a big project, you want your components to have a name.
在一个大项目中,您希望您的组件具有名称。
When I open a React component, I want to see what it receives at first glance. That’s why I put the Props interface at the top of the file, right below imports.
当我打开一个React组件时,我想乍一看它会收到什么。 这就是为什么我将Props接口放在文件的顶部,就在导入的下面。
I then destructure the props in the function’s parameter. Since VSCode only highlights props that are used, it makes it simpler to see if a prop is still used or not. It’s all about keeping my component as clean as possible.
然后,我在函数的参数中分解道具。 由于VSCode仅突出显示已使用的道具,因此使查看是否仍在使用道具更加简单。 这是关于保持组件尽可能清洁的全部。
They are noisy, but we need them. They are at the top of the component so they don’t bother me later on when I read the rest of the file.
它们很吵,但是我们需要它们。 它们位于组件的顶部,因此以后在我阅读文件的其余部分时,它们不会打扰我。
These rules are simple, easy to follow, and don’t represent much work, but they still make your life a bit easier.
这些规则很简单,易于遵循,并且工作量不大,但是它们仍然使您的生活更加轻松。
Keep in mind that the example above is super simple. Components are usually bigger and less easy to read than this one.
请记住,上面的示例非常简单。 组件通常比此组件更大且不易阅读。
So that’s where my eyes stop — right after the Props interface. They are right below the hooks, as they represent the most valuable information: what data my container receives from the store and what state it manages locally.
因此,在Props界面之后,我的眼睛就停了下来。 它们就在钩子下面,因为它们代表了最有价值的信息:我的容器从商店接收什么数据以及它在本地管理什么状态。
I know what my component is made of. Effects tell me how it behaves. In order of importance, that’s what I want to see below the component’s state.
我知道我的组件是由什么组成的。 效果告诉我它的行为。 按照重要性的顺序,这就是我想在组件状态下看到的内容。
Lastly, the business logic. I have every piece of information I need to understand the logic.
最后,业务逻辑。 我掌握了了解逻辑所需的所有信息。
Below is the full structure that I went through in this article.
下面是我在本文中介绍的完整结构。
As I mentioned earlier, you might not need to implement the complete structure in your app because each app has its own needs. If your app is extremely small, with five components and one container, it’s obviously too much.
如前所述,您可能不需要在应用程序中实现完整的结构,因为每个应用程序都有自己的需求。 如果您的应用程序非常小,只有五个组件和一个容器,那么显然太多了。
This is my approach to structuring a React app. It is an approach that scales, works great, and allows me to work fast without compromising the quality of the codebase as the app grows.
这是我构建React应用程序的方法。 这是一种可扩展,有效的方法,可让我快速工作,而不会因应用程序的增长而影响代码库的质量。
There are many other ways to structure your app, and I would say that more than the structure choices you make, the most important consideration is staying consistent across the whole project.
还有许多其他方法来构建应用程序,我想说的是,除了您做出的结构选择之外,最重要的考虑因素是在整个项目中保持一致。
/src │ ├── assets│ ├── auth │ │ │ │ │ ├── connected.mp3 │ │ └── background.png │ │ │ ├── fonts │ │ └── ... │ │ │ ├── ionicon │ │ └── ... │ │ │ ├── profile │ │ └── background.png │ │ │ ├── logo.svg │ └── ... │ ├── components │ ├── componentA │ │ ├── service │ │ │ ├── index.test.tsx │ │ │ └── index.ts │ │ │ │ │ ├── index.test.tsx │ │ ├── index.tsx │ │ └── styled.ts │ └── ... │ ├── containers │ ├── containerA │ │ ├── components │ │ │ ├── sub-componentA │ │ │ │ ├── index.test.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── styled.ts │ │ │ │ │ │ │ └── sub-componentB │ │ │ └── ... │ │ │ │ │ ├── service │ │ │ ├── index.test.tsx │ │ │ └── index.ts │ │ │ │ │ ├── index.test.tsx │ │ ├── index.tsx │ │ └── styled.ts │ │ │ └── ... │ ├── core │ ├── models │ │ ├── notification.model.ts │ │ ├── user.model.ts │ │ └── ... │ │ │ ├── services │ │ ├── notification.ts │ │ ├── notification.test.ts │ │ ├── user.ts │ │ ├── user.test.ts │ │ └── ... │ │ │ ├── store │ │ ├── middlewares │ │ │ └── ... │ │ │ │ │ ├── auth │ │ │ ├── actions.ts │ │ │ ├── epics │ │ │ │ ├── some-side-effect.ts │ │ │ │ ├── fetch-stuff.ts │ │ │ │ └── ... │ │ │ │ │ │ │ ├── reducer.ts │ │ │ └── selectors.ts │ │ │ │ │ ├── index.ts │ │ └── state.ts │ │ │ └── theme │ ├── animations.ts │ ├── global-state.ts │ └── index.ts │ ├── app.tsx ├── index.tsx └── router.tsxThanks for reading!
谢谢阅读!
翻译自: https://medium.com/better-programming/how-you-should-structure-your-react-applications-e7dd32375a98
创建react应用程序
相关资源:kkt:创建没有构建配置的React应用程序,用于创建React应用程序的Cli工具-源码