標(biāo)簽(空格分隔): redux javascirpt createStore
導(dǎo)語:
最近在看 redux,也看了源碼,于是就寫一點(diǎn)東西來記錄一下這個過程。
先看一下redux源碼的目錄結(jié)構(gòu)
src
-utils
: applyMiddleware.js
bindAction.js
combineReducers.js
compose.js
isPlainObject.js
mapValues.js
pick.js
createStore.js
index.js
redux的代碼量不多,結(jié)構(gòu)也比較清晰,讓我們先從index.js開始。
index.js的內(nèi)容
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 可以進(jìn)行如創(chuàng)建 store, 獲取當(dāng)前的 state, 注冊store的事件, dispatch action 等 store 相關(guān)操作。
- combineReducers 可以將多個 reducer 組合為一個 root reducer。
- bindActionCreators 將store.dispatch 和 action creater 組合在一起,簡化了調(diào)用。
- applyMiddleware 可以為redux增加的中間件,類似express的中間件,比如添加一個log。
- compose
讓我們先看一看 createStore.js 是個什么鬼。
createStore.js的代碼結(jié)構(gòu)如下(不是全部的代碼,也沒必要都寫出來):
...
export default function createStore(reducer, initialState) {
...
var currentReducer = reducer; //這個是 store 綁定的 reducer。
var currentState = initialState; //在這里存儲 state。
var listeners = []; //這里放著所有的監(jiān)聽函數(shù)。
var isDispatching = false;
//獲取當(dāng)前的state。
function getState() { ... }
//注冊一個監(jiān)聽的方法,當(dāng)state改變時(shí)會執(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 的, 接受兩個參數(shù):
- reducer 是一個形如 (previoustState, action) => nextState 的 pure function,可以是一個 reducer,也可以是多個 reducer 經(jīng)過 combineReducer 之后的結(jié)果。
- initialState 一個 plain Object,可以來自一個json文件,服務(wù)器端渲染時(shí)的初始數(shù)據(jù)等。
crreateStore 將 reducer 賦值給 currentReducer, 將 initialState 賦值給 currentState。 當(dāng)調(diào)用 store.dispatch(action) 時(shí),currentReducer 根據(jù) 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),當(dāng) store.dispatch(action) 發(fā)生時(shí)會執(zhí)行所有的 listener, 在其中執(zhí)行 store.getState() 就可以獲取最新的 state 了。
subscribe 返回一個刪除當(dāng)前 listener 的方法。
function subscribe(listener) {
listeners.push(listener);
return function unsubscribe() {
var index = listeners.indexOf(listener);
listeners.splice(index, 1);
}
}
//添加一個 listener, 打印當(dāng)前的 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 的基本代碼結(jié)構(gòu),此外源碼中還有一個常量, 是 redux 內(nèi)置的一個 action。
export var ActionTypes = {
INIT: '@@redux/INIT'
};
讓我們來看一點(diǎn)例子,
//先來兩個常量
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,直接調(diào)用 createStore 方法,傳遞一個 reducer 和一個 state。
* reducer 是一個函數(shù),形式是 (state, action) => action。
* state 可以是一個對象,數(shù)組,或者一個變量也可以。
*/
const store = createStore(reducer, []);
//來看一下當(dāng)前的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,因?yàn)樵?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']
//--------------------------------------------------
總結(jié)
createStore 會創(chuàng)建一個 store,store 中保持著一個 state ,一個 reducer 和一組 listener, 可以使用 store.subscribe(listener) 添加一個 listener, 也可以刪除這個 listener。 每當(dāng)執(zhí)行 store.dispatch(action) 的時(shí)候,就會執(zhí)行 reducer, reducer 返回一個新的 state(不是修改原來的 state), 替換到原來的 state, 然后執(zhí)行所有的 listener, 告知 state 已經(jīng)更新了。