前端小栈前端魔法世界React.js

React项目结构和组件命名最佳实践

2019-05-22  本文已影响32人  _TheSpecialOne
image.png

阅读该文章只需要8分钟

React 只是一个库,它并没有制定任何规则指导你该如何组织你的项目结构。这很好,因为它让我们可以自由尝试不同的组织方式,并选用更适合自己项目的方式。另一方面,这可能会给React开发初学者带来一些困惑。

我将在本文中展示一些我已经采用并扩展良好的方法。这些方法并没有重复造轮子,只是把圈内一些优秀实践组合在一起并加以改良。

记住: 这里提及的方法并不是一成不变的!你可以选用一些你觉得合理的方式
直接采用或者有所修改用于适应你自己的项目。

目录结构 (Folder Structure)

我经常碰到的一个问题是关于如何组织文件和文件夹的目录结构,在本文中,我先假定你使用 create-react-app 创建了一个最基本的目录结构,根目录下包含 .gitignore, package.json, README.md, yarn.lock 文件,以及public
src文件夹,项目的源代码将存放于src目录下。

目录结构如下图所示,本文将关注src目录,其余的一切将保持原样。

image.png

容器和组件 (Containers and Components)

您可能已经在某个项目的根目录中尝试过分离容器组件(Containers)演示组件(Presentation Components), 在src中,创建名为componentscontainers的目录。

src
├─ components 
└─ containers

然后,这样的方式会存在一些问题,如下所示:

这种方法还有一个变种,在模块内部保留这种分离。

想象一下在你的应用内部有一个 User 模块,你用两个目录分离你的组件

src
└─ User
  ├─ components
  └─ containers

上述方法最大限度的降低了你在项目中切换目录的麻烦,同时也衍生了一些杂音。项目中的模块越多,将会产生越多的containerscomponenets目录。

基于这些原因,当我们在讨论如何组织文件和目录时,用展示容器的概念来区分组件显得无关紧要,也就是说,我们可以把所有的组件都存放在components目录除了纯UI的组件。

即便展示组件容器在区分文件夹部分是无关紧要,但了解他们之间的差异依然非常重要,如果对于这个概念你有任何疑问,建议你阅读该文Presentational and Container Components.

代码分离与分组

components目录下,我们按照模块/功能方式分组文件。

对于 userCRUD , 我们将会有一个 User 模块, 结构如下所示:

└─ 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并存在相同目录。有没有一点眼熟的感觉,假如你了解google的前端框架

UI 组件

除了按模块分离组件之外,我们还在src/components中包含一个UI文件夹用于存放所以的公用组件

UI组件作为公用组件并不属于任何模块, 甚至是可以保存在开源的lib,因为它们并不包含任何应用的任何业务逻辑。例如Buttons, Inputs, Checkboxes, Selects, Modals, Data display elements 等等。

组件命名

我们讨论过了按照模块来组织和分离我们的组件,那么问题来了,如何为他们命名?

当我们讨论如何为组件命名,指的是给一个定义组件的类或者常量命名:
class MyComponent extends Component {
}
const MyComponent () => {};

为了方便查找和避免冲突,组件的命名必须在应用中清晰且唯一。

当我们使用React Dev Tools调试应用时,好的组件命名会变得非常方便,并在一些运行时错误(run-time errors)中清晰得指出错误的组件。

我们遵循path-based-component-naming原则,用基于src或者components目录的相对路径为组件命名,基本上一个组件的路径为 components/User/List.jsx将会被命名为UserList.

当文件名和所在目录重名时,我们不必再次重复,例如components/User/Form/Form.jsx命名为UserForm而不是UserFormForm

上述模式的一些好处如下:

搜索便利

假如你的编辑器支持模糊搜索,只需要搜索UserForm就可以找到你需要的文件:

image.png

假如你通过文件夹目录查找文件,根据组件名称可以很容易的定位:

image.png
避免文件与文件夹名称重复

按照这种模式,我们根据上下文命名文件。上一段提及的Form,我们知道它是一个User Form,既然它已经在User文件夹了,那么不必在文件名中重复出现User,只需命名为Form.jsx

当我最初使用React时,曾经使用全名(组件名)命名文件,这样导致同一名称多次重复出现(/screens/User/UserForm)并且引用路径变得过于冗长。

请看一下两种方式的不同:

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';

那再想象一下在这个文件中引用5倍甚至更多的依赖,吧

基于这个原因,我们推荐根据文件的上下文来命名文件,component则相对于componentssrc文件夹的位置来命名。

界面(Screens)

顾名思义,Screens就是我们在应用程序中界面。

user 的增删查改中,我们将会有 user 列表界面,新增user界面和修改user界面。

界面是你用组件为应用程序组合成的一个的页面,理想状况下,界面应该不包含任何的逻辑只是功能组件。

我们将界面(Screens)保存在src根目录下单独的文件夹中,它们按路由(route)定义分组而不是模块:

src
├─ components 
└─ screens
  └─ User
    ├─ Form.jsx
    └─ List.jsx

假设项目使用react-router,我们把Root.jsx保存在Screens文件夹中,并在其中定义所有的应用程序路由。

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;

请注意,我们将所有screens文件放在一个与路由同名的文件夹中,user/ ->User/。为每个父路由(parent route)创建一个文件夹,并在其中定义子路由(sub-routes)。在上面例子中,我们创建了User文件夹,并将ScreensUserListScreensUserForm保存在其中。在这种模式下,你通过url轻松找到每个路由对应的渲染界面(screen)

一个界面(screen)可以同时为多个路由(route)渲染, 如上述例子中ScreensUserForm同时为创建user和编辑user渲染。

你应该已经注意到所有的视图组件命名中都包含Screen前缀。当组件文件存放于components文件夹之外,我们应该用针对src的相对路径进行命名。在src/screens/User/List.jsx路径的组件应该命名为ScreensUserList.

创建Root.jsx后,我们的结构如下:

src
├─ components 
└─ screens
  ├─ User
  │ ├─ Form.jsx
  │ └─ List.jsx
  └─ Root.jsx

不要忘记在index.js中引用Root.jsx作为应用程序的根组件(root component)

如果你对怎样定义screen存在疑问,请看下面的user formscreen定义:

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

重述

最后

本文仅涵盖React项目结构的一些建议,Redux部分将在后续的文章中介绍。

感谢你的阅读,如有你什么想法请留言,我会很乐意阅读和回复。

如果本文对你有所帮助,请点赞或者推荐给你的同事和朋友阅读,不胜感谢。

原文地址: https://hackernoon.com/structuring-projects-and-naming-components-in-react-1261b6e18d76
感谢 Vinicius Dacal

上一篇下一篇

猜你喜欢

热点阅读