摘要:假如說是士兵的話那么就是將軍,來管理士兵的狀態(tài)。對(duì)于來說,也就是一個(gè)最外層的容器,全部作為該容器組件的類似于,這樣一來發(fā)生變化,就會(huì)重新渲染整個(gè)頁面,從而達(dá)到更新的效果。得益于,每次并不用更新整個(gè)樹。規(guī)定,對(duì)外不暴露任何修改數(shù)據(jù)的接口。
redux 為什么引入redux
以react來說,state可以包含內(nèi)部的狀態(tài)以及業(yè)務(wù)數(shù)據(jù),對(duì)于一個(gè)復(fù)雜的應(yīng)用來說,state非常難以管理,一個(gè)model的變化可能引起另一個(gè)model的變化...依次下去,造成了難以維護(hù)的情況,別人很難一下子摸透你的state到底是怎么變得?所以需要引入一個(gè)東西來做數(shù)據(jù)管理,使數(shù)據(jù)變化變得清晰,redux做的就是這件事情。假如說react是士兵的話那么redux就是將軍,來管理士兵的狀態(tài)。
Flux與redux FluxFlux是facebook提出的一種架構(gòu)思想,與react一樣,強(qiáng)調(diào)的是單向數(shù)據(jù)流,由三大部分組成:
store來保存數(shù)據(jù),一個(gè)數(shù)據(jù)對(duì)應(yīng)一個(gè)store,每一個(gè)store有一個(gè)監(jiān)聽器
dispatcher,含有兩個(gè)方法:
register,注冊(cè)事件
dispatch,分發(fā)一個(gè)action使store變化
view,與flux搭配不僅僅是react還可以是vue等,為當(dāng)前頁面數(shù)據(jù)的一個(gè)展現(xiàn),與數(shù)據(jù)保持一致
flux并不是一個(gè)mvc框架,flux沒有明確的contoller,但是卻有著一個(gè)controller-view,類似于mvvm中的vm(view=vm(model))。對(duì)于react來說,也就是一個(gè)最外層的容器,store全部作為該容器組件的state(類似于
flux規(guī)定store,store對(duì)外不暴露任何修改數(shù)據(jù)的接口。
reduxfacebook提供的flux包基本只有一個(gè)dispatcher的實(shí)現(xiàn),意味著我們需要為對(duì)每一個(gè)store進(jìn)行一次定義并且創(chuàng)建一個(gè)dispatcher實(shí)例,需要register一次,dispatch時(shí)也需要區(qū)分不同的store(聽著就麻煩)。基于flux思想的redux為我們做了簡(jiǎn)化。
redux與Flux的區(qū)別redux提倡的是單一數(shù)據(jù)源,也就是一個(gè)應(yīng)用就有一個(gè)store,包含著許多的reducer,reducer根據(jù)傳入的action來決定怎么改變當(dāng)前狀態(tài)。關(guān)于redux,大家可以直接去看文檔,說的很清楚詳細(xì),下面來看一下redux的源碼:
入口index.js為redux的入口文件,暴露出來了redux所提供的API:
export { createStore, // 創(chuàng)建store combineReducers, // 合并reducer bindActionCreators, applyMiddleware, compose }
其中還有一個(gè)isCrushed函數(shù),其作用就是做環(huán)境的檢測(cè),假如在非生產(chǎn)環(huán)境中使用了壓縮的redux,則提出警告,判斷方式也很簡(jiǎn)單:
isCrushed.name !== "isCrushed" // 壓縮后函數(shù)名字會(huì)變短
下面來一個(gè)一個(gè)的看redux暴露出API的實(shí)現(xiàn):
composecompose源自于函數(shù)式編程,redux將其用于用于store的增強(qiáng),將傳入的函數(shù)從右到左的執(zhí)行,避免了層層嵌套,舉個(gè)例子:
const x = 10, add = x => x + 10, sub = x => x - 20, sup = x => x * 10; // 原生方式嵌套實(shí)現(xiàn) add( sup( sub( add(x) ) ) ); // 利用compose改進(jìn) const fn = compose(add, sup, sub, add); fn(x);
對(duì)比上面代碼,利用compose會(huì)使代碼變得清晰,compose就是函數(shù)式編程中的組合:
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg; } if (funcs.length === 1) { return funcs[0]; } return funcs.reduce( (a, b) => ( (...args) => a(b(...args)) ) ); };createStore 什么是store
redux強(qiáng)調(diào)的是單一數(shù)據(jù)源,把所有的state放入到一棵object tree中,這棵樹只能唯一的存在于一個(gè)store中,也就是說redux強(qiáng)調(diào)整個(gè)應(yīng)用只有一個(gè)store。store可以包含
解析依賴函數(shù)該模塊依賴了lodash的isPlainObject,該函數(shù)用來判斷對(duì)象是否繼承了自定義類,實(shí)現(xiàn)起來非常簡(jiǎn)單:
function isPlainObject(val) { // 非對(duì)象的情況直接返回false if (!isObjectLike(value) || baseGetTag(value) != "[object Object]") { return false } const proto = Object.getPrototypeOf(value) // 針對(duì)Object.create(null)創(chuàng)建出來的對(duì)象 if (proto === null) { return true } const Ctor = hasOwnProperty.call(proto, "constructor") && proto.constructor // prototype.constructor === Object return typeof Ctor == "function" && Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString }主體函數(shù)
// 內(nèi)部的action,用于reset export const ActionTypes = { INIT: "@@redux/INIT" }; /* * 創(chuàng)建store * reducer reducer函數(shù) * preloadedState 初始狀態(tài) * enhancer 增強(qiáng)函數(shù),對(duì)createStoren能力進(jìn)行增強(qiáng),如devtools */ export default function createStore(reducer, preloadedState, enhancer) { // 參數(shù)修正 if (typeof preloadedState === "function" && typeof enhancer === "undefined") { enhancer = preloadedState; preloadedState = undefined; } if (typeof enhancer !== "undefined") { if (typeof enhancer !== "function") { throw new Error("Expected the enhancer to be a function."); } // 返回已經(jīng)增強(qiáng)后的store return enhancer(createStore)(reducer, preloadedState); } if (typeof reducer !== "function") { throw new Error("Expected the reducer to be a function."); } // 記錄當(dāng)前值 let currentReducer = reducer; let currentState = preloadedState; // 監(jiān)聽store變化的監(jiān)聽器(一些回調(diào)函數(shù)) let currentListeners = []; let nextListeners = currentListeners; // 是否處于dispatch的過程中 let isDispatching = false; /**** 看下面文字解釋 ****/ function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice(); } } // 給store新增一個(gè)監(jiān)聽器 function subscribe(listener) { if (typeof listener !== "function") { throw new Error("Expected listener to be a function."); } let isSubscribed = true; ensureCanMutateNextListeners(); nextListeners.push(listener); // 返回一個(gè)卸載方法 return function unsubscribe() { if (!isSubscribed) { return } isSubscribed = false; ensureCanMutateNextListeners(); const index = nextListeners.index(listener); nextListeners.splice(index, 1); }; } // 返回當(dāng)前的狀態(tài) function getState() { return currentState; } function dispatch(action) { // 一些類型檢測(cè) if (!isPlainObject(action)) { throw new Error( "Actions must be plain objects. " + "Use custom middleware for async actions." ) } if (typeof action.type === "undefined") { throw new Error( "Actions may not have an undefined "type" property. " + "Have you misspelled a constant?" ) } if (isDispatching) { throw new Error("Reducers may not dispatch actions.") } try { isDispatching = true; // 根據(jù)recuder來更新當(dāng)前狀態(tài) currentState = currentReducer(currentState, action); } finally { isDispatching = false; } const listeners = currentListeners = nextListeners; // 發(fā)布事件 for (let i = 0; i < listeners.length; i++) { const listener = listeners; listener(); } return action; } // 更新當(dāng)前reducer,重置store function replaceReducer(nextReducer) { if (typeof nextReducer !== "function") { throw new Error("Expected the nextReducer to be a function."); } currentReducer = nextReducer; dispatch({ type: ActionTypes.INIT }); } // 初始化store dispatch({ type: ActionTypes.INIT }); // 與Rxjs這種交互,根本看不懂-- function observable() { ... } return { getState, subscribe, dispatch, replaceReducer, [$$observable]: observable }; };
需要注意的有以下幾點(diǎn):
觀察源碼可以發(fā)現(xiàn),currentListeners與nextListeners存儲(chǔ)的都是監(jiān)聽函數(shù),這樣做的目的是保證dispatch的過程不發(fā)生錯(cuò)誤,加入使用一個(gè)隊(duì)列的話,當(dāng)執(zhí)行過程中有監(jiān)聽函數(shù)被移除時(shí),則會(huì)發(fā)生錯(cuò)誤,如:當(dāng)前監(jiān)聽函數(shù)隊(duì)列為:[a, b, c],當(dāng)執(zhí)行回調(diào)函數(shù)a后將其移除,則隊(duì)列發(fā)生改變[b, c],而利用for循環(huán)來執(zhí)行回調(diào),執(zhí)行到i = 2時(shí)會(huì)拋出錯(cuò)誤。
forEach比for差在:
ie8不兼容forEach (ie8 is out)
forEach不能提前終止,但是在dispatch中無此問題
性能,這是forEach最大的問題,for要比forEach快的多的多(差不多一倍左右),查看v8代碼也可以感覺出,forEach本質(zhì)上做的還是for循環(huán),每次loop進(jìn)行一次判斷和函數(shù)調(diào)用,自然速度會(huì)慢。
applyMiddleware中間件,express與koa也就中間件,express中的中間件處理的請(qǐng)求到響應(yīng)的這一過程,redux中的中間件處理的是從action發(fā)出到reducer接收到action這一過程,在這個(gè)過程中可以利用中間件進(jìn)行寫日志,處理異步操作等過程,作用就是來增強(qiáng)createStore,給其添加更多的功能。先來看下下面這個(gè)簡(jiǎn)化的例子:
const calc = (obj) => obj.value + 20; // 簡(jiǎn)單的模擬下stroe const obj = { calc }; // 加強(qiáng)fn函數(shù) function applyMiddleware(fn, middlewares) { middlewares = middlewares.slice(); middlewares.reverse(); // 每次改變fn,一次擴(kuò)展一個(gè)功能 let { calc } = obj; middlewares.forEach( middleware => calc = middleware(obj)(calc) ); return calc; } // arrow function is cool!!! const logger = obj => next => num => { console.log(`num is ${num.value}`); let result = next(num); console.log(`finish calc, result is ${result}`); return result; }; const check = obj => next => num => { console.log(`typeof num.value is ${typeof num.value}`); let result = next(num); return result; }; const fn = applyMiddleware(obj, [check, logger]); fn({ value: 50 });
在上面簡(jiǎn)單的例子中為calc做了兩個(gè)功能擴(kuò)展,執(zhí)行起來就是一個(gè)高階函數(shù)check->logger->calc,理解了這個(gè)再來看看redux的applyMiddleware的實(shí)現(xiàn):
export default function applyMiddleware(...middlewares) { // 利用閉包保存下來了middlewares return (createStore) => (reducer, preloadadState, enhancer) => { const store = createStore(reducer, preloadadState, enhancer); let dispatch = store.dispatch; let chain = []; // middleware不會(huì)改變store,利用閉包保存 const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) }; // chain中的元素仍未函數(shù) // 接受的參數(shù)為`next` => 下一個(gè)中間件 chain = middlewares.map(middleware => middleware(middlewareAPI)); // 本身的dispatch放在最后執(zhí)行 // dispatch仍未函數(shù),接受的參數(shù)為`action` // 返回的是一個(gè)高階函數(shù),需要注意的是中間件并不會(huì)改變?cè)镜腶ction // dispatch變成了一個(gè)升級(jí)版 dispatch = compose(...chain)(store.dispatch); return { ...store, dispatch }; }; };
附一張圖:
applyMiddleware代碼雖少,但是卻是最難理解的部分
redux-thunk是一個(gè)十分剪短有用的中間件,尤其是在異步應(yīng)用中,利用redux-thunk可以使action變?yōu)橐粋€(gè)函數(shù),而不僅僅是一個(gè)對(duì)象,并且在該函數(shù)中仍然可以觸發(fā)dispatch:
// 讓action可以成為函數(shù) function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { // action為函數(shù)類型,執(zhí)行action,dispatch結(jié)束 if (typeof action === "function") { return action(dispatch, getState, extraArgument); } return next(action); }; }combineReducers
combineReducers用來合并多個(gè)reducer:
assertReducerSanity來驗(yàn)證reducer利用createStore文件中定義的ActionTypes來進(jìn)行初始化state,也就是定義的reducer會(huì)在一開始執(zhí)行一遍,而后對(duì)得到state進(jìn)行檢測(cè),為undefined拋出異常,同時(shí)利用隨機(jī)字符串測(cè)試,防止其處理type為@@redux/INIT而未處理default的情況。
combineReducers將我們所寫的reducer進(jìn)行合并,返回一個(gè)函數(shù),每次dispatch時(shí),執(zhí)行函數(shù),遍歷所有的reducer,計(jì)算出最終的state:
export default function combineReducers(reducers) { const reducerKeys = Object.keys(reducers) const finalReducers = {} // 有效reducer集合 // 驗(yàn)證reducer,必須是純函數(shù)才有效 for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (NODE_ENV !== "production") { if (typeof reducers[key] === "undefined") { warning(`No reducer provided for key "${key}"`) } } if (typeof reducers[key] === "function") { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache if (NODE_ENV !== "production") { unexpectedKeyCache = {} } let sanityError try { // 檢測(cè)reducer assertReducerSanity(finalReducers) } catch (e) { sanityError = e } return function combination(state = {}, action) { if (sanityError) { throw sanityError } // getUnexpectedStateShapeWarningMessage // 檢測(cè)state類型是否為0繼承對(duì)象 // 用于找出多余的redcuer并給出警告 if (NODE_ENV !== "production") { const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache) if (warningMessage) { warning(warningMessage) } } let hasChanged = false const nextState = {} // 每次發(fā)出一個(gè)action會(huì)遍歷所有的reducer for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] // 保留之前的state與新生成的state做對(duì)比 const reducer = finalReducers[key] const previousStateForKey = state[key] const next StateForKey = reducer(previousStateForKey, action) // state為undefined拋出異常 if (typeof nextStateForKey === "undefined") { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey // 比較是否數(shù)據(jù)發(fā)生變化 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 未發(fā)生變化返回之前的數(shù)據(jù) return hasChanged ? nextState : state } }bindActionCreators
這個(gè)基本上的作用不大,唯一運(yùn)用的情況就是將其作為props傳入子組件,對(duì)于子組件來說可以全然不知redux的存在。如果利用UI庫的組件來操作頁面狀態(tài),bindActionCreators是一個(gè)很好的選擇,實(shí)現(xiàn)很簡(jiǎn)單:
function bindActionCreator(actionCreator, dispatch) { return (...args) => dispatch(actionCreator(...args)) }
bindActionCreators僅僅是遍歷所有的actions返回一個(gè)鍵值對(duì)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/81465.html
摘要:深入簡(jiǎn)述在快速理解中,我簡(jiǎn)單的介紹了的基礎(chǔ)內(nèi)容,本篇文章中,我們將再度深入。二修改我曾在快速理解中提起,為了解決模塊組件之間需要共享數(shù)據(jù)和數(shù)據(jù)可能被任意修改導(dǎo)致不可預(yù)料的結(jié)果的矛盾,團(tuán)隊(duì)創(chuàng)建了。 深入Redux 簡(jiǎn)述 在快速理解redux中,我簡(jiǎn)單的介紹了redux的基礎(chǔ)內(nèi)容,本篇文章中,我們將再度深入redux。 redux解決的問題 數(shù)據(jù)和函數(shù)的層層傳遞 多個(gè)組件同時(shí)修改某全局變...
摘要:使得在變化和異步中可預(yù)測(cè)。它是數(shù)據(jù)的唯一來源。指定了應(yīng)用狀態(tài)的變化如何響應(yīng)并發(fā)送到的,只是描述了有事情發(fā)生了這一事實(shí),并沒有描述應(yīng)用如何更新。更新的函數(shù)稱為,它是一個(gè)純函數(shù),接受舊的和,返回新的。是和之間的橋梁,是把它們聯(lián)系到一起的對(duì)象。 為什么使用redux 隨著前端單頁面開發(fā)越來越復(fù)雜,javascript需要管理越來越多的狀態(tài)state。如果一個(gè)model的變化引起另一個(gè)mode...
摘要:我們知道狀態(tài)機(jī)是表示有限個(gè)狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動(dòng)作等行為的數(shù)學(xué)模型。對(duì)照上面我們自己寫的狀態(tài)機(jī)代碼可以看出的作用告訴狀態(tài)樹發(fā)生什么變化,及所需要的數(shù)據(jù)是什么。 前言 我之前開發(fā)網(wǎng)站的時(shí)候一直用的是 Flux, 自從出了 Redux 之后由于種種原因沒有跟進(jìn)了解,最近手頭上的事情基本忙的差不多了,抽空閱讀了 Redux 的源碼,并整理了這篇博文。 先說重點(diǎn): Redux 與 R...
摘要:我們約定,內(nèi)使用一個(gè)字符串類型的字段來表示將要執(zhí)行的動(dòng)作。多數(shù)情況下,會(huì)被定義成字符串常量。要進(jìn)去工廠加工產(chǎn)品,就得帶上相應(yīng)得鑰匙,不同的鑰匙對(duì)應(yīng)工廠中上不同的生產(chǎn)線里面的函數(shù),從而有不同的產(chǎn)出改變之后的 時(shí)間:2016.4.7-17:24作者:三月懶驢入門配置文章:鏈接 準(zhǔn)備 在你的項(xiàng)目下面加入redux / react-redux npm install redux --s...
摘要:調(diào)用鏈中最后一個(gè)會(huì)接受真實(shí)的的方法作為參數(shù),并借此結(jié)束調(diào)用鏈。總結(jié)我們常用的一般是除了和之外的方法,那個(gè)理解明白了,對(duì)于以后出現(xiàn)的問題會(huì)有很大幫助,本文只是針對(duì)最基礎(chǔ)的進(jìn)行解析,之后有機(jī)會(huì)繼續(xù)解析對(duì)他的封裝 前言 雖然一直使用redux+react-redux,但是并沒有真正去講redux最基礎(chǔ)的部分理解透徹,我覺得理解明白redux會(huì)對(duì)react-redux有一個(gè)透徹的理解。 其實(shí),...
摘要:本周精讀內(nèi)容是重新思考。數(shù)據(jù)流對(duì)數(shù)據(jù)緩存,性能優(yōu)化,開發(fā)體驗(yàn)優(yōu)化都有進(jìn)一步施展的空間,擁抱插件生態(tài)是一個(gè)良好的發(fā)展方向。 本周精讀內(nèi)容是 《重新思考 Redux》。 1 引言 《重新思考 Redux》是 rematch 作者 Shawn McKay 寫的一篇干貨軟文。 dva 之后,有許多基于 redux 的狀態(tài)管理框架,但大部分都很局限,甚至是倒退。但直到看到了 rematch,總算...
閱讀 2978·2023-04-26 02:04
閱讀 1286·2021-11-04 16:07
閱讀 3712·2021-09-22 15:09
閱讀 685·2019-08-30 15:54
閱讀 1906·2019-08-29 14:11
閱讀 2534·2019-08-26 12:19
閱讀 2261·2019-08-26 12:00
閱讀 764·2019-08-26 10:27