标签(空格分隔): 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 可以进行如创建 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 = []; //这里放着所有的监听函数。
var isDispatching = false;
//获取当前的state。
function getState() { ... }
//注册一个监听的方法,当state改变时会执行所有的listeners。
function subscribe(listener) { ... }
//dispatch a action。
function dispatch(action) { ... }
//替换reducer。
function replaceReducer(nextReducer) { ... }
return {
dispatch,
subscribe,
getState,
replaceReducer
};
}
redux 是一个状态管理器, store 就是存储状态的地方,有几个方法用于交互, createStore 是用来创建 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,然后执行 listeners 中的方法, 在这些方法中使用 getState() 获取最新的 state, 然后执行下一步操作。
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) 发生时会执行所有的 listener, 在其中执行 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) 方法中执行 currentReducer(currentState, action) 得到一个新的 state,然后执行所有的 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;
}
//执行所有的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
}
}
//--------------------------------------------------
/**
* 先来创建一个 store,直接调用 createStore 方法,传递一个 reducer 和一个 state。
* reducer 是一个函数,形式是 (state, action) => action。
* state 可以是一个对象,数组,或者一个变量也可以。
*/
const store = createStore(reducer, []);
//来看一下当前的state
store.getState() //返回[]。
//执行一次dispatch后state会发生变化
store.dispatch(push)
store.getState() //返回['HELLO']
//再次执行dispatch
store.dispatch(push)
store.getState() //返回['HELLO', 'HELLO']
store.dispatch(pop)
store.getState() //返回['HELLO']
//添加一个listener
let unsubscribe = store.subscribe(function(){
console.log('我是一个listener, 我被执行了')
})
//dispatch一个不会改变 state 的 action,因为在 reducer 没有定义 type 为 UNKOWN 的处理
store.dispatch({type: 'UNKOWN'}) //console 输出我是一个listener, 我被执行了
//删除这个listener
unsubscribe()
store.dispatch({type: 'UNKOWN'}) //console 没有输出
//替换一个reducer
store.replaceReducer(anotherReducer)
store.dispatch(push)
store.getState() //返回['HELLO', 'WORLD']
//--------------------------------------------------
总结
createStore 会创建一个 store,store 中保持着一个 state ,一个 reducer 和一组 listener, 可以使用 store.subscribe(listener) 添加一个 listener, 也可以删除这个 listener。 每当执行 store.dispatch(action) 的时候,就会执行 reducer, reducer 返回一个新的 state(不是修改原来的 state), 替换到原来的 state, 然后执行所有的 listener, 告知 state 已经更新了。