一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - 編程技術 - 深入理解 Redux 數據流和異步過程管理

深入理解 Redux 數據流和異步過程管理

2021-09-26 22:51神光的編程秘籍神說要有光zxg 編程技術

前端框架實現了數據驅動視圖變化的功能,我們用 template 或者 jsx 描述好了數據和視圖的綁定關系,然后就只需要關心數據的管理了。

深入理解 Redux 數據流和異步過程管理

前端框架的數據流

前端框架實現了數據驅動視圖變化的功能,我們用 template 或者 jsx 描述好了數據和視圖的綁定關系,然后就只需要關心數據的管理了。

數據在組件和組件之間、組件和全局 store 之間傳遞,叫做前端框架的數據流。

一般來說,除了某部分狀態數據是只有某個組件關心的,我們會把狀態數據放在組件內以外,業務數據、多個組件關心的狀態數據都會放在 store 里面。組件從 store 中取數據,當交互的時候去通知 store 改變對應的數據。

這個 store 不一定是 redux、mobox 這些第三方庫,其實 react 內置的 context 也可以作為 store。但是 context 做為 store 有一個問題,任何組件都能從 context 中取出數據來修改,那么當排查問題的時候就特別困難,因為并不知道是哪個組件把數據改壞的,也就是數據流不清晰。

正是因為這個原因,我們幾乎見不到用 context 作為 store,基本都是搭配一個 redux。

所以為什么 redux 好呢?第一個原因就是數據流清晰,改變數據有統一的入口。

深入理解 Redux 數據流和異步過程管理

組件里都是通過 dispatch 一個 action 來觸發 store 的修改,而且修改的邏輯都是在 reducer 里面,組件再監聽 store 的數據變化,從中取出最新的數據。

這樣數據流動是單向的,清晰的,很容易管理。

這就像為什么我們在公司里想要什么權限都要走審批流,而不是直接找某人,一樣的道理。集中管理流程比較清晰,而且還可以追溯。

異步過程的管理

很多情況下改變 store 數據都是一個異步的過程,比如等待網絡請求返回數據、定時改變數據、等待某個事件來改變數據等,那這些異步過程的代碼放在哪里呢?

組件?

放在組件里是可以,但是異步過程怎么跨組件復用?多個異步過程之間怎么做串行、并行等控制?

所以當異步過程比較多,而且異步過程與異步過程之間也不獨立,有串行、并行、甚至更復雜的關系的時候,直接把異步邏輯放組件內不行。

不放組件內,那放哪呢?

redux 提供的中間件機制是不是可以用來放這些異步過程呢?

redux 中間件

先看下什么是 redux 中間件:

redux 的流程很簡單,就是 dispatch 一個 action 到 store, reducer 來處理 action。那么如果想在到達 store 之前多做一些處理呢?在哪里加?

改造 dispatch!中間件的原理就是層層包裝 dispatch。

下面是 applyMiddleware 的源碼,可以看到 applyMiddleware 就是對 store.dispatch 做了層層包裝,最后返回修改了 dispatch 之后的 store。

  1. functionapplyMiddleware(middlewares){
  2. letdispatch=store.dispatch
  3. middlewares.forEach(middleware=>
  4. dispatch=middleware(store)(dispatch)
  5. )
  6. return{...store,dispatch}
  7. }

所以說中間件最終返回的函數就是處理 action 的 dispatch:

  1. functionmiddlewareXxx(store){
  2. returnfunction(next){
  3. returnfunction(action){
  4. //xx
  5. };
  6. };
  7. };
  8. }

中間件會包裝 dispatch,而 dispatch 就是把 action 傳給 store 的,所以中間件自然可以拿到 action、拿到 store,還有被包裝的 dispatch,也就是 next。

比如 redux-thunk 中間件的實現:

  1. functioncreateThunkMiddleware(extraArgument){
  2. return({dispatch,getState})=>next=>action=>{
  3. if(typeofaction==='function'){
  4. returnaction(dispatch,getState,extraArgument);
  5. }
  6. returnnext(action);
  7. };
  8. }
  9. constthunk=createThunkMiddleware();

它判斷了如果 action 是一個函數,就執行該函數,并且把 store.dispath 和 store.getState 傳進去,否則傳給內層的 dispatch。

通過 redux-thunk 中間件,我們可以把異步過程通過函數的形式放在 dispatch 的參數里:

  1. constlogin=(userName)=>(dispatch)=>{
  2. dispatch({type:'loginStart'})
  3. request.post('/api/login',{data:userName},()=>{
  4. dispatch({type:'loginSuccess',payload:userName})
  5. })
  6. }
  7. store.dispatch(login('guang'))

但是這樣解決了組件里的異步過程不好復用、多個異步過程之間不好做并行、串行等控制的問題了么?

