亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 網站 > WEB開發 > 正文

redux源碼解析

2024-04-27 15:07:58
字體:
來源:轉載
供稿:網友

用npm安裝redux,看目錄結構 npm install redux 找到src目錄 * index.js: redux主文件,主要對外暴露核心api * createStore.jscreateStore方法的定義 * utils: applyMiddleware、combineReducers、bindActionCreators為redux的核心方法,pick、mapValue、compose為普通的工具函數

├── createStore.js ├── index.js └── utils ├── applyMiddleware.js ├── bindActionCreators.js ├── combineReducers.js ├── compose.js ├── isPlainObject.js ├── mapValues.js └── pick.js

index.js

import createStore from './createStore';import combineReducers from './utils/combineReducers';import bindActionCreators from './utils/bindActionCreators';import applyMiddleware from './utils/applyMiddleware';import compose from './utils/compose';export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose};

createStore.js

翻譯一下createStore方法的注釋

/** * Creates a Redux store that holds the state tree. * The only way to change the data in the store is to call `dispatch()` on it. * * There should only be a single store in your app. To specify how different * parts of the state tree respond to actions, you may combine several reducers * into a single reducer function by using `combineReducers`. * * @param {Function} reducer A function that returns the next state tree, given * the current state tree and the action to handle. * * @param {any} [PReloadedState] The initial state. You may optionally specify it * to hydrate the state from the server in universal apps, or to restore a * previously serialized user session. * If you use `combineReducers` to produce the root reducer function, this must be * an object with the same shape as `combineReducers` keys. * * @param {Function} enhancer The store enhancer. You may optionally specify it * to enhance the store with third-party capabilities such as middleware, * time travel, persistence, etc. The only store enhancer that ships with Redux * is `applyMiddleware()`. * * @returns {Store} A Redux store that lets you read the state, dispatch actions * and subscribe to changes. */

創建一個redux store來管理狀態樹,如果你想改變狀態樹上的數據,只能通過調用dispatch發起一個action。在你的web app中,有且只能有一個store。為了說明狀態樹對action的響應,你應該把所有reducer通過一個combineReducers合并成一個單一的reducer函數(關于reducer可以參考我另一篇博文redux文檔解讀)。這里的參數reducer是一個接收當前狀態樹和action,返回下一個狀態樹的函數。參數preloadedState指的是初始狀態,你可以將它與服務器的狀態合并(比如Ajax)。參數enhancer是store的加強功能,你可以選擇通過第三的東西比如中間件、持久化等來加強store的功能,唯一一個使用方法是applyMiddleware()。函數返回值是一個redux store,可以讓你讀取狀態樹上的state,也可以讓你dispatch action來改變狀態。所以這個createStore方法, 傳入了reducer、initialState,并返回一個store對象。 先看一下redux的初始化

// When a store is created, an "INIT" action is dispatched so that every // reducer returns their initial state. This effectively populates // the initial state tree. dispatch({ type: ActionTypes.INIT })

當一個store創建的時候,會先dispatch一個action,type為ActionTypes.INIT(上文中有定義),這時候reducer會把他們初始的state返回,構成了初始的狀態樹 再來看一下比較核心的dispatch方法

/** * Dispatches an action. It is the only way to trigger a state change. * * The `reducer` function, used to create the store, will be called with the * current state tree and the given `action`. Its return value will * be considered the **next** state of the tree, and the change listeners * will be notified. * * The base implementation only supports plain object actions. If you want to * dispatch a Promise, an Observable, a thunk, or something else, you need to * wrap your store creating function into the corresponding middleware. For * example, see the documentation for the `redux-thunk` package. Even the * middleware will eventually dispatch plain object actions using this method. * * @param {Object} action A plain object representing “what changed”. It is * a good idea to keep actions serializable so you can record and replay user * sessions, or use the time travelling `redux-devtools`. An action must have * a `type` property which may not be `undefined`. It is a good idea to use * string constants for action types. * * @returns {Object} For convenience, the same action object you dispatched. * * Note that, if you use a custom middleware, it may wrap `dispatch()` to * return something else (for example, a Promise you can await). */ function dispatch(action) { 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 currentState = currentReducer(currentState, action) } finally { isDispatching = false } var listeners = currentListeners = nextListeners for (var i = 0; i < listeners.length; i++) { listeners[i]() } return action }

