Redux之源码分析(九)
# 一、index.js
- 暴露了几个核心
API
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
};
@前端进阶之旅: 代码已经复制到剪贴板
# 二、createStore.js
https://github.com/reactjs/redux/blob/master/src/createStore.js
redux.createStore(reducer, initialState)传入了reducer、initialState,并返回一个store对象store对象对外暴露了dispatch、getState、subscribe方法store对象通过getState()获取内部状态initialState为store的初始状态,如果不传则为undefinedstore对象通过reducer来修改内部状态store对象创建的时候,内部会主动调用dispatch({ type: ActionTypes.INIT });来对内部状态进行初始化。通过断点或者日志打印就可以看到,store对象创建的同时,reducer就会被调用进行初始化
# 三、combineReducers.js
https://github.com/reactjs/redux/blob/master/src/combineReducers.js
-
redux.combineReducers(reducerMap)的作用在于合并多个reducer函数,并返回一个新的reducer函数。因此可以看到,combineReducers返回了一个函数,并且该函数的参数同样是state、reducer -
最终
store.getState()返回的state,大概会是这么个样子{todos: xx, filter: xx}。简单的说,state被拆分成了两份,TodoReducer的返回值赋值给了state.todos,FilterReducer的返回值赋值给了state.filter
function TodoReducer(state, action) {}
function FilterReducer(state, action) {}
var finalReducers = redux.combineReducers({
todos: TodoReducer,
filter: FilterReducer
});
@前端进阶之旅: 代码已经复制到剪贴板
combineReducers(reducerMap)传入一个对象,并返回一个全新的reducer。调用方式跟跟普通的reducer一样,也是传入state、action- 通过
combineReducers,对store的状态state进行拆分 reducerMap的key,就是state的key,而 调用对应的reducer返回的值,则是这个key对应的值。如上面的例子,state.todos == TodoReducer(state, action)redux.createStore(finalReducers, initialState)调用时,同样会对state进行初始化。这个初始化跟通过普通的reducer进行初始化没多大区别。举例来说,如果initialState.todos = undefined,那么TodoReducer(state, action)初始传入的state就是undefined;如果initialState.todos = [],那么TodoReducer(state, action)初始传入的state就是[]store.dispatch(action),finalReducers里面,会遍历整个reducerMap,依次调用每个reducer,并将每个reducer返回的子state赋给state对应的key。
# 四、bindActionCreator.js
https://github.com/reactjs/redux/blob/master/src/bindActionCreators.js
- 先看个简单例子可能方便理解一些
var addTodo = function(text){
return {
type: 'add_todo',
text: text
};
};
var addTodos = function(){
return {
type: 'add_todos',
items: Array.prototype.slice.call(arguments, 0)
};
};
var reducer = function(state, action){
switch (action.type) {
case 'add_todo':
return state.concat(action.text);
case 'add_todos':
return state.concat(action.items);
default:
return state;
}
};
var store = redux.createStore(reducer, []);
// 注意,关键代码在这里
var actions = redux.bindActionCreators({
addTodo: addTodo,
addTodos: addTodos
}, store.dispatch);
console.log('state is: ' + store.getState());
store.dispatch({type: 'add_todo', text: '读书'});
store.dispatch({type: 'add_todos', items: ['阅读', '睡觉']});
console.log('state is: ' + store.getState()); // state is: 读书,阅读,睡觉
actions.addTodo('看电影');
console.log('state is: ' + store.getState()); // state is: 读书,阅读,睡觉,看电影
actions.addTodos(['刷牙', '洗澡']);
console.log('state is: ' + store.getState()); // state is: 读书,阅读,睡觉,看电影,刷牙,洗澡
@前端进阶之旅: 代码已经复制到剪贴板
- 直接看代码
# 五、applyMiddleware.js
https://github.com/reactjs/redux/blob/master/src/applyMiddleware.js
- 中间件应该是
redux源码里面最绕的一部分
例子:redux-thunk
export default function thunkMiddleware({ dispatch, getState }) {
return next => action =>
typeof action === 'function' ?
action(dispatch, getState) :
next(action);
}
@前端进阶之旅: 代码已经复制到剪贴板
//es5
function thunkMiddleware(store) {
var dispatch = store.dispatch;
var getState = store.getState;
return function (next) {
return function (action) {
return typeof action === 'function' ? action(dispatch, getState) : next(action);
};
};
}
@前端进阶之旅: 代码已经复制到剪贴板
自定义中间件:logger
- 先看
logger的实现
function middleware(store){
return function(next){
return function(action){
return next(action);
}
}
}
@前端进阶之旅: 代码已经复制到剪贴板
基本看出中间件声明的模版来了,就是下面这个样子。下面结合
applyMiddleware的调用,来说明store、next、action几个参数。
function logger(store){
return function(next){
return function(action){
console.log('logger: dispatching ' + action.type);
var result = next(action);
console.log('logger: next state ' + result);
return result;
}
}
}
@前端进阶之旅: 代码已经复制到剪贴板
applyMiddleware调用例子
applyMiddleware的调用方式为applyMiddleware(...middlewares)(react.createStore)。其实这里直接先创建store,然后applyMiddleware(...middlewares)(store)也很容易实现相同的效果,不过作者是故意这样设计的,为了避免在同一个store上多次应用同一个middlerware- 中间件顶层的
store参数,并不是常规的store,虽然它也有getState、dispatch两个方法
// 上面的store参数,其实就是这个对象
// 其中,store 为内部的store,我们在外面 storeWithMiddleWare.dipatch的时候,内部实现是转成 store.dispatch
// 此外,可以看到 middlewareAPI.dispatch 方法,是最终封装后的dispatch(千万注意,如果在中间件内部 调用 store.dispatch,可能导致死循环 )
var middlewareAPI = {
getState: store.getState,
// 最后面, dispatch 被覆盖, 变成包装后的 dispatch 方法
dispatch: (action) => dispatch(action)
};
@前端进阶之旅: 代码已经复制到剪贴板
- 第二层的next函数,其实是一个“dispatch”方法
- storeWithMiddleWare.dispatch(action) 的时候,会顺序进入各个中间件(按照定义时的顺序)。从当前的例子来看,大约如下,其实就是柯里化啦
storeWithMiddleWare.dispatch(action) --> logger(store)(next)(action) --> timer(store)(next)(action) --> store.dispatch(action)
@前端进阶之旅: 代码已经复制到剪贴板
完整的示例代码
源码解析