大家好,我卡颂。

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.tsexport 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.tsximport { 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 client - GraphQL
  • urql - GraphQl

表单状态

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

  • React Hook Form
  • Formik
  • React Final Form

URL状态

URL状态包含:

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

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

总结

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

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