redux
的源碼雖然代碼量並很少(除去註釋大概300行吧)。可是,由於函數式編程的思想在裏面體現得淋漓盡致,理解起來並不太容易,因此準備使用三篇文章來分析。html
redux
的核心思想和實現,並用100多行的代碼實現了其核心功能,相信看完以後,你會徹底理解 redux
的核心。這裏甩掉 combindReducers
和 applyMiddleware
,不會涉及很高深的柯里化、高階、歸併的思想,可是須要你對閉包有必定的理解。其實,redux
源碼自己並不可怕,可怕的是網上太多文章把他和函數式放在一塊兒來分析(裝逼)了!!!嚇得咱們一看到就想跑了。redux
的核心以後, 咱們會分析 reducers
合併(即 combindReducers
)的實現。applyMiddleware
)的實現,這是最體現函數式風格的地方,並實現一個處理異步請求的 promise
中間件。在解讀 redux
源碼以前,咱們首先要弄清楚一個問題,就是 redux
和 react-redux
不是同一個東東。 react-redux
是爲 react
而定製的,主要是提供 Provider
組件和 connect
方法,方便於咱們把 redux
和 react組件
綁定起來。可是, redux
是沒有限制說必定要跟 react
一塊兒使用的。本文只介紹 redux
,不涉及 react
或者 react-redux
。由於我以爲,若是把 redux
和 react
放在一塊兒討論,反而會加深了理解的複雜度,分散了咱們的注意力,從而影響咱們分析源碼進度。如今要分析 redux
源碼,那就只專一於 redux
,甩開 react
, 就連後面的測試例子,也不要引入 react
,就簡單的使用原生html和js測試一下就OK了。react
什麼是 redux
呢?, 這裏也不展開介紹了。就簡單的回顧一下 redux
的具體用法:git
reducer
函數redux.createStore(reducer)
方法建立 store
實例store.subscribe(callback)
方法訂閱回調事件(即狀態變化時會觸發回調函數callback)store.dispatch(action)
, 改變 store
的狀態可能用些朋友會說,我歷來沒有用過 store.subscribe
啊,那是由於你使用了 react-redux
, 在 connet()
的時候幫你作了這一步。好吧,說好了不扯 react
的。那下面咱們就就一步步的來實現 redux
的核心功能吧。github
首先來看一下 createStore
, 咱們平時的用法以下:編程
const store = createStore(reducer, preloadedState, enhance)
能夠接受3個參數,第一個是自定義的reducer
函數, 第二個是初始狀態,第三個是加強器(即 applyMiddleware()
返回的東西),由於前面已經說過了,這裏咱們不會涉及到 applyMiddleware
,因此,咱們的 createStore
只接收2個參數,以下:redux
function createStore(reducer, preloadedState) { if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') } // 定義一些變量,後面幾乎全部的方法都會用到,這就是閉包的力量! let currentState = preloadedState // state let listeners = [] // 訂閱事件列表 let isDispatching = false // 是否正在執行reducer }
createStore
參數和可能會用到的變量定義好了,咱們須要實現三個函數,分別是 store.getState
、store.subscribe
和 store.dispatch
。數組
首先來實現 store.getState
方法,這個方法沒有好說的,就是把閉包裏面的 currentState
返回出去就好了,代碼以下:promise
function createStore(reducer, preloadedState) { // 省略和上面重複的代碼 // 獲取state function getState() { // 若是正在執行reducer,則拋出異常 if (isDispatching) { throw new Error('You may not call store.getState() while the reducer is executing. ') } return currentState; } }
接着咱們來實現 store.subscribe
。這個方法是用來添加訂閱回調函數的。首先要判斷傳進來的參數是否是函數類型,而後,把他它push到回調隊列(數組)裏面。由於可能後面須要把這個回調取消掉,因此還要返回一個方法給外部調用取消,實現代碼以下:閉包
function createStore(reducer, preloadedState) { // 省略和上面重複的代碼 // 添加訂閱事件 function subscribe(listener) { if(typeof listener !== 'function') { throw new Error('Expected the listener to be a function.') } let isSubscribed = true; listeners.push(listener); // 返回一個取消訂閱事件的函數 return function unsubscribe() { if(!isSubscribed) { return; } if(isDispatching) { throw new Error('You may not unsubscribe from a store listener while the reducer is executing. '); } isSubscribed = false; const index = listeners.indexOf(listener); listeners.splice(index, 1); } } }
最後,咱們再來看一下 store.dispatch
方法的實現。 dispatch
接受的參數類型是一個 action
。咱們來回顧一下 action
是什麼鬼?他要求是一個原生對象,並且必需要有 type
屬性,還有可能有 payload
屬性。以下是咱們的一個用法 :app
store.dispatch({ type: 'ADD_SHOPPING', payload: 1 })
調用store.dispatch(action)
, 它的返回值也是 action
。下面代碼是 store.dispatch()
的實現:
function createStore(reducer, preloadedState) { // 省略和上面重複的代碼 function dispatch(action) { // 若是action不是原生對象,則拋出異常 // 由於咱們期待的action結構爲"{type: 'xxx', payload: 'xxx'}"的原生對象 if(Object.prototype.toString.call(action, null) !== '[object Object]') { throw new Error('Actions must be plain objects. '); } if(typeof action.type === 'undefined') { throw new Error('Actions may not have an undefined "type" property. ') } if(isDispatching) { throw new Error('Reducers may not dispatch actions.') } // 開始調用reducer獲取新狀態。由於可能會出錯須要用try-catch // 而且無論成功失敗,執行完畢後都要設置isDispatching=true try { isDispatching = true; currentState = reducer(currentState, action); } finally { isDispatching = false; } // 遍歷全部經過store.subscribe()綁定的的訂閱事件,並調用他們 listeners.forEach((listener) => { listener(); }) return action; } }
關於 redux
的分析就寫到這裏的了。下面是前面分析的代碼整合到了一塊兒。
function createStore(reducer, preloadedState) { if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') } let currentState = preloadedState // state let listeners = [] // 訂閱事件列表 let isDispatching = false // 是否正在執行reducer function getState() { // 若是正在執行reducer,則拋出異常 if (isDispatching) { throw new Error('You may not call store.getState() while the reducer is executing. ') } return currentState; } // 添加訂閱事件 function subscribe(listener) { if(typeof listener !== 'function') { throw new Error('Expected the listener to be a function.') } let isSubscribed = true; listeners.push(listener); // 返回一個取消訂閱事件的函數 return function unsubscribe() { if(!isSubscribed) { return; } if(isDispatching) { throw new Error('You may not unsubscribe from a store listener while the reducer is executing. '); } isSubscribed = false; const index = listeners.indexOf(listener); listeners.splice(index, 1); } } function dispatch(action) { // 若是action不是原生對象,則拋出異常 // 由於咱們期待的action結構爲"{type: 'xxx', payload: 'xxx'}"的原生對象 if(!isPlainObject(action)) { throw new Error('Actions must be plain objects. '); } if(typeof action.type === 'undefined') { throw new Error('Actions may not have an undefined "type" property. ') } if(isDispatching) { throw new Error('Reducers may not dispatch actions.') } // 開始調用reducer獲取新狀態。由於可能會出錯須要用try-catch // 而且無論成功失敗,執行完畢後都要設置isDispatching=true try { isDispatching = true; currentState = reducer(currentState, action); } finally { isDispatching = false; } // 遍歷全部經過store.subscribe()綁定的的訂閱事件,並調用他們 listeners.forEach((listener) => { listener(); }) return action; } // 將getState, subscribe, dispatch這三個方法暴露出去 // 建立了store實例以後,能夠store.getState()、store.subscripbe()... return { getState, subscribe, dispatch } }
完整的代碼和測試例子,能夠到個人github下載 點擊進入simplest-redux 。若是以爲我分析得還不是太清楚的,建議把github上的代碼clone下來,本身多看幾遍,並在demo中運行調試幾下就會明白的了。