沒有,這段邏輯依然是在組件里寫,只不過移到了 dispatch 里,也沒有提供多個異步過程的管理機制。

解決這個問題,需要用 redux-saga 或 redux-observable 中間件。

redux-saga

redux-saga 并沒有改變 action,它會把 action 透傳給 store,只是多加了一條異步過程的處理。

深入理解 Redux 數據流和異步過程管理

redux-saga 中間件是這樣啟用的:

  1. import{createStore,applyMiddleware}from'redux'
  2. importcreateSagaMiddlewarefrom'redux-saga'
  3. importrootReducerfrom'./reducer'
  4. importrootSagafrom'./sagas'
  5. constsagaMiddleware=createSagaMiddleware()
  6. conststore=createStore(rootReducer,{},applyMiddleware(sagaMiddleware))
  7. sagaMiddleware.run(rootSaga)

要調用 run 把 saga 的 watcher saga 跑起來:

watcher saga 里面監聽了一些 action,然后調用 worker saga 來處理:

  1. import{all,takeLatest}from'redux-saga/effects'
  2. function*rootSaga(){
  3. yieldall([
  4. takeLatest('login',login),
  5. takeLatest('logout',logout)
  6. ])
  7. }
  8. exportdefaultrootSaga

redux-saga 會先把 action 透傳給 store,然后判斷下該 action 是否是被 taker 監聽的:

  1. functionsagaMiddleware({getState,dispatch}){
  2. returnfunction(next){
  3. returnfunction(action){
  4. constresult=next(action);//把action透傳給store
  5. channel.put(action);//觸發saga的action監聽流程
  6. returnresult;
  7. }
  8. }
  9. }

當發現該 action 是被監聽的,那么就執行相應的 taker,調用 worker saga 來處理:

  1. function*login(action){
  2. try{
  3. constloginInfo=yieldcall(loginService,action.account)
  4. yieldput({type:'loginSuccess',loginInfo})
  5. }catch(error){
  6. yieldput({type:'loginError',error})
  7. }
  8. }
  9. function*logout(){
  10. yieldput({type:'logoutSuccess'})
  11. }

比如 login 和 logout 會有不同的 worker saga。

login 會請求 login 接口,然后觸發 loginSuccess 或者 loginError 的 action。

logout 會觸發 logoutSuccess 的 action。

redux saga 的異步過程管理就是這樣的:先把 action 透傳給 store,然后判斷 action 是否是被 taker 監聽的,如果是,則調用對應的 worker saga 進行處理。

redux saga 在 redux 的 action 流程之外,加了一條監聽 action 的異步處理的流程。

其實整個流程還是比較容易理解的。理解成本高一點的就是 generator 的寫法了:

比如下面這段代碼:

  1. function*xxxSaga(){
  2. while(true){
  3. yieldtake('xxx_action');
  4. //...
  5. }
  6. }

它就是對每一個監聽到的 xxx_action 做同樣的處理的意思,相當于 takeEvery:

  1. function*xxxSaga(){
  2. yieldtakeEvery('xxx_action');
  3. //...
  4. }

但是因為有一個 while(true),很多同學就不理解了,這不是死循環了么?

不是的。generator 執行后返回的是一個 iterator,需要另外一個程序調用 next 方法才會繼續執行。所以怎么執行、是否繼續執行都是由另一個程序控制的。

在 redux-saga 里面,控制 worker saga 執行的程序叫做 task。worker saga 只是告訴了 task 應該做什么處理,通過 call、fork、put 這些命令(這些命令叫做 effect)。

然后 task 會調用不同的實現函數來執行該 worker saga。

為什么要這樣設計呢?直接執行不就行了,為啥要拆成 worker saga 和 task 兩部分,這樣理解成本不就高了么?

確實,設計成 generator 的形式會增加理解成本,但是換來的是可測試性。因為各種副作用,比如網絡請求、dispatch action 到 store 等等,都變成了 call、put 等 effect,由 task 部分控制執行。那么具體怎么執行的就可以隨意的切換了,這樣測試的時候只需要模擬傳入對應的數據,就可以測試 worker saga 了。

redux saga 設計成 generator 的形式是一種學習成本和可測試性的權衡。

還記得 redux-thunk 有啥問題么?多個異步過程之間的并行、串行的復雜關系沒法處理。那 redux-saga 是怎么解決的呢?

redux-saga 提供了 all、race、takeEvery、takeLatest 等 effect 來指定多個異步過程的關系:

比如 takeEvery 會對多個 action 的每一個做同樣的處理,takeLatest 會對多個 action 的最后一個做處理,race 會只返回最快的那個異步過程的結果,等等。

這些控制多個異步過程之間關系的 effect 正是 redux-thunk 所沒有的,也是復雜異步過程的管理必不可少的部分。

所以 redux-saga 可以做復雜異步過程的管理,而且具有很好的可測試性。