再翻譯下注釋,action只支持plain object,即用JSON形式定義的普通對象或者new Object()創建的簡單對象。如果想dipatch一個Promise,一個thunk,你需要包裝你的store create函數??梢钥匆豢磖edux-thunk的文檔,當然最后middleware還是使用這個方法diapatch了plain object。action用來表示改變的是什么,可以把action序列化, 便于記錄。action必須有一個type屬性,而且這個屬性不能是undefined。最好是把action的屬性賦值為大寫的字符串常量。 dispatch主要是幾個判斷,action和action.type的判斷,當dipatch的時候,改變函數內的全局變量isDispatching,然后把currentState修改為reducer返回的state,最后調用subscribe中傳入的回調函數也就是監聽器listener。 下面講一下subscribe函數

/** * Adds a change listener. It will be called any time an action is dispatched, * and some part of the state tree may potentially have changed. You may then * call `getState()` to read the current state tree inside the callback. * * You may call `dispatch()` from a change listener, with the following * caveats: * * 1. The subscriptions are snapshotted just before every `dispatch()` call. * If you subscribe or unsubscribe while the listeners are being invoked, this * will not have any effect on the `dispatch()` that is currently in progress. * However, the next `dispatch()` call, whether nested or not, will use a more * recent snapshot of the subscription list. * * 2. The listener should not expect to see all state changes, as the state * might have been updated multiple times during a nested `dispatch()` before * the listener is called. It is, however, guaranteed that all subscribers * registered before the `dispatch()` started will be called with the latest * state by the time it exits. * * @param {Function} listener A callback to be invoked on every dispatch. * @returns {Function} A function to remove this change listener. */ function subscribe(listener) { if (typeof listener !== 'function') { throw new Error('Expected listener to be a function.') } var isSubscribed = true ensureCanMutateNextListeners() nextListeners.push(listener) //返回一個解綁函數 return function unsubscribe() { if (!isSubscribed) { return } isSubscribed = false ensureCanMutateNextListeners() //通過閉包拿到listener的值 var index = nextListeners.indexOf(listener) //在listeners數組中除去這個lisnter nextListeners.splice(index, 1) } }

這里調用了ensureCanMutateNextListeners方法

//初始化監聽器數組 var currentListeners = [] var nextListeners = currentListeners function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } }

添加一個變化監聽器,listener是我們調用時傳入的回調函數,listener會在action被dispatch之后被調用,你可以在回調函數listener里調用 getState() 來拿到當前 state。 如果需要解綁這個變化監聽器,執行 subscribe 返回的函數即可。在實際使用中:

function handleChange(){ store.getState()... } let unsubscribe = store.subscribe(handleChange) unsubscribe()

接下來是replaceReducer

/** * Replaces the reducer currently used by the store to calculate the state. * * You might need this if your app implements code splitting and you want to * load some of the reducers dynamically. You might also need this if you * implement a hot reloading mechanism for Redux. * * @param {Function} nextReducer The reducer for the store to use instead. * @returns {void} */ function replaceReducer(nextReducer) { if (typeof nextReducer !== 'function') { throw new Error('Expected the nextReducer to be a function.') } currentReducer = nextReducer dispatch({ type: ActionTypes.INIT }) }

替換 store 當前用來計算 state 的 reducer,并且dispatch初始化的action。最后返回的store對象,具有dispatch,subscribe,getState,replaceReducer方法

return { dispatch, subscribe, getState, replaceReducer };

combineReducers.js

先看核心的combineReducers

/** * Turns an object whose values are different reducer functions, into a single * reducer function. It will call every child reducer, and gather their results * into a single state object, whose keys correspond to the keys of the passed * reducer functions. * * @param {Object} reducers An object whose values correspond to different * reducer functions that need to be combined into one. One handy way to obtain * it is to use ES6 `import * as reducers` syntax. The reducers may never return * undefined for any action. Instead, they should return their initial state * if the state passed to them was undefined, and the current state for any * unrecognized action. * * @returns {Function} A reducer function that invokes every reducer inside the * passed object, and builds a state object with the same shape. */ export default function combineReducers(reducers) { var reducerKeys = Object.keys(reducers) var finalReducers = {} for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i] if (process.env.NODE_ENV !== 'production') { if (typeof reducers[key] === 'undefined') { warning(`No reducer provided for key "${key}"`) } } if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } var finalReducerKeys = Object.keys(finalReducers) if (process.env.NODE_ENV !== 'production') { var unexpectedKeyCache = {} } var sanityError try { // 對所有的子reducer 做一些合法性斷言 assertReducerSanity(finalReducers) } catch (e) { sanityError = e } return function combination(state = {}, action) { if (sanityError) { throw sanityError } if (process.env.NODE_ENV !== 'production') { var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache) if (warningMessage) { warning(warningMessage) } } var hasChanged = false var nextState = {} for (var i = 0; i < finalReducerKeys.length; i++) { var key = finalReducerKeys[i] var reducer = finalReducers[key] var previousStateForKey = state[key] var nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { var errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } return hasChanged ? nextState : state } }

先翻譯下注釋,先將一個reducers對象(value為不同的reducer函數),轉化成唯一一個reducer函數,它可以調用原來的每一個reducer函數,并且將他們的結果合并到一個單一的狀態對象。調用這個函數只需要使用es6的語法import即可。此外這個函數永遠不會返回undefined,redux 首次執行時,state 為 undefined,它將會返回我們設置的初始state。

const initialState = { ... }; //使用es6的默認值語法 function todoApp(state = initialState, action) { ... return state }

而為什么我們之前將reducer分解為多個子reducer函數,是為了對不同的狀態對應相應的reducer,并且更新時使用

//Object.assign實現局部更新,只對state的變化部分進行更新 return Object.assign({}, todos, { completed: !todos.completed })

從combineReducers的代碼上看,先是使用finalReducers對象對reducers對象中的鍵值對進行保存,遍歷reducers對象,檢查其中的value值是否為函數,是函數的話加入finalReducers對象, 當key值為undefined的時候,非生產環境會提示警告。assertReducerSanity函數檢查我們是否在子reducer函數中定義了initialState。finalReducers的對象key為reducer函數名,value為reducer函數。所以遍歷這個對象,對應每一個state(下面場景中的state.todos,state.filter)對應的reducer函數進行執行,最后將結果保存到nextState。 調用這個函數的場景如下:

function TodoReducer(state, action) {} function FilterReducer(state, action) {} var finalReducers = redux.combineReducers({ todos: TodoReducer, filter: FilterReducer });

bindActionCreator.js

/** * Turns an object whose values are action creators, into an object with the * same keys, but with every function wrapped into a `dispatch` call so they * may be invoked directly. This is just a convenience method, as you can call * `store.dispatch(MyActionCreators.doSomething())` yourself just fine. * * For convenience, you can also pass a single function as the first argument, * and get a function in return. * * @param {Function|Object} actionCreators An object whose values are action * creator functions. One handy way to obtain it is to use ES6 `import * as` * syntax. You may also pass a single function. * * @param {Function} dispatch The `dispatch` function available on your Redux * store. * * @returns {Function|Object} The object mimicking the original object, but with * every action creator wrapped into the `dispatch` call. If you passed a * function as `actionCreators`, the return value will also be a single * function. */ export default function bindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } if (typeof actionCreators !== 'object' || actionCreators === null) { throw new Error( `bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` + `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?` ) } var keys = Object.keys(actionCreators) var boundActionCreators = {} for (var i = 0; i < keys.length; i++) { var key = keys[i] var actionCreator = actionCreators[key] if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) } } return boundActionCreators }

