摘要:用法源碼由在年創(chuàng)建的科技術(shù)語(yǔ)。我們除去源碼校驗(yàn)函數(shù)部分,從最終返回的大的來看。這個(gè)返回值無法被識(shí)別。洋蔥模型我們來看源碼源碼每個(gè)都以作為參數(shù)進(jìn)行注入,返回一個(gè)新的鏈。改變?cè)冀M數(shù),是一種副作用。
@(Redux)[|用法|源碼]
Redux 由Dan Abramov在2015年創(chuàng)建的科技術(shù)語(yǔ)。是受2014年Facebook的Flux架構(gòu)以及函數(shù)式編程語(yǔ)言Elm啟發(fā)。很快,Redux因其簡(jiǎn)單易學(xué)體積小短時(shí)間內(nèi)成為最熱門的前端架構(gòu)。
@[三大原則]
單一數(shù)據(jù)源 - 整個(gè)應(yīng)用的state被儲(chǔ)存在一棵object tree中,并且這個(gè)object tree只存在于唯一一個(gè)store中。所有數(shù)據(jù)會(huì)通過store.getState()方法調(diào)用獲取.
State‘只讀’ - 根據(jù)State只讀原則,數(shù)據(jù)變更會(huì)通過store,dispatch(action)方法.
使用純函數(shù)修改 -Reducer只是一些純函數(shù)1,它接收先前的state和action,并返回新的state.
[TOC]
準(zhǔn)備階段 柯里化函數(shù)(curry)//curry example const A = (a) => { return (b) => { return a + b } }
通俗的來講,可以用一句話概括柯里化函數(shù):返回函數(shù)的函數(shù).
優(yōu)點(diǎn): 避免了給一個(gè)函數(shù)傳入大量的參數(shù),將參數(shù)的代入分離開,更有利于調(diào)試。降低耦合度和代碼冗余,便于復(fù)用.
舉個(gè)例子
let init = (...args) => args.reduce((ele1, ele2) => ele1 + ele2, 0) let step2 = (val) => val + 2 let step3 = (val) => val + 3 let step4 = (val) => val + 4 let steps = [step4, step3, step2, init] let composeFunc = compose(...steps) console.log(composeFunc(1, 2, 3)) // 1+2+3+2+3+4 = 15
接下來看下FP思想的compose的源碼
const compose = function (...args) { let length = args.length let count = length - 1 let result let this_ = this // 遞歸 return function f1(...arg1) { result = args[count].apply(this, arg1) if (count <= 0) { count = length - 1 return result } count-- return f1.call(null, result) } }
通俗的講: 從右到左執(zhí)行函數(shù),最右函數(shù)以arguments為參數(shù),其余函數(shù)以上個(gè)函數(shù)結(jié)果為入?yún)?shù)執(zhí)行。
優(yōu)點(diǎn): 通過這樣函數(shù)之間的組合,可以大大增加可讀性,效果遠(yuǎn)大于嵌套一大堆的函數(shù)調(diào)用,并且我們可以隨意更改函數(shù)的調(diào)用順序
CombineReducers 作用隨著整個(gè)項(xiàng)目越來越大,state狀態(tài)樹也會(huì)越來越龐大,state的層級(jí)也會(huì)越來越深,由于redux只維護(hù)唯一的state,當(dāng)某個(gè)action.type所對(duì)應(yīng)的需要修改state.a.b.c.d.e.f時(shí),我的函數(shù)寫起來就非常復(fù)雜,我必須在這個(gè)函數(shù)的頭部驗(yàn)證state 對(duì)象有沒有那個(gè)屬性。這是讓開發(fā)者非常頭疼的一件事。于是有了CombineReducers。我們除去源碼校驗(yàn)函數(shù)部分,從最終返回的大的Reducers來看。
源碼Note:
FinalReducers : 通過=== "function"校驗(yàn)后的Reducers.
FinalReducerKeys : FinalReducers的所有key
(與入?yún)?b>Object的key區(qū)別:過濾了value不為function的值)
// 返回一個(gè)function。該方法接收state和action作為參數(shù) return function combination(state = {}, action) { var hasChanged = false var nextState = {} // 遍歷所有的key和reducer,分別將reducer對(duì)應(yīng)的key所代表的state,代入到reducer中進(jìn)行函數(shù)調(diào)用 for (var i = 0; i < finalReducerKeys.length; i++) { var key = finalReducerKeys[i] var reducer = finalReducers[key] // CombineReducers入?yún)bject中的Value為reducer function,從這可以看出reducer function的name就是返回給store中的state的key。 var previousStateForKey = state[key] // debugger var nextStateForKey = reducer(previousStateForKey, action) // 如果reducer返回undefined則拋出錯(cuò)誤 if (typeof nextStateForKey === "undefined") { var errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } // 將reducer返回的值填入nextState nextState[key] = nextStateForKey // 如果任一state有更新則hasChanged為true hasChanged = hasChanged || nextStateForKey !== previousStateForKey } return hasChanged ? nextState : state }小結(jié)
combineReducers實(shí)現(xiàn)方法很簡(jiǎn)單,它遍歷傳入的reducers,返回一個(gè)新的reducer.該函數(shù)根據(jù)State 的key 去執(zhí)行相應(yīng)的子Reducer,并將返回結(jié)果合并成一個(gè)大的State 對(duì)象。
CreateStore 作用createStore主要用于Store的生成,我們先整理看下createStore具體做了哪些事兒。(這里我們看簡(jiǎn)化版代碼)
源碼(簡(jiǎn)化版)const createStore = (reducer, initialState) => { // initialState一般設(shè)置為null,或者由服務(wù)端給默認(rèn)值。 // internal variables const store = {}; store.state = initialState; store.listeners = []; // api-subscribe store.subscribe = (listener) => { store.listeners.push(listener); }; // api-dispatch store.dispatch = (action) => { store.state = reducer(store.state, action); store.listeners.forEach(listener => listener()); }; // api-getState store.getState = () => store.state; return store; }小結(jié)
源碼角度,一大堆類型判斷先忽略,可以看到聲明了一系列函數(shù),然后執(zhí)行了dispatch方法,最后暴露了dispatch、subscribe……幾個(gè)方法。這里dispatch了一個(gè)init Action是為了生成初始的State樹。
ThunkMiddleware 作用首先,說ThunkMiddleware之前,也許有人會(huì)問,到底middleware有什么用?
這就要從action說起。在redux里,action僅僅是攜帶了數(shù)據(jù)的普通js對(duì)象。action creator返回的值是這個(gè)action類型的對(duì)象。然后通過store.dispatch()進(jìn)行分發(fā)……
action ---> dispatcher ---> reducers
同步的情況下一切都很完美……
如果遇到異步情況,比如點(diǎn)擊一個(gè)按鈕,希望1秒之后顯示。我們可能這么寫:
function (dispatch) { setTimeout(function () { dispatch({ type: "show" }) }, 1000) }
這會(huì)報(bào)錯(cuò),返回的不是一個(gè)action,而是一個(gè)function。這個(gè)返回值無法被reducer識(shí)別。
大家可能會(huì)想到,這時(shí)候需要在action和reducer之間架起一座橋梁……
當(dāng)然這座橋梁就是middleware。接下來我們先看看最簡(jiǎn)單,最精髓的ThunkMiddleware的源碼
const thunkMiddleware = ({ dispatch, getState }) => { return next => action => { typeof action === "function" ? action(dispatch, getState) : next(action) } }
非常之精髓。。。我們先記住上述代碼,引出下面的ApplyMiddleware
ApplyMiddleware 作用介紹applyMiddleware之前我們先看下項(xiàng)目中store的使用方法如下:
let step = [ReduxThunk, middleware, ReduxLogger] let store = applyMiddleware(...step)(createStore)(reducer) return store
通過使用方法可以看到有3處柯里化函數(shù)的調(diào)用,applyMiddleware 函數(shù)Redux 最精髓的地方,成功的讓Redux 有了極大的可拓展空間,在action 傳遞的過程中帶來無數(shù)的“副作用”,雖然這往往也是麻煩所在。 這個(gè)middleware的洋蔥模型思想是從koa的中間件拿過來的,用圖來表示最直觀。
洋蔥模型
我們來看源碼:
const applyMiddleware = (...middlewares) => { return (createStore) => (reducer, initialState, enhancer) => { var store = createStore(reducer, initialState, enhancer) var dispatch var chain = [] var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } // 每個(gè) middleware 都以 middlewareAPI 作為參數(shù)進(jìn)行注入,返回一個(gè)新的鏈。 // 此時(shí)的返回值相當(dāng)于調(diào)用 thunkMiddleware 返回的函數(shù): (next) => (action) => {} ,接收一個(gè)next作為其參數(shù) chain = middlewares.map(middleware => middleware(middlewareAPI)) // 并將鏈代入進(jìn) compose 組成一個(gè)函數(shù)的調(diào)用鏈 dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
applyMiddleware函數(shù)第一次調(diào)用的時(shí)候,返回一個(gè)以createStore為參數(shù)的匿名函數(shù),這個(gè)函數(shù)返回另一個(gè)以reducer,initialState,enhancer為參數(shù)的匿名函數(shù).我們?cè)谑褂梅椒ㄖ?,分別可以看到傳入的值。
結(jié)合一個(gè)簡(jiǎn)單的實(shí)例來理解中間件以及洋蔥模型
// 傳入middlewareA const middlewareA = ({ dispatch, getState }) => { return next => action => { console.warn("A middleware start") next(action) console.warn("A middleware end") } } // 傳入多個(gè)middlewareB const middlewareB = ({ dispatch, getState }) => { return next => action => { console.warn("B middleware start") next(action) console.warn("B middleware end") } } // 傳入多個(gè)middlewareC const middlewareC = ({ dispatch, getState }) => { return next => action => { console.warn("C middleware start") next(action) console.warn("C middleware end") } }
當(dāng)我們傳入多個(gè)類似A,B,C的middleware到applyMiddleware后,調(diào)用
dispatch = compose(...chain)(store.dispatch)
結(jié)合場(chǎng)景并且執(zhí)行compose結(jié)果為:
dispatch = middlewareA(middlewareB(middlewareC(store.dispatch)))
從中我們可以清晰的看到middleware函數(shù)中的next函數(shù)相互連接,這里體現(xiàn)了compose FP編程思想中代碼組合的強(qiáng)大作用。再結(jié)合洋蔥模型的圖片,不難理解是怎么樣的一個(gè)工作流程。
最后我們看結(jié)果,當(dāng)我們觸發(fā)一個(gè)store.dispath的時(shí)候進(jìn)行分發(fā)。則會(huì)先進(jìn)入middlewareA并且打印A start 然后進(jìn)入next函數(shù),也就是middlewareB同時(shí)打印B start,然后觸發(fā)next函數(shù),這里的next函數(shù)就是middlewareC,然后打印C start,之后才處理dispath,處理完成后先打印C end,然后B end,最后A end。完成整體流程。
小結(jié)Redux applyMiddleware機(jī)制的核心在于,函數(shù)式編程(FP)的compose組合函數(shù),需將所有的中間件串聯(lián)起來。
為了配合compose對(duì)單參函數(shù)的使用,對(duì)每個(gè)中間件采用currying的設(shè)計(jì)。同時(shí),利用閉包原理做到每個(gè)中間件共享Store。(middlewareAPI的注入)
Feedback & Bug Reportgithub: @同性交友網(wǎng)站
Thank you for reading this record.
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94974.html
摘要:大多的初學(xué)者都會(huì)使用中間件來處理異步請(qǐng)求,其理解簡(jiǎn)單使用方便具體使用可參考官方文檔。源碼的源碼非常簡(jiǎn)潔,出去空格一共只有行,這行中如果不算上則只有行。官方文檔中的一節(jié)講解的非常好,也確實(shí)幫我理解了中間件的工作原理,非常推薦閱讀。 總覺得文章也應(yīng)該是有生命力的,歡迎關(guān)注我的Github上的博客,這里的文章會(huì)依據(jù)我本人的見識(shí),逐步更新。 大多redux的初學(xué)者都會(huì)使用redux-thunk...
摘要:概念是一個(gè)狀態(tài)管理容器使用可以更好的管理和監(jiān)測(cè)組件之間需要通信的數(shù)據(jù)。參考源碼參考鏈接 redux概念 redux是一個(gè)狀態(tài)管理容器,使用redux可以更好的管理和監(jiān)測(cè)組件之間需要通信的數(shù)據(jù)。 redux基本原則 單一數(shù)據(jù)源 在redux中,整個(gè)應(yīng)用保持一個(gè)數(shù)據(jù)源,數(shù)據(jù)源是一個(gè)樹形的結(jié)構(gòu) 狀態(tài)只讀 狀態(tài)只讀意思是不能直接修改,需要通過dispatch action方式才可以,返回的是一...
摘要:瀏覽器端使用的和集成使用時(shí)會(huì)用到中路由分類基于提供的和事件來保持和的同步。路由剖析在上面的示例中是轉(zhuǎn)發(fā)的樞紐在這個(gè)中轉(zhuǎn)站有很多線路通過開關(guān)可以啟動(dòng)列車的運(yùn)行乘坐列車就可以發(fā)現(xiàn)新大陸。 引言 在使用react做復(fù)雜的spa開發(fā)中,開發(fā)中必不可少的就是react-router,它使用Lerna管理多個(gè)倉(cāng)庫(kù), 在browser端常使用的幾個(gè)如下所示 react-router 提供了路由的...
摘要:前端進(jìn)階進(jìn)階構(gòu)建項(xiàng)目一配置最佳實(shí)踐狀態(tài)管理之痛點(diǎn)分析與改良開發(fā)中所謂狀態(tài)淺析從時(shí)間旅行的烏托邦,看狀態(tài)管理的設(shè)計(jì)誤區(qū)使用更好地處理數(shù)據(jù)愛彼迎房源詳情頁(yè)中的性能優(yōu)化從零開始,在中構(gòu)建時(shí)間旅行式調(diào)試用輕松管理復(fù)雜狀態(tài)如何把業(yè)務(wù)邏輯這個(gè)故事講好和 前端進(jìn)階 webpack webpack進(jìn)階構(gòu)建項(xiàng)目(一) Webpack 4 配置最佳實(shí)踐 react Redux狀態(tài)管理之痛點(diǎn)、分析與...
摘要:原文地址數(shù)據(jù)流通過這張流程圖,我們可以更好的理解和直接數(shù)據(jù)如何流通,關(guān)系如何映射。函數(shù)只是一個(gè)純函數(shù),它接收應(yīng)用程序的當(dāng)前狀態(tài)以及發(fā)生的,然后返回修改后的新狀態(tài)或者有人稱之為歸并后的狀態(tài)。的更新意味著更新。 原文地址:https://github.com/YutHelloWo... showImg(https://segmentfault.com/img/bVRQRK?w=1205&h...
閱讀 1882·2021-11-25 09:43
閱讀 3177·2021-11-15 11:38
閱讀 2718·2019-08-30 13:04
閱讀 494·2019-08-29 11:07
閱讀 1508·2019-08-26 18:37
閱讀 2743·2019-08-26 14:07
閱讀 594·2019-08-26 13:52
閱讀 2289·2019-08-26 12:09