其實異步過程的管理,最出名的是 rxjs,而 redux-observable 就是基于 rxjs 實現的,它也是一種復雜異步過程管理的方案。

redux-observable

redux-observable 用起來和 redux-saga 特別像,比如啟用插件的部分:

  1. constepicMiddleware=createEpicMiddleware();
  2. conststore=createStore(
  3. rootReducer,
  4. applyMiddleware(epicMiddleware)
  5. );
  6. epicMiddleware.run(rootEpic);

和 redux saga 的啟動流程是一樣的,只是不叫 saga 而叫 epic。

但是對異步過程的處理,redux saga 是自己提供了一些 effect,而 redux-observable 是利用了 rxjs 的 operator:

  1. import{ajax}from'rxjs/ajax';
  2. constfetchUserEpic=(action$,state$)=>action$.pipe(
  3. ofType('FETCH_USER'),
  4. mergeMap(({payload})=>ajax.getJSON(`/api/users/${payload}`).pipe(
  5. map(response=>({
  6. type:'FETCH_USER_FULFILLED',
  7. payload:response
  8. }))
  9. )
  10. );

通過 ofType 來指定監聽的 action,處理結束返回 action 傳遞給 store。

相比 redux-saga 來說,redux-observable 支持的異步過程的處理更豐富,直接對接了 operator 的生態,是開放的,而 redux-saga 則只是提供了內置的幾個 effect 來處理。

所以做特別復雜的異步流程處理的時候,redux-observable 能夠利用 rxjs 的操作符的優勢會更明顯。

但是 redux-saga 的優點還有基于 generator 的良好的可測試性,而且大多數場景下,redux-saga 提供的異步過程的處理能力就足夠了,所以相對來說,redux-saga 用的更多一些。

總結

前端框架實現了數據到視圖的綁定,我們只需要關心數據流就可以了。

相比 context 的混亂的數據流,redux 的 view -> action -> store -> view 的單向數據流更清晰且容易管理。

前端代碼中有很多異步過程,這些異步過程之間可能有串行、并行甚至更復雜的關系,放在組件里并不好管理,可以放在 redux 的中間件里。

redux 的中間件就是對 dispatch 的層層包裝,比如 redux-thunk 就是判斷了下 action 是 function 就執行下,否則就是繼續 dispatch。

redux-thunk 并沒有提供多個異步過程管理的機制,復雜異步過程的管理還是得用 redux-saga 或者 redux-observable。

redux-saga 透傳了 action 到 store,并且監聽 action 執行相應的異步過程。異步過程的描述使用 generator 的形式,好處是可測試性。比如通過 take、takeEvery、takeLatest 來監聽 action,然后執行 worker saga。worker saga 可以用 put、call、fork 等 effect 來描述不同的副作用,由 task 負責執行。

redux-observable 同樣監聽了 action 執行相應的異步過程,但是是基于 rxjs 的 operator,相比 saga 來說,異步過程的管理功能更強大。

不管是 redux-saga 通過 generator 來組織異步過程,通過內置 effect 來處理多個異步過程之間的關系,還是 redux-observable 通過 rxjs 的 operator 來組織異步過程和多個異步過程之間的關系。它們都解決了復雜異步過程的處理的問題,可以根據場景的復雜度靈活選用。

原文鏈接:https://mp.weixin.qq.com/s/qBDJSoXNOVtCatOomXLrIg

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲视频在线一区二区三区 | 男人含玉势出嫁束器 | 亚洲a图 | 亚洲国产日韩制服在线观看 | 精品国产综合 | 欧美日韩一区二区三区在线观看 | 星星动漫在线观看无删减 | 人与善交大片免费看 | 色综合色狠狠天天综合色hd | 国产一区二区播放 | 国产一级视频久久 | 四虎现在的网址入口2022 | 涩涩漫画免费 | 皇上撞着太子妃的秘密小说 | 男人j桶进女人p桶爽 | 欧美╳bbbb | 色综合91久久精品中文字幕 | 亚洲国产中文字幕在线视频综合 | 高清在线免费观看 | www一级片 | 天天黄视频 | 国产成人精品视频频 | 欧美ⅹxxxx视频 | 久久久久影视 | 日韩在线中文字幕 | 精品国产一区二区三区在线 | 免费在线视频观看 | 超兴奋朋友的中文字幕下 | 美女秘密网站 | 国产精品反差婊在线观看 | 精品推荐国产麻豆剧传媒 | 91看片淫黄大片在看 | 九九精品免视看国产成人 | 无套内射在线观看THEPORN | 2021国产精品视频 | 亚洲精品一二三四 | 情乱奶水欲 | 91制片厂果冻星空传媒3xg | uoco福利姬 | 呜呜别塞了啊抽插 | 性欧美xxxxx护士另类 |