dva 是一個(gè)基于 redux 和 redux-saga 的數(shù)據(jù)流方案,為了簡(jiǎn)化開發(fā)體驗(yàn)印荔,dva 還額外內(nèi)置了 react-router 和 fetch低葫,所以也可以理解為一個(gè)輕量級(jí)的應(yīng)用框架。
目前最流行的社區(qū) React 應(yīng)用架構(gòu)方案如下
1.路由:React-Router
2.架構(gòu):Redux
3.異步操作:Redux-saga
缺點(diǎn):要引入多個(gè)庫(kù)仍律,項(xiàng)目結(jié)構(gòu)復(fù)雜嘿悬。
dva 是將上面三個(gè) React 工具庫(kù)包裝在一起,簡(jiǎn)化了 API水泉,讓開發(fā) React 應(yīng)用更加方便和快捷善涨。
dva = React-Router + Redux + Redux-saga
一.Dva解決了React 沒(méi)有解決的問(wèn)題
React 本身只是一個(gè) DOM 的抽象層主到,使用組件構(gòu)建虛擬 DOM。
如果開發(fā)大應(yīng)用躯概,還需要解決2個(gè)問(wèn)題登钥。
·通信:組件之間如何通信
·數(shù)據(jù)流:數(shù)據(jù)如何和視圖如何串聯(lián)起來(lái)?路由和數(shù)據(jù)如何綁定娶靡?
1.通信問(wèn)題
組件會(huì)發(fā)生三種通信
(1)向子組件發(fā)消息
(2)向父組件發(fā)消息
(3)向其他組件發(fā)消息
React 只提供了一種通信手段:傳參牧牢。對(duì)于大應(yīng)用來(lái)說(shuō)很不方便。
react本身的傳參是子組件通過(guò)父組件傳入的函數(shù)姿锭,將自己的值再傳回父組件塔鳍。
2.數(shù)據(jù)流向:
數(shù)據(jù)的改變發(fā)生通常是通過(guò)用戶交互行為或者瀏覽器行為(如路由跳轉(zhuǎn)等)觸發(fā)的,當(dāng)此類行為會(huì)改變數(shù)據(jù)的時(shí)候可以通過(guò)?dispatch?發(fā)起一個(gè) action呻此,如果是同步行為會(huì)直接通過(guò)?Reducers?改變?State?轮纫,如果是異步行為會(huì)先觸發(fā)?Effects?然后流向?Reducers?最終改變?State。
二.dva僅有6個(gè)api焚鲜,對(duì)redux用戶尤其友好
// 1.創(chuàng)建應(yīng)用
const app = dva();
// 2.注冊(cè)插件
app.use(createLoading());
// 3.注冊(cè)model
app.model(model);
// 4.取消model注冊(cè)掌唾,清理reducers,effects和subscriptions
app.unmodel(namespace)
// 5.注冊(cè)路由
app.router(()=> );
// 6.啟動(dòng)應(yīng)用
app.start('#root');
在這6步當(dāng)中,dva完成了使用React解決view層忿磅、redux管理model糯彬、saga解決異步的主要功能。
三.Model
Model 是 dva 最重要的部分葱她,所有的應(yīng)用邏輯都定義在它上面撩扒,可以理解為 redux、react-redux吨些、redux-saga 的封裝搓谆。通常項(xiàng)目中一個(gè)模塊對(duì)應(yīng)一個(gè) model。
Model對(duì)象的屬性:
1.namespace
是該 Model 的命名空間豪墅,只能用字符串泉手,不支持通過(guò)?.?的方式創(chuàng)建多層命名空間。
2.state:
該 Model 當(dāng)前的數(shù)據(jù)狀態(tài)但校,直接決定了視圖層的輸出螃诅。
3.reducers:
Action處理器啡氢,處理同步操作状囱,可以看做是state的計(jì)算器,類似于redux中的reducer倘是,用來(lái)算出最新的state亭枷,是唯一可以修改state的地方,由action觸發(fā)搀崭,它有state和action兩個(gè)參數(shù)叨粘。
4.effects
Action處理器猾编,處理異步動(dòng)作,基于Redux-saga實(shí)現(xiàn)升敲。
內(nèi)部使用 yield 關(guān)鍵字答倡,標(biāo)識(shí)每一步的操作(不管是異步或同步)。
不能直接修改state驴党,由action觸發(fā)瘪撇,也可觸發(fā)action。
有action和effects兩個(gè)參數(shù)港庄,effects包含put倔既、call和select三個(gè)字段,put用于觸發(fā)action鹏氧,類似于dispatch渤涌,call用于調(diào)用異步處理邏輯,select用于從state中獲取數(shù)據(jù)把还。
5.subscriptions
用于訂閱一個(gè)數(shù)據(jù)源实蓬,然后根據(jù)需要 dispatch 相應(yīng)的 action。
相當(dāng)于一個(gè)監(jiān)聽(tīng)器吊履,可以監(jiān)聽(tīng)路由變化瞳秽,鼠標(biāo),鍵盤變化率翅,服務(wù)器連接變化练俐,狀態(tài)變化等,這樣就可以根據(jù)不同變化做相應(yīng)的處理冕臭,在這個(gè)subsription中的方法名是隨意定的腺晾,每次變化都會(huì)去調(diào)用里面的所有方法,所以需要加相應(yīng)的判斷辜贵。
subscriptions: {
? setup({dispatch,history }){?
? ? window.onresize =()=> {? ?
????????//當(dāng)瀏覽器的頁(yè)面的大小變化時(shí)觸發(fā)里面的dispatch方法
? ? ?????dispatch(type:? "save")
? ? }
? },
? onClick({dispatch }){
? ? document.addEventListener('click',()=> {
????????//當(dāng)鼠標(biāo)點(diǎn)擊時(shí)觸發(fā)里面的dispatch方法
? ? ?????dispatch(type: "save")
? ? })
? }
}
四.connect方法
寫完model和組件后悯蝉,需要將model和組件連接起來(lái)。dva提供了connect方法托慨,connect是一個(gè)函數(shù)鼻由,綁定State到View。connect后的組件可以獲取到dispatch和state厚棵。
import { connect } from "dva";?
class Counter extends Component {
? ? constructor(props){
? ? ? ? super(props)
? ? }?
? ? render(){
? ? ? ? return?<div>{this.props.example.initText}</div>
? ? }
}
const mapStateToProps =(state)=>{
? ? //這里的example表示后面用this.props.example獲取state(根節(jié)點(diǎn))中exmpale命名空間中的state所有的數(shù)據(jù)
? ? return {
? ? ? ? example:state.example,
? ? }
}
//把model層的數(shù)據(jù)傳遞到當(dāng)前組件
export default connect(mapStateToProps)(Counter)?
五.dispatch方法
dispatch是一個(gè)函數(shù)方法蕉世,用來(lái)將Action發(fā)送到Model。
dispatch({
? type: 'click-submit-button',
? payload: this.form.data
})
被connect的Component會(huì)自動(dòng)在props中擁有dispatch方法婆硬。
六.異步請(qǐng)求
dva集成了isomorphic-fetch用于處理異步請(qǐng)求狠轻,并且使用dva-cli初始化的項(xiàng)目中,已經(jīng)在./src/utils/request.js中對(duì)fetch進(jìn)行了簡(jiǎn)單的封裝彬犯,可以在這里根據(jù)服務(wù)端API的數(shù)據(jù)結(jié)構(gòu)進(jìn)行統(tǒng)一的錯(cuò)誤處理向楼。
最后千萬(wàn)注意:effects和reducers中的方法不能同名查吊,否則會(huì)產(chǎn)生死循環(huán)