對於react技術棧的前端同窗來講,redux應該是相對熟悉的。其代碼之精簡和設計之巧妙,一直爲你們所推崇。此外redux的註釋簡直完美,閱讀起來比較省事。本來也是強行讀了通源碼,如今也忘得差很少了。由於最近打算對redux進行些操做,因此又開始重讀了redux,收益匪淺。前端
關於redux的基本概念,這裏就再也不詳細描述了。能夠參考Redux 中文文檔。react
有不少大牛已經提供了不少閱讀經驗。
我的感受一開始就強行讀源碼是不可取的,就像我當初讀的第一遍redux,只能說食之無味,如今全忘了。redux
應該是對其基礎用法比較熟練以後,有問題或者有興趣時再讀比較好,結合文檔或者實例,完整的流程走一走。數組
此外直接源碼倉庫clone下來,本地跑一跑,實在看不懂的斷點跟進去。app
對於不理解的地方,多是某些方法不太熟悉,這時候多去找找其具體用法和目的框架
實在不明白的能夠結合網上已有的源碼實例,和別人的思路對比一下,看本身哪裏理解有誤差。函數
一句話,但願讀過以後對本身有啓發,更深刻的理解和學習,而非只是提及來讀過而已。學習
redux 提供了以下方法:ui
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose
}
複製代碼
下面的文章就是按照Redux 中文文檔例子的順序,來分別看下各方法的實現。spa
action 本質上是 JavaScript 普通對象 在 Redux 中的 actionCreater就是生成 action 的方法
//addTodo 就是actionCreater
function addTodo(text) {
//return的對象即爲action
return {
type: ADD_TODO,
text
}
}
複製代碼
在 傳統的 Flux 實現中,當調用 action 建立函數時
通常會觸發一個 dispatch
Redux 中只需把 action 建立函數的結果傳給 dispatch() 方法便可發起一次 dispatch 過程。
dispatch(addTodo(text))
//或者
const boundAddTodo = text => dispatch(addTodo(text))
複製代碼
固然實際使用的時候,通常狀況下(這裏指的是簡單的同步actionCreater)咱們不須要每次都手動dispatch,
react-redux 提供的 connect() 會幫咱們來作這個事情。
裏面經過bindActionCreators() 能夠自動把多個 action 建立函數 綁定到 dispatch() 方法上。
這裏先不涉及connect,咱們一塊兒看看bindActionCreators如何實現的。
在看以前,咱們能夠大膽的猜一下,若是是咱們要提供一個warper,將兩個方法綁定在一塊兒會怎麼作:
function a (){
/*.....*/
};
function b(f){
/*.....*/
return f()
}
複製代碼
b裏面調用a(先不考慮其餘),經過一個c來綁定一下
function c(){
return ()=> b(a)
}
複製代碼
應該就是這麼個樣子,那麼看一下具體實現
先看源碼:
// 綁定單個actionCreator
function bindActionCreator(actionCreator, dispatch) {
//將方法dispatch中,避免了action建立手動調用。
return (...args) => dispatch(actionCreator(...args))
}
export default function bindActionCreators(actionCreators, dispatch) {
// function 說明是單個的actionCreator 直接調用bindActionCreator
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
// 校驗,不然拋錯
if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(`錯誤提示`)
}
//獲取keys數組,以便遍歷
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
}
複製代碼
該方法分爲兩部分
對單個ActionCreator方法封裝
function bindActionCreator(actionCreator, dispatch) {
//將方法dispatch中,避免了action建立手動調用。
return (...args) => dispatch(actionCreator(...args))
}
複製代碼
bindActionCreators的actionCreators指望是個對象,即actionCreator,
能夠想到下面確定是對該對象進行屬性遍歷,依次調用bindActionCreator
// function 說明是單個的actionCreator 直接調用bindActionCreator
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
// 校驗,不然拋錯
if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(`錯誤提示`)
}
//獲取keys數組,以便遍歷
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)
}
}
複製代碼
這樣咱們得到了綁定以後的 actionCreators,無需手動調用dispatch(同步的簡單狀況下)
action 只是描述了有事發生及提供源數據,具體如何作就須要reducer來處理(詳細介紹就略過了)。
在 Redux 應用中,全部的 state 都被保存在一個單一對象中
當reducer處理多個atcion時,顯得比較冗長,須要拆分,以下這樣:
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
case ADD_TODO:
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
})
case TOGGLE_TODO:
return Object.assign({}, state, {
todos: state.todos.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
})
default:
return state
}
}
複製代碼
須要拆分的時候,每一個reducer只處理相關部分的state相比於所有state應該更好, 例如:
//reducer1 中
function reducer1(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return state.reducer1.a
//相比較state只是state.reducer1,顯然好一點
return state.a
}
複製代碼
每一個 reducer 只負責管理全局 state 中它負責的一部分。
每一個 reducer 的 state 參數都不一樣,分別對應它管理的那部分 state 數據
這樣須要在主函數裏,分別對子reducer的入參進行管理,能夠以下面這樣:
function todoApp(state = {}, action) {
return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: todos(state.todos, action)
}
}
複製代碼
固然redux提供了combineReducers()方法
import { combineReducers } from 'redux'
const todoApp = combineReducers({
visibilityFilter,
todos
})
複製代碼
那麼咱們來看下combineReducers是如何來實現的
仍是把完整的代碼放上來
export default function combineReducers(reducers) {
// 獲取reducer的key 不做處理的話是子reducer的方法名
var reducerKeys = Object.keys(reducers)
var finalReducers = {}
// 遍歷 構造finalReducers即總的reducer
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
var finalReducerKeys = Object.keys(finalReducers)
var sanityError
try {
// 規範校驗
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)
if (warningMessage) {
warning(warningMessage)
}
}
/** * 當有action改變時, * 遍歷finalReducers,執行reducer並賦值給nextState, * 經過對應key的state是否改變決定返回當前或者nextState * */
// state改變與否的flag
var hasChanged = false
var nextState = {}
// 依次處理
for (var i = 0; i < finalReducerKeys.length; i++) {
var key = finalReducerKeys[i]
var reducer = finalReducers[key]
// 獲取對應key的state屬性
var previousStateForKey = state[key]
// 目的之一,只處理對應key數據
var nextStateForKey = reducer(previousStateForKey, action)
// 不能返回undefined,不然拋錯
if (typeof nextStateForKey === 'undefined') {
var errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
// 新狀態賦給 nextState對象
nextState[key] = nextStateForKey
// 是否改變處理
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
// 視狀況返回state
return hasChanged ? nextState : state
}
}
複製代碼
首先看一下入參:reducers
既然是個對象集合,確定要遍歷對象,因此前幾步就是這麼個操做。
// 獲取reducer key 目的在於每一個子方法處理對應key的state
var reducerKeys = Object.keys(reducers)
var finalReducers = {}
// 遍歷 構造finalReducers即總的reducer
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
//獲取finalReducers 供下面遍歷調用
var finalReducerKeys = Object.keys(finalReducers)
複製代碼
而後是規範校驗,做爲一個框架這是必須的,能夠略過
返回一個function
當action被dispatch進來時,該方法主要是分發不一樣state到對應reducer處理,並返回最新state
先是標識變量:
// state改變與否的flag
var hasChanged = false
var nextState = {}
複製代碼
進行遍歷finalReducers 保存原來的previousStateForKey
而後分發對應屬性給相應reducer進行處理獲取nextStateForKey
先對nextStateForKey 作個校驗,由於reducer要求作兼容的,因此不容許undefined的出現,出現就拋錯。
正常的話就nextStateForKey把賦給nextState對應的key
先後兩個state作個比較看是否相等,相等的話hasChanged置爲true 遍歷結束以後就得到了一個新的state即nextState
for (var i = 0; i < finalReducerKeys.length; i++) {
var key = finalReducerKeys[i]
var reducer = finalReducers[key]
// 獲取對應key的state屬性
var previousStateForKey = state[key]
// 目的之一,只處理對應key數據
var nextStateForKey = reducer(previousStateForKey, action)
// 不能返回undefined,不然拋錯
if (typeof nextStateForKey === 'undefined') {
//.....
}
// 新狀態賦給 nextState對象
nextState[key] = nextStateForKey
// 是否改變處理
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
複製代碼
根據hasChanged來決定返回新舊state。
// 視狀況返回state
return hasChanged ? nextState : state
複製代碼
到這裏combineReducers就結束了。
此次先分享一半,仍是有點多的,剩下的下次再記錄一下。拋磚引玉,提高本身,共同窗習吧。