對應(yīng)源碼項(xiàng)目地址
- 托管在 github 上的項(xiàng)目鏈接:https://github.com/uncleAndyChen/react-full-stack-learning
- 托管在 gitee 上的項(xiàng)目鏈接 :https://gitee.com/uncleAndyChen/react-full-stack-learning
能學(xué)到些啥窿冯?
作為學(xué)習(xí)的例子,只有三個(gè)頁面芙粱,但是麻雀雖小圣蝎,五臟俱全羡铲。通過該例子可以學(xué)習(xí)到以下知識(shí)點(diǎn):
- 項(xiàng)目的目錄結(jié)構(gòu)設(shè)計(jì)最佳實(shí)踐弧岳。
- 項(xiàng)目的 state 設(shè)計(jì)和模塊設(shè)計(jì)技巧譬正。
- 異步獲取 API 數(shù)據(jù),以及將獲取到的數(shù)據(jù)展示到頁面上盛撑。
三個(gè)核心頁面
- 登錄頁面碎节。
- 帖子列表頁面,僅展示帖子的基本信息抵卫。
- 帖子詳情頁面狮荔,展示帖子的詳細(xì)內(nèi)容,包括用戶的評論列表介粘。
測試賬號(hào)
- 該bbs內(nèi)置三個(gè)用戶
- tom
- jack
- steve
- 密碼都是:123456
state 設(shè)計(jì)
{
app: {
requestQuantity: 0, // 當(dāng)前應(yīng)用中正在進(jìn)行的 API 請求數(shù)
error: null // 應(yīng)用全局錯(cuò)誤信息
},
auth: {
userId: '59e5d570fe26fff867fc94c0', // 當(dāng)前登錄用戶的 id
username: 'jack' // 當(dāng)前登錄用戶的用戶名
},
ui: {
addDialogOpen: false, // 用于新增帖子的對話框的顯示狀態(tài)
editDialogOpen: false // 用于編輯帖子的對話框的顯示狀態(tài)
},
posts: {
allIds: [], // 維護(hù)數(shù)據(jù)的有序性
byId: {} // 根據(jù) id 獲取帖子的相關(guān)內(nèi)容
}
},
comments: {
byPost: {
'5c10b55d34ce789876fc00ed': [] // 帖子 id 與該帖子下的評價(jià) id 的映射殖氏。
},
byId: {} // 根據(jù)評論 id 獲取到的該條評論相關(guān)內(nèi)容。
}
},
users: {}
}
}
state 解讀
一共六個(gè)子 state
- app:記錄應(yīng)用業(yè)務(wù)狀態(tài)數(shù)據(jù)
- requestQuantity:當(dāng)前應(yīng)用中正在進(jìn)行的 API 請求數(shù)姻采。
- error:應(yīng)用全局錯(cuò)誤信息雅采。
- auth:登錄認(rèn)證狀態(tài)
- userId:當(dāng)前登錄用戶的 id。
- username:當(dāng)前登錄用戶的用戶名。
- ui:UI 狀態(tài)數(shù)據(jù)
- addDialogOpen:用于新增帖子的對話框的顯示狀態(tài)婚瓜。
- editDialogOpen:用于編輯帖子的對話框的顯示狀態(tài)宝鼓。
- posts:帖子列表
- allIds:保存帖子列表的 id,維護(hù)數(shù)據(jù)的有序性巴刻。
- byId:以帖子 id 為 key 的列表席函,每一個(gè)子項(xiàng)為帖子的相關(guān)內(nèi)容。
- comments
- byPost 保存以某一個(gè)帖子的 id 為 key 的冈涧、該帖子 id 下的評論列表 id茂附,即:帖子 id 與該帖子下的評價(jià) id 的映射。
- byId 與 posts 下的 byId 類似督弓,該項(xiàng)是以評論 id 為 key 的列表营曼,每一個(gè)子項(xiàng)為評論相關(guān)的內(nèi)容。
- users:當(dāng)前頁面相關(guān)的用戶信息列表
模塊設(shè)計(jì)
對應(yīng) state 的設(shè)計(jì)愚隧,模塊設(shè)計(jì)基本上也出來了蒂阱,除了對應(yīng)上面的六個(gè)子 state 都有相應(yīng)的模塊之外,還有一個(gè) Redux 模塊狂塘。
Redux 模塊录煤,位于 redux/modules 目錄下,各個(gè)功能相關(guān)的 reducer荞胡、action types妈踊、action creators 都定義到一個(gè)文件中。各個(gè)功能的 reducer 又通過 redux/modules/index.js 合并成一個(gè)根 reducer泪漂,以供 react-redux 創(chuàng)建 store 并進(jìn)行統(tǒng)一管理廊营。
運(yùn)行時(shí)數(shù)據(jù)
首頁,即帖子列表頁面
看一下運(yùn)行起來的 state萝勤,僅保留了兩篇帖子的數(shù)據(jù)露筒。
{
app: {
requestQuantity: 0,
error: null
},
auth: {
userId: '59e5d570fe26fff867fc94c0',
username: 'jack'
},
ui: {
addDialogOpen: false,
editDialogOpen: false
},
posts: {
allIds: [
'5c10b579a41380ad2bf95cfd',
'5c0f145bd76a23857943793c'
],
byId: {
'5c10b579a41380ad2bf95cfd': {
id: '5c10b579a41380ad2bf95cfd',
title: 'fs',
vote: 0,
updatedAt: '2018-12-12T07:16:10.863Z',
author: '59e5d570fe26fff867fc94c0'
},
'5c0f145bd76a23857943793c': {
id: '5c0f145bd76a23857943793c',
title: '',
vote: 0,
updatedAt: '2018-12-11T01:35:23.187Z',
author: '59e5d570fe26fff867fc94c0'
}
}
},
comments: {
byPost: {},
byId: {}
},
users: {
'59e5d22f6722f75272b3bbcf': {
id: '59e5d22f6722f75272b3bbcf',
username: 'tom'
},
'59e5d570fe26fff867fc94c0': {
id: '59e5d570fe26fff867fc94c0',
username: 'jack'
}
}
}
帖子詳情頁面
再看有三條評論的帖子,進(jìn)入詳情頁面時(shí)敌卓,state 的內(nèi)容慎式。帖子是 jack 發(fā)的,三條評論都是 tom 發(fā)的趟径,所以users有兩個(gè)用戶的信息瘪吏。
{
app: {
requestQuantity: 0,
error: null
},
auth: {
userId: '59e5d570fe26fff867fc94c0',
username: 'jack'
},
ui: {
addDialogOpen: false,
editDialogOpen: false
},
posts: {
allIds: [],
byId: {
'5c10b55d34ce789876fc00ed': {
id: '5c10b55d34ce789876fc00ed',
title: 'asd',
content: 'ads',
vote: 0,
updatedAt: '2018-12-12T07:14:37.395Z',
author: '59e5d570fe26fff867fc94c0'
}
}
},
comments: {
byPost: {
'5c10b55d34ce789876fc00ed': [
'5c1215a60dbbbd7c0a640389',
'5c12159eb50852373a0d1a9e',
'5c12151f9536aad30f94f59f'
]
},
byId: {
'5c1215a60dbbbd7c0a640389': {
id: '5c1215a60dbbbd7c0a640389',
content: 'sadf',
updatedAt: '2018-12-13T08:17:42.783Z',
author: '59e5d22f6722f75272b3bbcf'
},
'5c12159eb50852373a0d1a9e': {
id: '5c12159eb50852373a0d1a9e',
content: 'sad',
updatedAt: '2018-12-13T08:17:34.272Z',
author: '59e5d22f6722f75272b3bbcf'
},
'5c12151f9536aad30f94f59f': {
id: '5c12151f9536aad30f94f59f',
content: 'sa',
updatedAt: '2018-12-13T08:15:27.067Z',
author: '59e5d22f6722f75272b3bbcf'
}
}
},
users: {
'59e5d570fe26fff867fc94c0': {
id: '59e5d570fe26fff867fc94c0',
username: 'jack'
},
'59e5d22f6722f75272b3bbcf': {
id: '59e5d22f6722f75272b3bbcf',
username: 'tom'
}
}
}
對應(yīng)源碼結(jié)構(gòu)(模塊設(shè)計(jì))
源碼結(jié)構(gòu)與state設(shè)計(jì)是相輔相成的。
│ index.js
│
├─components // 全局通用組件
│ ├─Header
│ │ index.js
│ │ style.css
│ │
│ ├─Loading
│ │ index.js
│ │ style.css
│ │
│ └─ModalDialog
│ index.js
│ style.css
│
├─containers
│ ├─App
│ │ index.js
│ │
│ ├─Home
│ │ index.js
│ │
│ ├─Login
│ │ index.js
│ │ style.css
│ │
│ ├─Post
│ │ │ index.js // Post 容器組件
│ │ │ style.css
│ │ │
│ │ └─components // Post 專用組件
│ │ ├─CommentList
│ │ │ index.js
│ │ │ style.css
│ │ │
│ │ ├─CommentsView
│ │ │ index.js
│ │ │ style.css
│ │ │
│ │ ├─PostEditor
│ │ │ index.js
│ │ │ style.css
│ │ │
│ │ └─PostView
│ │ index.js
│ │ style.css
│ │
│ └─PostList
│ │ index.js // PostList 容器組件
│ │ style.css
│ │
│ └─components // PostList 專用組件
│ ├─PostItem
│ │ index.js
│ │ style.css
│ │
│ └─PostsView
│ index.js
│
├─images
│ like-default.png
│ like.png
│
├─redux
│ │ configureStore.js
│ │
│ └─modules
│ app.js
│ auth.js
│ comments.js
│ index.js // Redux 的根模塊舵抹,僅將其余模塊的 reducer 合并成一個(gè)根 reducer肪虎。
│ posts.js
│ ui.js
│ users.js
│
└─utils
AsyncComponent.js
connectRoute.js
date.js
request.js // 對 fetch 的封裝
SHA1.js
url.js // API 配置
關(guān)于目錄結(jié)構(gòu)設(shè)計(jì)的最佳實(shí)踐,請看:React+Redux工程目錄結(jié)構(gòu)惧蛹,最佳實(shí)踐
redux 模塊
redux 模塊扇救,指的是在 redux/modules
下定義的模塊刑枝。
一個(gè) redux 模塊,不僅包含 action types迅腔、action creators装畅、reducers,還包含從該模塊獲取 state 數(shù)據(jù)的 selectors 函數(shù)沧烈。
selectors 函數(shù)的使命:
- 封裝:對外提供數(shù)據(jù)接口掠兄,外部調(diào)用者不需要知道內(nèi)部實(shí)現(xiàn)細(xì)節(jié),也不用關(guān)心內(nèi)部 state 的具體結(jié)構(gòu)锌雀。
- 解耦:內(nèi)部 state 結(jié)構(gòu)如果有變化蚂夕,修改對外接口即可,不會(huì)影響到外部調(diào)用方腋逆,降低模塊間依賴關(guān)系婿牍,最大限度解耦。
全局 selector 函數(shù)
當(dāng)需要從多個(gè)模塊的 state 中獲取數(shù)據(jù)時(shí)惩歉,最好的做法等脂,是在 redux/module/index.js
文件中定義全局 selector 函數(shù),該 selector 再通過各個(gè)模塊的 selector 獲取需要的數(shù)據(jù)撑蚌。這樣上遥,容器組件通過調(diào)用全局 selector 函數(shù),可以非常便利地對全局?jǐn)?shù)據(jù)進(jìn)行處理争涌。