翻譯下注釋,將一個對象(value是action的creator),變成一個key相同,但是每個函數都被包進了dispatch。這是一個快捷方式,總比你調用store.dispatch(MyActionCreators.doSomething())快得多。參數dispatch就是createStore扔出來的store的dispatch方法。 這個js的核心是bindActionCreator函數

function bindActionCreator(actionCreator, dispatch){ //...args是actionCreator和dispatch,相當于給actionCreator函數再包裹了一層dispatch return (...args) => dispatch(actionCreator(...args)) }

可以通過一個例子來理解

var addTodo = function(text){ return { type: 'add_todo', text: text }; }; var actions = redux.bindActionCreators({ addTodo: addTodo }, store.dispatch); //相當于dispatch一個{ type: 'add_todo', text: 'xx' } actions.addTodo('xx');

applyMiddleware.js

/** * Creates a store enhancer that applies middleware to the dispatch method * of the Redux store. This is handy for a variety of tasks, such as expressing * asynchronous actions in a concise manner, or logging every action payload. * * See `redux-thunk` package as an example of the Redux middleware. * * Because middleware is potentially asynchronous, this should be the first * store enhancer in the composition chain. * * Note that each middleware will be given the `dispatch` and `getState` functions * as named arguments. * * @param {...Function} middlewares The middleware chain to be applied. * @returns {Function} A store enhancer applying the middleware. */ export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer) var dispatch = store.dispatch var chain = [] var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }

