標簽(空格分隔): redux javascirpt createStore
導語:
最近在看 redux,也看了源碼,于是就寫一點東西來記錄一下這個過程展父。
先看一下redux源碼的目錄結構
src
-utils
: applyMiddleware.js
bindAction.js
combineReducers.js
compose.js
isPlainObject.js
mapValues.js
pick.js
createStore.js
index.js
redux的代碼量不多芭概,結構也比較清晰,讓我們先從index.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
};
index.js export了5個方法,依次為
- createStore 可以進行如創(chuàng)建 store宾肺, 獲取當前的 state溯饵, 注冊store的事件, dispatch action 等 store 相關操作锨用。
- combineReducers 可以將多個 reducer 組合為一個 root reducer丰刊。
- bindActionCreators 將store.dispatch 和 action creater 組合在一起,簡化了調用增拥。
- applyMiddleware 可以為redux增加的中間件啄巧,類似express的中間件,比如添加一個log掌栅。
- compose
讓我們先看一看 createStore.js 是個什么鬼秩仆。
createStore.js的代碼結構如下(不是全部的代碼,也沒必要都寫出來):
...
export default function createStore(reducer, initialState) {
...
var currentReducer = reducer; //這個是 store 綁定的 reducer猾封。
var currentState = initialState; //在這里存儲 state澄耍。
var listeners = []; //這里放著所有的監(jiān)聽函數。
var isDispatching = false;
//獲取當前的state晌缘。
function getState() { ... }
//注冊一個監(jiān)聽的方法齐莲,當state改變時會執(zhí)行所有的listeners。
function subscribe(listener) { ... }
//dispatch a action磷箕。
function dispatch(action) { ... }
//替換reducer选酗。
function replaceReducer(nextReducer) { ... }
return {
dispatch,
subscribe,
getState,
replaceReducer
};
}
redux 是一個狀態(tài)管理器, store 就是存儲狀態(tài)的地方岳枷,有幾個方法用于交互芒填, createStore 是用來創(chuàng)建 stroe 的, 接受兩個參數:
- reducer 是一個形如 (previoustState, action) => nextState 的 pure function,可以是一個 reducer嫩舟,也可以是多個 reducer 經過 combineReducer 之后的結果氢烘。
- initialState 一個 plain Object,可以來自一個json文件家厌,服務器端渲染時的初始數據等。
crreateStore 將 reducer 賦值給 currentReducer椎工, 將 initialState 賦值給 currentState饭于。 當調用 store.dispatch(action) 時蜀踏,currentReducer 根據 currentState 和 action 生成一個新的 state,然后執(zhí)行 listeners 中的方法掰吕, 在這些方法中使用 getState() 獲取最新的 state果覆, 然后執(zhí)行下一步操作。
View->Reducer: store.dispatch(action)
Reducer->Store: reducer(state, action) => state
Store-->View: listens.forEach( listener => listener() )
讓我們來具體看一下各個方法的代碼殖熟。
getState 就是 return currentState局待。
function getState() {
return currentState;
}
subscribe 向 listeners 中添加一個 listener(類型是function),當 store.dispatch(action) 發(fā)生時會執(zhí)行所有的 listener菱属, 在其中執(zhí)行 store.getState() 就可以獲取最新的 state 了钳榨。
subscribe 返回一個刪除當前 listener 的方法。
function subscribe(listener) {
listeners.push(listener);
return function unsubscribe() {
var index = listeners.indexOf(listener);
listeners.splice(index, 1);
}
}
//添加一個 listener纽门, 打印當前的 state薛耻。
let consoleState = store.subscribe( function(){
console.log(store.getState());
})
//刪除這個 listener
consoleState()
dispatch(action) 方法中執(zhí)行 currentReducer(currentState, action) 得到一個新的 state,然后執(zhí)行所有的 listeners赏陵。
function dispatch(action) {
// action 必須是一個 plainObject饼齿。
if (!isPlainObject(action)) {throw new Error(...)}
// action.type 要有一個 type 屬性,來表明 action 的類型蝙搔。
if (typeof action.type === 'undefined') {throw new Error(...)}
// 不能在 reducer 中 dispath an action缕溉。
if (isDispatching) {throw new Error(...)}
try{
isDispatching = true;
//得到一個新的state
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
//執(zhí)行所有的listener
listeners.slice().forEach(listener => listener());
return actioin;
}
replaceReducer 用于替換 currentReducer
function replaceReducer(nextReducer) {
currentReducer = nextReducer;
dispatch({type: ActionTypes.INIT});
}
dispatch({type: ActionTypes.INIT}); 使用 reducer 的 initial state 初始化 store。
dispatch({type: ActionTypes.INIT});
return {
dispatch,
subscribe,
getState,
replaceReducer
};
這就是 createStore 的基本代碼結構吃型,此外源碼中還有一個常量, 是 redux 內置的一個 action证鸥。
export var ActionTypes = {
INIT: '@@redux/INIT'
};
讓我們來看一點例子,
//先來兩個常量
const PUSH = 'PUSH';
const POP = 'POP';
//這是一個action败玉,叫push
let push = {
type: PUSH,
data: 'HELLO'
}
//這是一個action敌土,叫pop
let pop = {
type: POP
}
//這是一個reducer
let reducer = function(state, action) {
switch (action.type) {
case PUSH:
return state.concat(action.data)
case POP:
return state.slice(0, -1)
default:
return state
}
}
//再來一個rducer
let anotherReducer = function(state, action) {
switch (action.type) {
case PUSH:
return state.concat('WORLD')
default:
return state
}
}
//--------------------------------------------------
/**
* 先來創(chuàng)建一個 store,直接調用 createStore 方法运翼,傳遞一個 reducer 和一個 state返干。
* reducer 是一個函數,形式是 (state, action) => action血淌。
* state 可以是一個對象矩欠,數組,或者一個變量也可以悠夯。
*/
const store = createStore(reducer, []);
//來看一下當前的state
store.getState() //返回[]癌淮。
//執(zhí)行一次dispatch后state會發(fā)生變化
store.dispatch(push)
store.getState() //返回['HELLO']
//再次執(zhí)行dispatch
store.dispatch(push)
store.getState() //返回['HELLO', 'HELLO']
store.dispatch(pop)
store.getState() //返回['HELLO']
//添加一個listener
let unsubscribe = store.subscribe(function(){
console.log('我是一個listener, 我被執(zhí)行了')
})
//dispatch一個不會改變 state 的 action沦补,因為在 reducer 沒有定義 type 為 UNKOWN 的處理
store.dispatch({type: 'UNKOWN'}) //console 輸出我是一個listener乳蓄, 我被執(zhí)行了
//刪除這個listener
unsubscribe()
store.dispatch({type: 'UNKOWN'}) //console 沒有輸出
//替換一個reducer
store.replaceReducer(anotherReducer)
store.dispatch(push)
store.getState() //返回['HELLO', 'WORLD']
//--------------------------------------------------
總結
createStore 會創(chuàng)建一個 store,store 中保持著一個 state 夕膀,一個 reducer 和一組 listener虚倒, 可以使用 store.subscribe(listener) 添加一個 listener美侦, 也可以刪除這個 listener。 每當執(zhí)行 store.dispatch(action) 的時候魂奥,就會執(zhí)行 reducer菠剩, reducer 返回一個新的 state(不是修改原來的 state), 替換到原來的 state耻煤, 然后執(zhí)行所有的 listener具壮, 告知 state 已經更新了。