單項(xiàng)數(shù)據(jù)類框架始祖Flux
Flux的目的是用來替代Backbone.js惋鸥、Ember.js等MVC框架芒珠。
MVC框架將應(yīng)用分為三個(gè)部分:
Model(模型) 負(fù)責(zé)管理數(shù)據(jù)访雪,大部分業(yè)務(wù)邏輯放在Model中雏胃;
View(視圖) 負(fù)責(zé)渲染用戶界面,避免在View中涉及業(yè)務(wù)邏輯宪祥;
Controller(控制器) 負(fù)責(zé)接受用戶的輸入聂薪,根據(jù)用戶的輸入調(diào)用對應(yīng)的Model部分的邏輯,把產(chǎn)生的數(shù)據(jù)交給View部分品山,讓View渲染出必要的輸出;
MVC框架的缺點(diǎn)
MVC框架提出的數(shù)據(jù)流很理想烤低,但是在實(shí)際框架視線中肘交,總是允許View和Model直接通信,從而出現(xiàn)以下情況扑馁。
在MVC框架中涯呻,系統(tǒng)能提供什么的服務(wù),通過Controller 暴漏函數(shù)來實(shí)現(xiàn)腻要。每增加一個(gè)功能复罐,Controller就要增加一個(gè)函數(shù)。
Flux包含四個(gè)部分:
Dispatcher 處理動作分發(fā)雄家,維持Store之間的依賴關(guān)系效诅;
Store 負(fù)責(zé)存儲數(shù)據(jù)和處理數(shù)據(jù)邏輯;
Action 驅(qū)動 Dispatcher 的 JavaScript 對象趟济;
View 視圖部分乱投,負(fù)責(zé)顯示用戶界面;
在Flux中 Dispatcher 始終只需要暴漏一個(gè)函數(shù)Dispatch顷编,當(dāng)需要增加新的功能時(shí)戚炫,只用增加一種新的Action類型,Dispatcher 對外接口不改變媳纬。
Redux的基本原則
Flux的基本原則是“單向數(shù)據(jù)流”双肤,Redux在此強(qiáng)調(diào)三個(gè)基本原則
1.唯一數(shù)據(jù)源
應(yīng)用的狀態(tài)應(yīng)該只存儲在唯一一個(gè)Store上施掏,這個(gè)唯一Store上的狀態(tài),是一個(gè)樹形的對象茅糜,每個(gè)組件往往只是樹形象上的一部分?jǐn)?shù)據(jù)七芭,如何設(shè)計(jì)Store上狀態(tài)的結(jié)構(gòu),就死Redux應(yīng)用的核心問題限匣。
2.保持狀態(tài)只讀
不能直接修改狀態(tài)抖苦,要修改Store的狀態(tài),必須通過派發(fā) action 對象完成米死。
3.數(shù)據(jù)改變只能通過純函數(shù)完成
純函數(shù)就是Reducer锌历,reducer接受兩個(gè)參數(shù),第一個(gè)參數(shù)state是當(dāng)前的狀態(tài)峦筒,第二個(gè)參數(shù)action是接受到的action對象究西。
Action 是把數(shù)據(jù)從應(yīng)用(譯者注:這里之所以不叫 view 是因?yàn)檫@些數(shù)據(jù)有可能是服務(wù)器響應(yīng),用戶輸入或其它非 view 的數(shù)據(jù) )傳到 store 的有效載荷物喷。它是 store 數(shù)據(jù)的唯一來源卤材。一般來說會通過store.dispatch() 將 action 傳到 store。
約定:action 內(nèi)必須使用一個(gè)字符串類型的 type 字段來表示將要執(zhí)行的動作峦失。多數(shù)情況下扇丛,type 會被定義成字符串常量。當(dāng)應(yīng)用規(guī)模越來越大時(shí)尉辑,建議使用單獨(dú)的模塊或文件來存放 action帆精。除了 type 字段外,action 對象的結(jié)構(gòu)完全由你自己決定隧魄。
Action 創(chuàng)建函數(shù)
Action 創(chuàng)建函數(shù) 就是生成 action 的方法卓练。“action” 和 “action 創(chuàng)建函數(shù)” 這兩個(gè)概念很容易混在一起购啄,使用時(shí)最好注意區(qū)分襟企。在 Redux 中的 action 創(chuàng)建函數(shù)只是簡單的返回一個(gè) action:
function add(text) {
return {
type: TYPE_NAME,
text
}
}
Redux 中只需把 action 創(chuàng)建函數(shù)的結(jié)果傳給 dispatch() 方法即可發(fā)起一次 dispatch 過程
dispatch(addTodo(text))
Reducers 指定了應(yīng)用狀態(tài)的變化如何響應(yīng)actions并發(fā)送到 store 的,actions 只是描述了有事情發(fā)生了這一事實(shí)狮含,并沒有描述應(yīng)用如何更新 state顽悼。reducer 就是一個(gè)純函數(shù),接收舊的 state 和 action几迄,返回新的 state表蝙。
(previousState, action) => newState
reducer規(guī)范:(不能做)
修改傳入?yún)?shù);
執(zhí)行有副作用的操作乓旗,如 API 請求和路由跳轉(zhuǎn)府蛇;
調(diào)用非純函數(shù),如 Date.now() 或 Math.random()屿愚。
Store 就是把a(bǔ)ction汇跨、reducer聯(lián)系到一起的對象。Store 有以下職責(zé):
- 維持應(yīng)用的 state穷遂;
- 提供 getState() 方法獲取 state函匕;
- 提供 dispatch(action) 方法更新 state;
- 通過 subscribe(listener) 注冊監(jiān)聽器;
- 通過 subscribe(listener) 返回的函數(shù)注銷監(jiān)聽器蚪黑。
注意 Redux 應(yīng)用只有一個(gè)單一的 store盅惜。當(dāng)需要拆分?jǐn)?shù)據(jù)處理邏輯時(shí)忌穿,你應(yīng)該使用 reducer 組合而不是創(chuàng)建多個(gè) store抒寂。
詳細(xì)使用方法請看Redux中文文檔
容器組件和展示組件
承擔(dān)第一個(gè)任務(wù)的組件,也就是負(fù)責(zé)和 Redux Store 打交道的組件屈芜,處于外層,被稱為容器組件眠寿;對于承擔(dān)第二個(gè)任務(wù)的組件,也就是專心負(fù)責(zé)渲染界面的組件盯拱,處于內(nèi)層盒发,叫做展示組件。
展示組件是一個(gè)純函數(shù)蝶防,根據(jù) props 產(chǎn)生結(jié)果甚侣。
組件Context
在一個(gè)應(yīng)用中,不同組件都會調(diào)用全局的Store间学,在組件中直接導(dǎo)入Store不利于組件復(fù)用殷费,應(yīng)該只在最頂層調(diào)用一次Store,將Store從上層組件傳遞給子組件低葫,如果用props傳遞详羡,所有的組件props都要增加對這個(gè)支持,很麻煩嘿悬。React提供了一個(gè)Content的功能实柠,能解決這個(gè)問題。
Context(上下文環(huán)境)善涨,讓一個(gè)樹狀組件上所有組件都能訪問一個(gè)共同的對象窒盐,需要上級組件和下級組件配合草则。
//定義在src文件夾下,是一個(gè)通用的 context 提供者
import React, {Component} from 'react'
import PropTypes from 'prop-types'
class Provider extends Component {
getChildContext() {
//返回的代表Context的對象
return {
store: this.props.store
};
}
render() {
//簡單的把子組件渲染出來代表Provider標(biāo)簽之間的組件
return this.props.children;
}
}
//為了Provide比較通用蟹漓,所以store從外部傳遞進(jìn)來
//為了讓Provider能夠被React認(rèn)可為一個(gè)Context提供者炕横,需要指定Provider的childContextTypes
Provider.childContextTypes = {
store: PropTypes.object
};
export default Provider;
每個(gè)React組件都有一個(gè)特殊屬性 children,代表的是子組件葡粒。
子組件中對context的使用
className.contextTypes = {
store: PropTypes.object
}
在子組件中對store的訪問通過 this.context.store 完成份殿。由于自定義了構(gòu)造函數(shù),所以構(gòu)造函數(shù)要添加 context 參數(shù)
constructor(props, context) {
super(props, context);
//es6 super(...arguments)
}
context的使用:必須謹(jǐn)慎使用嗽交,只有對那些每個(gè)組件都可能使用卿嘲,但是中間組件又不可能使用的對象才必須使用Context。
具體使用方法請參考聊一聊我對 React Context 的理解以及應(yīng)用-張國鈺
后記 將組件拆分為容器組件和展示組件轮纫,以及利用 React 的 Context 提供一個(gè)所有組件都可以直接訪問的 Context腔寡,這兩種方法都有自己的套路來改進(jìn) React 應(yīng)用,可以將這兩種方法的套路部分提取出來掌唾,已經(jīng)有一個(gè)庫實(shí)現(xiàn)放前,就是 react-redux