乐趣区

关于前端:一个简洁强大可扩展的前端项目架构是什么样的

大家好,我卡颂。

React技术栈的一大劣势在于 —— 社区凋敝,你业务中须要实现的性能根本都能找到对应的开源库。

但凋敝也有不好的一面 —— 要实现同样的性能,有太多抉择,到底选哪个?

本文要介绍一个 12.7k 的开源我的项目 —— Bulletproof React

这个我的项目为构建 简洁、弱小、可扩大的前端我的项目架构 的方方面面给出了倡议。

欢送退出人类高质量前端框架群,带飞

Bulletproof React 是什么

Bulletproof React与咱们常见的脚手架(比方 CRA)不同,后者的作用是 依据模版创立一个新我的项目

而前者蕴含一个残缺的 React 全栈论坛我的项目:

作者通过这个我的项目举例,展现了与 我的项目架构 相干的 13 个方面的内容,比方:

  • 文件目录该如何组织
  • 工程化配置有什么举荐
  • 写业务组件时该怎么标准
  • 怎么做状态治理
  • API层如何设计
  • 等等 ……

限于篇幅无限,本文介绍其中局部观点。

不晓得这些观点你是否认同呢?

文件目录如何组织

我的项目举荐如下目录模式:

src
|
+-- assets            # 动态资源
|
+-- components        # 公共组件
|
+-- config            # 全局配置
|
+-- features          # 个性
|
+-- hooks             # 专用 hooks
|
+-- lib               # 二次导出的第三方库
|
+-- providers         # 利用中所有 providers
|
+-- routes            # 路由配置
|
+-- stores            # 全局状态 stores
|
+-- test              # 测试工具、mock 服务器
|
+-- types             # 全局类型文件
|
+-- utils             # 通用工具函数

其中,features目录与 components 目录的区别在于:

components寄存全局专用的组件,而 features 寄存 业务相干个性

比方我要开发 评论 模块,评论 作为一个个性,与他相干的所有内容都存在于 features/comments 目录下。

评论 模块中须要输入框,输入框这个通用组件来自于 components 目录。

所有 个性相干 的内容都会收敛到 features 目录下,具体包含:

src/features/xxx-feature
|
+-- api         # 与个性相干的申请
|
+-- assets      # 与个性相干的动态资源
|
+-- components  # 与个性相干的组件
|
+-- hooks       # 与个性相干的 hooks
|
+-- routes      # 与个性相干的路由
|
+-- stores      # 与个性相干的状态 stores
|
+-- types       # 与个性相干的类型申明
|
+-- utils       # 与个性相干的工具函数
|
+-- index.ts    # 入口

个性导出的所有内容只能通过对立的入口调用,比方:

import {CommentBar} from "@/features/comments"

而不是:

import {CommentBar} from "@/features/comments/components/CommentBar

这能够通过配置 ESLint 实现:

{
  rules: {
    'no-restricted-imports': [
      'error',
      {patterns: ['@/features/*/*'],
      },
    ],
    // ... 其余配置
  }
}

相比于将 个性相干的内容 都以 扁平的模式 寄存在全局目录下(比方将个性的 hooks 寄存在全局 hooks 目录),以 features 目录作为 相干代码的汇合 可能无效避免我的项目体积增大后代码组织凌乱的状况。

怎么做状态治理

我的项目中并不是所有状态都须要保留在 中心化的 store中,须要依据状态类型区别对待。

组件状态

对于组件的部分状态,如果只有组件本身以及他的子孙组件须要这部分状态,那么能够用 useStateuseReducer保留他们。

利用状态

与利用交互相干的状态,比方 关上弹窗 告诉 扭转黑夜模式 等,应该遵循 将状态尽可能凑近应用他的组件 的准则,不要什么状态都定义为 全局状态

Bulletproof React 中的示例我的项目举例,首先定义 告诉相干的状态

// bulletproof-react/src/stores/notifications.ts
export const useNotificationStore = create<NotificationsStore>((set) => ({notifications: [],
  addNotification: (notification) =>
    set((state) => ({notifications: [...state.notifications, { id: nanoid(), ...notification }],
    })),
  dismissNotification: (id) =>
    set((state) => ({notifications: state.notifications.filter((notification) => notification.id !== id),
    })),
}));

再在任何应用 告诉相干的状态 的中央援用useNotificationStore,比方:

// bulletproof-react/src/components/Notifications/Notifications.tsx
import {useNotificationStore} from '@/stores/notifications';

import {Notification} from './Notification';

export const Notifications = () => {const { notifications, dismissNotification} = useNotificationStore();

  return (
    <div
    >
      {notifications.map((notification) => (
        <Notification
          key={notification.id}
          notification={notification}
          onDismiss={dismissNotification}
        />
      ))}
    </div>
  );
};

这里应用的状态管理工具是zustand,除此之外还有很多可选计划:

  • context + hooks
  • redux + redux toolkit
  • mobx
  • constate
  • jotai
  • recoil
  • xstate

这些计划各有特点,但他们都是为了解决 利用状态

服务端缓存状态

对于从服务端申请而来,缓存在前端的数据,尽管能够用上述解决 利用状态 的工具解决,但 服务端缓存状态 相比于 利用状态 ,还波及到 缓存生效 序列化数据 等问题。

所以最好用专门的工具解决,比方:

  • react-query - REST + GraphQL
  • swr - REST + GraphQL
  • apollo clientGraphQL
  • urqlGraphQl

表单状态

表单数据须要辨别 受控 非受控 ,表单自身还有很多逻辑须要解决(比方 表单校验),所以也举荐用专门的库解决这部分状态,比方:

  • React Hook Form
  • Formik
  • React Final Form

URL 状态

URL状态包含:

  • url params (/app/${dynamicParam})
  • query params (/app?dynamicParam=1)

这部分状态通常是路由库解决,比方react-router-dom

总结

本文节选了局部 Bulletproof React 中举荐的计划,有没有让你认可的观点呢?

欢送在评论区交换我的项目架构中的最佳实际。

退出移动版