React提供更優(yōu)雅的前端代碼書寫方式和更優(yōu)的界面更新機(jī)制婚夫,redux提供了組件和業(yè)務(wù)分離的解決方案,saga或thunk基于redux提供異步業(yè)務(wù)實(shí)現(xiàn)方案署鸡。
圖中的Middleware工作于redux內(nèi)部案糙,介于action和reducer之間,而saga只是某一種Middleware靴庆。
saga工作于action和reducer之間时捌。如果按照原始的redux工作流程,當(dāng)組件中產(chǎn)生一個(gè)action后會(huì)直接觸發(fā)reducer修改state炉抒;而往往實(shí)際中奢讨,組件中發(fā)生的action后,在進(jìn)入reducer之前需要完成一個(gè)異步任務(wù)焰薄,原生的redux不支持這種操作拿诸。
不過,實(shí)現(xiàn)異步操作的整體步驟是很清晰的:action被觸發(fā)后塞茅,首先執(zhí)行異步任務(wù)亩码,待完成后再將這個(gè)action交給reducer。說到這里野瘦,thunk和saga的達(dá)到的效果是一致的描沟,差別在于對action的處理完全不一樣。
thunk采用的是擴(kuò)展action的方式:使得redux的store能dispatch的內(nèi)容從普通對象擴(kuò)展到函數(shù)鞭光。
const mapDispatchToProps = dispatch => {
return {
delayData: () => dispatch(delayData)
};
};
const delayDataAction = {
type: DALAY_DATA
};
export const delayData = dispatch => {
delayDataTask(dispatch, delayDataAction);
};
saga采用的方案更接近于redux的全局思想吏廉,使用方式和thunk有很大不同:
saga需要一個(gè)全局監(jiān)聽器(watcher saga),用于監(jiān)聽組件發(fā)出的action惰许,將監(jiān)聽到的action轉(zhuǎn)發(fā)給對應(yīng)的接收器(worker saga)席覆,再由接收器執(zhí)行具體任務(wù),任務(wù)執(zhí)行完后啡省,再發(fā)出另一個(gè)action交由reducer修改state娜睛,所以這里必須注意:watcher saga監(jiān)聽的action和對應(yīng)worker saga中發(fā)出的action不能是同一個(gè),否則造成死循環(huán)卦睹。
在saga中畦戒,全局監(jiān)聽器和接收器都使用Generator函數(shù)和saga自身的一些輔助函數(shù)實(shí)現(xiàn)對整個(gè)流程的管控。
整個(gè)流程可以簡單描述為:
Component —> Action1 —> Watcher Saga —> Worker Saga —> Action2 —> Reducer —> Component
相比thunk结序,saga是多了一個(gè)action的障斋,因?yàn)閟aga是將業(yè)務(wù)的觸發(fā)(watcher saga)和業(yè)務(wù)的執(zhí)行(worker saga)分開描述的。
const getCountAction = (param) => {
return {
type: 'count',
param
}
}
const getCountFinishAction = (param) => {
return {
type: 'countFinish',
param
}
}
const mapDispatchToProps = (dispatch) => {
return {
count: (param) => {
return dispatch(getCountAction(param))//這里dispatch的參數(shù)是個(gè)普通對象
}
}
}
//一個(gè)普通函數(shù) 將當(dāng)前計(jì)數(shù)+1
function counter(curCount) {
return ++curCount
}
//worker saga 這里其實(shí)是有接收參數(shù)的 這個(gè)參數(shù)action就是redux執(zhí)行dispatch方法中的參數(shù)
function* counterSaga(action) {
console.log(`counterSaga receive the action ${action.type}`)
//接受到這個(gè)action之后 執(zhí)行saga的call方法 call方法會(huì)執(zhí)行counter函數(shù) 并將當(dāng)前計(jì)數(shù)作為它的參數(shù)
const countResult = yield call(counter, action.param)
//counter執(zhí)行的結(jié)果作為另一個(gè)action的參數(shù)
yield put(getCountFinishAction(countResult))
}
//watcher saga 監(jiān)聽takeEvery這個(gè)action 并執(zhí)行helloSaga
function* watchIncrementAsync() {
console.log('watcher saga is listening to action')
yield* takeEvery('count', counterSaga);
}
//執(zhí)行watcher saga 這樣就可以一直監(jiān)聽?wèi)?yīng)用中的action了
sagaMiddleware.run(watchIncrementAsync)