繼續先翻譯,創建一個store的增強件,它可以引進一些中間件給dispatch方法,這個對于很多任務都很方便,比如說異步的action,或者記錄每一個action。 再結合官方文檔對middleware的解釋,這里提供的是action被發起后,到達reducer之前的擴展點,你可以利用 Redux middleware 來進行日志記錄、創建崩潰報告、調用異步接口或者路由等等。在調用store.dispatch前后想記錄被發起的action和新的state,第一種方法如下:

store.dispatch(addTodo('xx')); //第一種方法 let action = addTodo('xx'); console.log('dispatching', action) store.dispatch(action) console.log('next state', store.getState()) //第二種方法,封裝 function dispatchAndLog(store, action) { console.log('dispatching', action) store.dispatch(action) console.log('next state', store.getState()) } dispatchAndLog(store, addTodo('Use Redux')) //第三種方法,用一個中間變量保存store.dispatch let next = store.dispatch store.dispatch = function dispatchAndLog(action){ console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } //第四種方法 function logger(store) { let next = store.dispatch // 我們之前的做法: // store.dispatch = function dispatchAndLog(action).. return function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } } function applyMiddlewareByMonkeypatching(store,middlewares){ middlewares = middlewares.slice() middlewares.reverse() // 在每一個 middleware 中變換 dispatch 方法。 middlewares.forEach(middleware => store.dispatch = middleware(store) ) } applyMiddlewareByMonkeypatching(store, [ logger, crashReporter ])

第四種方法在middleware的數組內部的回調函數中改變了store.dispatch方法。那么為什么我們要替換原來的dispatch呢,主要是因為每一個middleware都想使用包裝過的store.dispatch,在這邊例子中就是我們包裝過的日志記錄函數。如果 applyMiddlewareByMonkeypatching 方法中沒有在第一個 middleware 執行時立即替換掉 store.dispatch,那么 store.dispatch 將會一直指向原始的 dispatch 方法。也就是說,第二個 middleware 依舊會作用在原始的 dispatch 方法。 但是,還有另一種方式來實現這種鏈式調用的效果??梢宰?middleware 以方法參數的形式接收一個 next() 方法,而不是通過 store 的實例去獲取。

