美文网首页前端小栈前端魔法世界React.js
React项目结构和组件命名最佳实践

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

作者: _TheSpecialOne | 来源:发表于2019-05-22 23:40 被阅读32次
    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
    

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

    • 主观规则 - 你并没有很清晰的规则区分什么是容器组件, 什么是展示组件
      彼此之间的差异是很主观的,当你在一个团队里面,很难让所有的开发者都有相同的判断。

    • 没有考虑组件的动态性 - 即使当你确定某个组件是容器组件,也很容易在项目生命周期中变更成为展示组件,并迫使你将它从容器组件目录移动到展示组件目录,反之亦然。

    • 允许组件重名 - 在一个应用中,组件应该具有声明性和唯一的命名以避免混淆每个组件的职责。然而,上述的方法可产生漏洞让两个组件重名,一个代表容器组件,一个代表展示组件

    • 效率缺失 - 你需要不断的切换containerscomponents目录,甚至你只是在开发同一个功能点,通常一个功能点会需要两种类型的组件。

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

    想象一下在你的应用内部有一个 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
    

    重述

    • 展示组件(Presentational component)和容器组件(container component)都应该保存于 src/components目录
    • 模块/功能分类组件,如User/List
    • 将通用组件存于src/components/UI目录
    • 保持界面(screens)简单,结构和代码最小
    • 根据路由定义为screens分组,路由 /user/list对应的界面(screen)位于/src/screens/User/List.jsx
    • 组件依据和componentsrc相对路径来命名,鉴于此,位于src/components/User/List.jsx应该命名为UserList,位于src/screens/User/List应该命名为ScreensUserList
    • 组件文件名和所在文件夹重名时,组件名不需再重复,位于src/components/User/List/List.jsx的组件应该命名为UserList而不是UserListList

    最后

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

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

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

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

    相关文章

      网友评论

        本文标题:React项目结构和组件命名最佳实践

        本文链接:https://www.haomeiwen.com/subject/uqddzqtx.html