React構(gòu)建簡(jiǎn)潔強(qiáng)大可擴(kuò)展的前端項(xiàng)目架構(gòu)
引言
React技術(shù)棧的一大優(yōu)勢(shì)在于 —— 社區(qū)繁榮,你業(yè)務(wù)中需要實(shí)現(xiàn)的功能基本都能找到對(duì)應(yīng)的開源庫。
但繁榮也有不好的一面 —— 要實(shí)現(xiàn)同樣的功能,有太多選擇,到底選哪個(gè)?
本文要介紹一個(gè)12.7k的開源項(xiàng)目 —— Bulletproof React
這個(gè)項(xiàng)目為構(gòu)建簡(jiǎn)潔、強(qiáng)大、可擴(kuò)展的前端項(xiàng)目架構(gòu)的方方面面給出了建議。
Bulletproof React是什么
Bulletproof React與我們常見的腳手架(比如CRA)不同,后者的作用是根據(jù)模版創(chuàng)建一個(gè)新項(xiàng)目。
而前者包含一個(gè)完整的React全棧論壇項(xiàng)目:

作者通過這個(gè)項(xiàng)目舉例,展示了與項(xiàng)目架構(gòu)相關(guān)的13個(gè)方面的內(nèi)容,比如:
- 文件目錄該如何組織
- 工程化配置有什么推薦
- 寫業(yè)務(wù)組件時(shí)該怎么規(guī)范
- 怎么做狀態(tài)管理
API層如何設(shè)計(jì)- 等等......