function logger(store) { return function wrapDispatchToAddLogging(next) { return function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } } }
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
午夜精品理论片| 视频直播国产精品| 久久久久久久久国产精品| 久久亚洲精品视频| 亚洲免费电影在线观看| 中文字幕在线看视频国产欧美在线看完整| 亚洲国产精品国自产拍av秋霞| 成人信息集中地欧美| 91av在线精品| 亚洲欧美日韩在线高清直播| 欧美成年人网站| 亚洲精品福利视频| 亚洲精品永久免费精品| 亚洲丁香婷深爱综合| 久久精品亚洲94久久精品| 高清亚洲成在人网站天堂| 欧美性xxxx18| 精品少妇一区二区30p| 亚洲成av人片在线观看香蕉| 亚洲一区二区在线播放| 国产丝袜一区二区三区免费视频| 国产精品久久999| 国产成人精品一区二区在线| 日本精品性网站在线观看| www.亚洲天堂| xxx成人少妇69| 久久伊人精品天天| 国产精品欧美在线| 亚洲精品乱码久久久久久金桔影视| 欧美激情中文网| 国内精品久久久久久中文字幕| 最近2019年好看中文字幕视频| 成人黄色在线播放| 久久理论片午夜琪琪电影网| 欧美成人精品一区| 日韩欧美精品网址| 欧美性视频网站| 福利视频导航一区| 久久视频在线视频| 最近中文字幕2019免费| 亚洲国产天堂网精品网站| 国产一区二区三区在线播放免费观看| 国产精品v片在线观看不卡| 欧美日韩免费区域视频在线观看| 国产精品入口免费视频一| 全色精品综合影院| 亚洲精品动漫久久久久| 欧洲s码亚洲m码精品一区| 久久99久久99精品免观看粉嫩| 奇米4444一区二区三区| 亚洲欧美国产精品久久久久久久| 日韩综合视频在线观看| 中文字幕少妇一区二区三区| 91po在线观看91精品国产性色| 国产精品中文字幕在线| 麻豆精品精华液| 日本国产欧美一区二区三区| 久久久久久久久国产| 国产亚洲人成a一在线v站| 精品女同一区二区三区在线播放| 色老头一区二区三区| 疯狂做受xxxx高潮欧美日本| 亚洲区中文字幕| 97超碰蝌蚪网人人做人人爽| 97视频在线看| 欧美精品在线第一页| 欧美一级大胆视频| 亚洲色图18p| 欧美日韩国产激情| 日韩免费看的电影电视剧大全| 91久久久久久久久久久久久| 亚洲一区二区三区成人在线视频精品| 欧美国产亚洲精品久久久8v| 91av成人在线| 亚洲成人a级网| 亚洲网站在线观看| 久久夜色精品国产欧美乱| 国产91久久婷婷一区二区| 国产成人精品免高潮费视频| 欧美理论电影在线观看| 在线日韩精品视频| 成人久久一区二区| 亚洲国产日韩一区| 国产精品久久久久久久久| 欧美精品videosex极品1| 色偷偷噜噜噜亚洲男人的天堂| 日韩风俗一区 二区| 国产精品久久久精品| 国产欧美精品日韩| 日韩精品一二三四区| 亚洲区中文字幕| 亚洲国产精彩中文乱码av在线播放| 性欧美亚洲xxxx乳在线观看| 日本中文字幕不卡免费| 国产男人精品视频| 国产日韩精品一区二区| 久久视频在线直播| 国产精品偷伦视频免费观看国产| 久久精品成人欧美大片古装| 黄色成人av网| 另类天堂视频在线观看| 欧美另类极品videosbestfree| 91精品国产自产91精品| 91日本在线观看| 欧美日韩午夜剧场| 久久久久国色av免费观看性色| 91欧美精品成人综合在线观看| 久久精品国亚洲| 91亚洲一区精品| 精品国产一区二区三区久久狼5月| 国产成人综合精品在线| 精品香蕉在线观看视频一| 在线一区二区日韩| 欧美在线视频观看| 欧美激情精品久久久久久蜜臀| 国产成人在线一区二区| 国产69精品久久久久99| 久久天天躁狠狠躁夜夜爽蜜月| 在线视频精品一| 98精品国产自产在线观看| 久久久久中文字幕2018| 国产精品视频xxxx| 亚洲成色www8888| 亚洲综合第一页| 欧美诱惑福利视频| 久久精品91久久久久久再现| 性欧美xxxx| 久久在线观看视频| 久久久精品久久久久| 国产一区二区三区精品久久久| 国产z一区二区三区| 亚洲国产日韩一区| 日本久久久久亚洲中字幕| 日韩在线免费视频| 国产精品视频内| 久久久久久国产精品三级玉女聊斋| 狠狠躁夜夜躁久久躁别揉| 性欧美暴力猛交69hd| 日本视频久久久| 亚洲级视频在线观看免费1级| 久久不射电影网| 欧美精品在线看| 91久久久久久久| 高清视频欧美一级| 国产欧美日韩精品丝袜高跟鞋| 俺也去精品视频在线观看| 亚洲综合在线小说| 久久久人成影片一区二区三区观看| 北条麻妃一区二区三区中文字幕| 欧美性xxxxx极品| 精品亚洲男同gayvideo网站| 国产精品丝袜久久久久久高清| 亚洲自拍偷拍网址| 成人www视频在线观看| 日韩色av导航| 亚洲精品国产成人| xxxx性欧美| 综合激情国产一区| 91国内免费在线视频| 日本高清+成人网在线观看| 狠狠躁夜夜躁人人躁婷婷91| 日韩精品在线免费观看视频| 国产精品久久一区| 欧美大尺度在线观看|