redux源碼分析
1. 背景
使用 redux 的開發者越來越多,但背後 redux 原理和為什麼用 redux 很多人其實並不清楚。如果我們不能完全理解 redux 的思想,可能在實際的應用中,會盲目使用。本文主要結合 redux 源碼分析 redux 原理,幫忙大家更好的使用 redux。
2. Redux 核心概念
2.1 Store
Store 在中文裡是儲備的意思,Store 其實就是一個保存數據的倉庫,按照 redux 設計理念,Store 只能有一個。下面是經過簡化的 createStore 源碼,方便大家理解。
// reducer 是純函數nexport default function createStore(reducer) {n //當前Reducern let currentReducer = reducern //當前staten let currentStatenn //獲取當前staten function getState() {n return currentStaten }nn /**n * 用來觸髮狀態改變n *n * @param {Object} action n *n * @returns {Object} 原來actionn */n function dispatch(action) {n currentState = currentReducer(currentState, action)n return actionn }nn // 初始化 state tree.n dispatch({ type: ActionTypes.INIT })nn return {n dispatch,n subscribe,n getStaten }n} n
Store 是一個由 createStore 函數創建生成的對象,包括了 dispatch, subscribe, getState等屬性。createStore 第一個參數是純函數 reducer,經過 dispatch 初始化,調用 reducer 函數生成了新的 State。
2.2 State
Store 包括了所有的數據,如果要得到某個時點的數據,就要對 Store 生成快照。這個時點的數據集合,就叫 State。根據上文的源碼,我們知道通過調用 store.getState() 就能獲取到當前的 state 對象。
2.3 Action
用戶不能修改 view, 不能直接修改 state,於是就有了 action 的概念。action 是一個對象,其中 type 屬性是必須的。
/**n * 用來觸髮狀態改變n *n * @param {Object} action n *n * @returns {Object} 原來actionn */n function dispatch(action) {n currentState = currentReducer(currentState, action)n return actionn }n
dispatch(action) 後,會調用當前的 reducer 根據 state 和 action ,生成新的 state;
2.4 Action Creator
如果每一個Action都需要手寫,會非常麻煩,可以定義一個函數生成 Action,這個函數就叫 Action Creator,下面是用法示例。
const ADD_TODO = 添加 TODO;nnfunction addTodo(text) {n return {n type: ADD_TODO,n textn }n}nnconst action = addTodo(Learn Redux);n
2.5 bindActionCreator
這個函數作用是簡化 store.dispatch 調用, 將一個或一組actionCreator跟 dispatch 綁定
//將dispatch 綁定到 actionCreatornfunction bindActionCreator(actionCreator, dispatch) {n return (...args) => dispatch(actionCreator(...args))n}nnexport default function bindActionCreators(actionCreators, dispatch) {n // 如果actionCreators是一個函數,直接返回 bindActionCreator處理後的函數n if (typeof actionCreators === function) {n return bindActionCreator(actionCreators, dispatch)n }nn if (typeof actionCreators !== object || actionCreators === null) {n throw new Error(n `bindActionCreators expected an object or a function, instead received ${actionCreators === null ? null : typeof actionCreators}. ` +n `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`n )n }nn const keys = Object.keys(actionCreators)n const boundActionCreators = {}n // 遍歷對象,然後對每個遍歷項的 actionCreator 生成函數,將函數按照原來的 key 值放到一個對象中,最後返回這個對象n for (let i = 0; i < keys.length; i++) {n const key = keys[i]n const actionCreator = actionCreators[key]n if (typeof actionCreator === function) {n boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)n } else {n warning(`bindActionCreators expected a function actionCreator for key ${key}, instead received type ${typeof actionCreator}.`)n }n }n return boundActionCreatorsn}n
2.6 Store.dispatch
定義了 Action,那麼如何執行 Action呢?dispatch 是 Redux 執行 action 唯一方法。下面是 dispatch 函數全部源碼,整個邏輯非常簡單。使用 reducer 函數處理當前 state,處理完成後,執行訂閱的 listeners。
function dispatch(action) {n //檢查是否是純粹的objectn if (!isPlainObject(action)) {n throw new Error(n Actions must be plain objects. +n Use custom middleware for async actions.n )n }n //檢查action.type是否定義n if (typeof action.type === undefined) {n throw new Error(n Actions may not have an undefined "type" property. +n Have you misspelled a constant?n )n }nn //如果正在dispath,拋出錯誤n if (isDispatching) {n throw new Error(Reducers may not dispatch actions.)n }nn try {n isDispatching = truen //調用currentReducer傳給curentStaten currentState = currentReducer(currentState, action)n } finally {n isDispatching = falsen }nn //將下一個監聽器傳給當前監聽器,使用for循環依次執行listenern const listeners = currentListeners = nextListenersn for (let i = 0; i < listeners.length; i++) {n const listener = listeners[i]n listener()n }nn return actionn }n
2.7 Reducer
Store 收到 Action 之後,需要計算一個新的 Action, 這種計算 State 變化的函數,就叫 Reducer。
const chatReducer = (state = defaultState, action = {}) => {n const { type, payload } = action;n switch (type) {n case ADD_CHAT:n return Object.assign({}, state, {n chatLog: state.chatLog.concat(payload)n });n case CHANGE_STATUS:n return Object.assign({}, state, {n statusMessage: payloadn });n case CHANGE_USERNAME:n return Object.assign({}, state, {n userName: payloadn });n default: return state;n }n};n
2.8 combineReducers
一個大型應用是由很多組件組成的,如果全部寫到一個 reducers 方法,不易維護。通過 redux 提供的 combineReducers 方法可以將多個 reducers 合併成一個 reducers。
/**n * 這個函數可以組合一組 reducers(對象) ,然後返回一個新的 reducer 函數給 createStore 使用。n *n * @param {Object} reducers reducers 組成的對象n *n * @returns {Function} 返回一個組合後的reducer .n */nexport default function combineReducers(reducers) {n const reducerKeys = Object.keys(reducers)n const finalReducers = {}n for (let i = 0; i < reducerKeys.length; i++) {n const key = reducerKeys[i]n //如果reducers[key]是函數,保存到finalReducersn if (typeof reducers[key] === function) {n finalReducers[key] = reducers[key]n }n }n const finalReducerKeys = Object.keys(finalReducers)nn //返回新合併的Reducernn return function combination(state = {}, action) {nn let hasChanged = falsen const nextState = {}n //用for循環遍歷,根據key取出的reducer和舊的state,然後執行reduder函數獲取新的state。如果state有變化,返回新的staten for (let i = 0; i < finalReducerKeys.length; i++) {n const key = finalReducerKeys[i]n const reducer = finalReducers[key]n const previousStateForKey = state[key]n const nextStateForKey = reducer(previousStateForKey, action)n nextState[key] = nextStateForKeyn hasChanged = hasChanged || nextStateForKey !== previousStateForKeyn }n return hasChanged ? nextState : staten }n}n
3. 流程圖:
推薦閱讀:
※如何評價 Airbnb 發布的 React Sketch.app 工具?
※前端新人的迷茫?
※CSS Modules入門Ⅲ:與React協同
※react中使用echarts就這麼簡單
※Rx 的編程方式(一)
TAG:React |