限于篇幅有限,本文介紹其中部分觀點(diǎn)。
不知道這些觀點(diǎn)你是否認(rèn)同呢?
文件目錄如何組織
項(xiàng)目推薦如下目錄形式:
src | +-- assets # 靜態(tài)資源 | +-- components # 公共組件 | +-- config # 全局配置 | +-- features # 特性 | +-- hooks # 公用hooks | +-- lib # 二次導(dǎo)出的第三方庫 | +-- providers # 應(yīng)用中所有providers | +-- routes # 路由配置 | +-- stores # 全局狀態(tài)stores | +-- test # 測(cè)試工具、mock服務(wù)器 | +-- types # 全局類型文件 | +-- utils # 通用工具函數(shù)
其中,features目錄與components目錄的區(qū)別在于:
components存放全局公用的組件,而features存放業(yè)務(wù)相關(guān)特性。
比如我要開發(fā)評(píng)論模塊,評(píng)論作為一個(gè)特性,與他相關(guān)的所有內(nèi)容都存在于features/comments目錄下。
評(píng)論模塊中需要輸入框,輸入框這個(gè)通用組件來自于components目錄。
所有特性相關(guān)的內(nèi)容都會(huì)收斂到features目錄下,具體包括:
src/features/xxx-feature | +-- api # 與特性相關(guān)的請(qǐng)求 | +-- assets # 與特性相關(guān)的靜態(tài)資源 | +-- components # 與特性相關(guān)的組件 | +-- hooks # 與特性相關(guān)的hooks | +-- routes # 與特性相關(guān)的路由 | +-- stores # 與特性相關(guān)的狀態(tài)stores | +-- types # 與特性相關(guān)的類型申明 | +-- utils # 與特性相關(guān)的工具函數(shù) | +-- index.ts # 入口
特性導(dǎo)出的所有內(nèi)容只能通過統(tǒng)一的入口調(diào)用,比如:
import { CommentBar } from "@/features/comments"
而不是:
import { CommentBar } from "@/features/comments/components/CommentBar
這可以通過配置ESLint實(shí)現(xiàn):
{
rules: {
'no-restricted-imports': [
'error',
{
patterns: ['@/features/*/*'],
},
],
// ...其他配置
}
}
相比于將特性相關(guān)的內(nèi)容都以扁平的形式存放在全局目錄下(比如將特性的hooks存放在全局hooks目錄),以features目錄作為相關(guān)代碼的集合能夠有效防止項(xiàng)目體積增大后代碼組織混亂的情況。
怎么做狀態(tài)管理
項(xiàng)目中并不是所有狀態(tài)都需要保存在中心化的store中,需要根據(jù)狀態(tài)類型區(qū)別對(duì)待。
組件狀態(tài)
對(duì)于組件的局部狀態(tài),如果只有組件自身以及他的子孫組件需要這部分狀態(tài),那么可以用useState或useReducer保存他們。
應(yīng)用狀態(tài)
與應(yīng)用交互相關(guān)的狀態(tài),比如打開彈窗、通知、改變黑夜模式等,應(yīng)該遵循將狀態(tài)盡可能靠近使用他的組件的原則,不要什么狀態(tài)都定義為全局狀態(tài)。
以Bulletproof React中的示例項(xiàng)目舉例,首先定義通知相關(guān)的狀態(tài):
// 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),
})),
}));
再在任何使用通知相關(guān)的狀態(tài)的地方引用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>
);
};
這里使用的狀態(tài)管理工具是zustand,除此之外還有很多可選方案:
context + hooks
redux + redux toolkit
mobx
constate
jotai
recoil
xstate
這些方案各有特點(diǎn),但他們都是為了處理應(yīng)用狀態(tài)。
服務(wù)端緩存狀態(tài)
對(duì)于從服務(wù)端請(qǐng)求而來,緩存在前端的數(shù)據(jù),雖然可以用上述處理應(yīng)用狀態(tài)的工具解決,但服務(wù)端緩存狀態(tài)相比于應(yīng)用狀態(tài),還涉及到緩存失效、序列化數(shù)據(jù)等問題。
所以最好用專門的工具處理,比如:
react-query - REST + GraphQL
swr - REST + GraphQL
apollo client - GraphQL
urql - GraphQl
表單狀態(tài)
表單數(shù)據(jù)需要區(qū)分受控與非受控,表單本身還有很多邏輯需要處理(比如表單校驗(yàn)),所以也推薦用專門的庫處理這部分狀態(tài),比如:
React Hook Form
Formik
React Final Form
URL狀態(tài)
URL狀態(tài)包括:
url params (/app/${dynamicParam})
query params (/app?dynamicParam=1)
這部分狀態(tài)通常是路由庫處理,比如react-router-dom。
總結(jié)
本文節(jié)選了部分Bulletproof React中推薦的方案,有沒有讓你認(rèn)可的觀點(diǎn)呢?
以上就是React構(gòu)建簡(jiǎn)潔強(qiáng)大可擴(kuò)展的前端項(xiàng)目架構(gòu)的詳細(xì)內(nèi)容,更多關(guān)于React前端項(xiàng)目架構(gòu)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React中實(shí)現(xiàn)父組件調(diào)用子組件的三種方法
在React中,組件之間的通信是一個(gè)常見的需求,有時(shí),我們需要從父組件調(diào)用子組件的方法,這可以通過幾種不同的方式實(shí)現(xiàn),需要的朋友可以參考下2024-04-04
react中如何對(duì)自己的組件使用setFieldsValue
react中如何對(duì)自己的組件使用setFieldsValue問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
使用 React 和 Threejs 創(chuàng)建一個(gè)VR全景項(xiàng)目的過程詳解
這篇文章主要介紹了使用 React 和 Threejs 創(chuàng)建一個(gè)VR全景項(xiàng)目的過程詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04
React-Router v6實(shí)現(xiàn)頁面級(jí)按鈕權(quán)限示例詳解
這篇文章主要介紹了使用 reac+reactRouter來實(shí)現(xiàn)頁面級(jí)的按鈕權(quán)限功能,這篇文章分三部分,實(shí)現(xiàn)思路、代碼實(shí)現(xiàn)、踩坑記錄,有需要的朋友可以借鑒參考下,希望能夠有所幫助2023-10-10
React踩坑之a(chǎn)ntd輸入框rules中的required=true問題
這篇文章主要介紹了React踩坑之a(chǎn)ntd輸入框rules中的required=true問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
react-redux及redux狀態(tài)管理工具使用詳解
Redux是為javascript應(yīng)用程序提供一個(gè)狀態(tài)管理工具集中的管理react中多個(gè)組件的狀態(tài)redux是專門作狀態(tài)管理的js庫(不是react插件庫可以用在其他js框架中例如vue,但是基本用在react中),這篇文章主要介紹了react-redux及redux狀態(tài)管理工具使用詳解,需要的朋友可以參考下2023-01-01

