- 原文地址:structuring projects and naming components in react
- 原文做者:Vinicius Dacal
- 译文出自:阿里云翻译小组
- 译文连接:github.com/dawn-teams/…
- 译者:也树
React 做为一个库,不会决定你如何组织项目的结构。这是件好事,由于这样咱们有了充分的自由去尝试不一样的组织方式而且选取最适合咱们的方式。可是从另外一个角度讲,这可能会让刚刚上手 React 的开发者产生些许困惑。css
我将会在本文为你们展现我已经使用过一段时间而且效果不错的方式,这些方式没有经过从新造轮子来实现,而是经过将社区中的方案组合和提炼获得。react
注意:这里没有什么是绝对正确的!你能够选择你认为容易理解,而且能够适应或能够改形成适应你的情景的方式。git
我最多见到的一个问题就是如何组织文件和目录结构。在本文中,咱们假设你有一个 create-react-app
生成的最简单的目录结构。github
create-react-app
为咱们生成了一个基础的项目,包含根目录还有诸如.gitignore
, package.json
, README.md
, yarn.lock
的文件。json
它还生成了 public
和 src
目录,src
目录就是咱们存放源代码的目录。bash
看一下下面图片所描述的结构:react-router
本文咱们只会关注 src
目录,全部在它以外的都会保持不变。app
咱们能够看到在 src
目录下有 containers 目录和 components 目录:dom
src
├─ components
└─ containers
复制代码
可是这个方式会致使下面这些问题:编辑器
components
挪到 containers
目录下,反之亦然。有一种基于这种方式的变种方式,在模块的目录下保持着两个目录的分离。
想象一下在你的应用中有一个 User 模块,在此模块下,你有两个目录去分离你的组件:
src
└─ User
├─ components
└─ containers
复制代码
上述方式最小化了在两个遥远目录下不断切换的的问题,可是一样增长了不少烦恼。当你的应用有很是多模块的时候,你最终会可能会建立几十个 containers
和 components
目录。
因此咱们讨论如何组织目录和文件的时候,和组件是否被拆分为展现型和容器型是无关的。也就是说,咱们会把全部的组件都放在 components
目录下,除了页面。
即便在目录上拆分它们是没必要要的,了解它们之间的差别性依然是有必要的。若是你对这个话题还有疑问,建议阅读这篇文章:Presentational and Container Components。
在 components
目录下,咱们经过模块/特性(module/feature)的结构来组织文件。
在对用户进行增删改查的过程当中,咱们只会有一个 User 模块。因此咱们的目录结构会像下面这样:
src
└─ components
└─ User
├─ Form.jsx
└─ List.jsx
复制代码
每当一个组件会有不止一个文件的时候,咱们会将这个组件和它对应的文件放在同一个文件夹下,而且使用同一个名字来命名。举个例子:如今咱们有一个 Form.css
文件包含了 Form.jsx
的样式,这时咱们的目录结构会像这样:
src
└─ components
└─ User
├─ Form
│ ├─ Form.jsx
│ └─ Form.css
└─ List.jsx
复制代码
测试用的文件和被测试的文件放在一块儿,在上面这个例子中,
Form.jsx
的测试文件会放在同一个文件夹下而且命名为Form.spec.jsx
除了经过模块拆分组件,咱们还会在 src/components
放置一个 UI
目录,用于存放全部通用的组件。
UI 组件不属于任何一个模块,须要足够通用。它们应该能够直接放在开源库中,由于它们不包含任何特定应用的业务逻辑。常见的这类组件有:按钮,输入框,复选框,下拉选择,模态框,数据可视化组件等等。
以上咱们了解了如何组织目录结构和如何经过模块来拆分咱们的组件,可是还有一个问题:如何命名它们?
这里咱们说的是如何命名咱们的 class 或者定义组件的常量。
class MyComponent extends Component {}
const MyComponent = () => {};
复制代码
组件的命名在应用中应当清晰且惟一,这样可让它们能够轻松被找到而且避免可能的困惑。
当应用在运行时发生错误或者经过 React 开发者工具调试时,组件的名字是很是方便易用的,由于错误发生的地方每每都伴随着组件的名字。
咱们采用基于路径的组件命名方式,即根据相对于 components
文件目录的相对路径来命名,若是在此文件夹之外,则使用相对于 src
目录的路径。举个例子,组件的路径若是是 components/User/List.jsx
,那么它就被命名为 UserList
。
若是文件名和文件目录名相同,咱们不须要重复这个名字。也就是说,components/User/Form/Form.jsx
会命名为 UserForm
而不是 UserFormForm
。
这样的命名方式有如下几点好处:
若是你的编辑器支持模糊搜索,只须要搜索 UserForm
就可让你找到对应的文件:
若是你想要在目录树中搜索文件,能够很容易地经过组件的名字定位到它:
遵循这种方式,你能够根据组件的上下文环境来命名文件。想一下上面的 form 组件,咱们知道它是一个 User 模块下的 form 组件,可是既然咱们已经把 form 组件放在了 User 模块的目录下,咱们就不须要在 form 组件的文件名上重复 user 这个单词,使用 Form.jsx
就能够了。
我最初使用 React 的时候喜欢用完整的名字来命名文件,可是这样会致使相同的部分重复太屡次,同时引入时的路径太长。来看看这两种方式的区别:
import ScreensUserForm from './screens/User/UserForm';
// vs
import ScreensUserForm from './screens/User/Form';
复制代码
在上面的例子中,咱们看不出来明显的优点。可是应用复杂度上升一点时就可以看到区别了。咱们来看看下面这个我实际项目中的例子:
import MediaPlanViewChannel from '/MediaPlan/MediaPlanView/MediaPlanViewChannel.jsx';
// vs
import MediaPlanViewChannel from './MediaPlan/View/Channel';
复制代码
如今想象一下一个文件名中重复五到十次。
出于这样的缘由,咱们认为根据组件文件的上下文环境以及它的相对路径来命名是更好的方式。
若是要对一个用户作增删改查的操做,咱们须要有用户列表页面,建立新用户的页面以及编辑已有用户的页面。
在应用中,经过使用组件相互组合的结果,就是一个页面。理想状态下,页面应该不包含任何逻辑,而仅仅是一个函数式组件。
咱们以 src
目录为根目录,将不一样页面分散在不一样文件夹中。由于它们是根据路由定义而不是模块来划分红组的。
src
├─ components
└─ screens
└─ User
├─ Form.jsx
└─ List.jsx
复制代码
假设咱们项目中在使用 react-router,咱们在 screens 目录下放置 Root.jsx 文件,而且在其中定义咱们应用全部的路由。
Root.jsx 的代码可能像下面这样:
import React, { Component } from 'react';
import { Router } from 'react-router';
import { Redirect, Route, Switch } from 'react-router-dom';
import ScreensUserForm from './User/Form';
import ScreensUserList from './User/List';
const ScreensRoot = () => (
<Router>
<Switch>
<Route path="/user/list" component={ScreensUserList} />
<Route path="/user/create" component={ScreensUserForm} />
<Route path="/user/:id" component={ScreensUserForm} />
</Switch>
</Router>
);
export default ScreensRoot;
复制代码
注意咱们将全部页面都放在同一个目录下,这个目录以路由名称命名。尝试为每一个父级路由创建一个目录,在这个目录中组织子路由。在这个示例中,咱们建立了 User 目录而且将 List 页面和 Form 页面放在里面。这种方式使你看一眼 url 就可以轻松定位当前路由渲染的页面。
像上面的例子中的建立和编辑一个用户的路由同样,一个页面可能会被两个不一样的路由渲染使用。
你可能注意到了全部的组件都包含 Screen 做为名称的前缀。当组件在组件目录外使用时,咱们须要使用它们相对于 src 目录的路径来命名。位于 src/screens/User/List.jsx
的组件应该被命名为 ScreensUserList。
包括 Root.jsx 在内,咱们的目录结构以下:
src
├─ components
└─ screens
├─ User
│ ├─ Form.jsx
│ └─ List.jsx
└─ Root.jsx
复制代码
别忘了在 index.js 中引入做为应用根组件的 Root.jsx 。
若是你对一个页面长什么样子还有疑问,看看下面的示例,它就是用户表单的页面。
import React from 'react';
import UserForm from '../../components/User/Form/Form';
const ScreensUserForm = ({ match: { params } }) => (
<div> <h1> {`${!params.id ? 'Create' : 'Update'}`} User </h1> <UserForm id={params.id} /> </div> ); export default ScreensUserForm; 复制代码
最终,咱们应用的目录结构会像下面这样:
src
├─ components
│ ├─ User
│ │ ├─ Form
│ │ │ ├─ Form.jsx
│ │ │ └─ Form.css
│ │ └─ List.jsx
│ └─ UI
│
└─ screens
├─ User
│ ├─ Form.jsx
│ └─ List.jsx
└─ Root.jsx
复制代码