内容简介:1、本文不涉及redux的使用方法,因此可能更适合使用过 redux 的同学阅读Redux作为大型React应用状态管理最常用的工具。虽然在平时的工作中很多次的用到了它,但是一直没有对其原理进行研究。最近看了一下源码,下面是我自己的一些简单认识,如有疑问欢迎交流。结合使用场景我们首先来看一下
1、本文不涉及redux的使用方法,因此可能更适合使用过 redux 的同学阅读
Redux作为大型React应用状态管理最常用的工具。虽然在平时的工作中很多次的用到了它,但是一直没有对其原理进行研究。最近看了一下源码,下面是我自己的一些简单认识,如有疑问欢迎交流。
1、createStore
结合使用场景我们首先来看一下 createStore
方法。
// 这是我们平常使用时创建store const store = createStore(reducers, state, enhance);
以下源码为去除异常校验后的源码,
export default function createStore(reducer, preloadedState, enhancer) {
// 如果有传入合法的enhance,则通过enhancer再调用一次createStore
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState) // 这里涉及到中间件,后面介绍applyMiddleware时在具体介绍
}
let currentReducer = reducer //把 reducer 赋值给 currentReducer
let currentState = preloadedState //把 preloadedState 赋值给 currentState
let currentListeners = [] //初始化监听函数列表
let nextListeners = currentListeners //监听列表的一个引用
let isDispatching = false //是否正在dispatch
function ensureCanMutateNextListeners() {}
function getState() {}
function subscribe(listener) {}
function dispatch(action) {}
function replaceReducer(nextReducer) {}
// 在 creatorStore 内部没有看到此方法的调用,就不讲了
function observable() {}
//初始化 store 里的 state tree
dispatch({ type: ActionTypes.INIT })
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
我们可以看到creatorStore方法除了返回我们常用的方法外,还做了一次初始化过程 dispatch({ type: ActionTypes.INIT })
;那么dispatch干了什么事情呢?
/**
* dispath action。这是触发 state 变化的惟一途径。
* @param {Object} 一个普通(plain)的对象,对象当中必须有 type 属性
* @returns {Object} 返回 dispatch 的 action
*/
function dispatch(action) {
// 判断 dispahch 正在运行,Reducer在处理的时候又要执行 dispatch
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
//标记 dispatch 正在运行
isDispatching = true
//执行当前 Reducer 函数返回新的 state
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = (currentListeners = nextListeners)
//遍历所有的监听函数
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener() // 执行每一个监听函数
}
return action
}
这里 dispatch
主要做了二件事情
-
通过
reducer更新state - 执行所有的监听函数,通知状态的变更
那么reducer是怎么改变state的呢?这就涉及到下面的 combineReducers
了
2、combineReducers
回到上面创建store的参数reducers,
// 两个reducer
const todos = (state = INIT.todos, action) => {
// ....
};
const filterStatus = (state = INIT.filterStatus, action) => {
// ...
};
const reducers = combineReducers({
todos,
filterStatus
});
// 这是我们平常使用时创建store
const store = createStore(reducers, state, enhance);
下面我们来看 combineReducers
做了什么
export default function combineReducers(reducers) {
// 第一次筛选,参数reducers为Object
// 筛选掉reducers中不是function的键值对
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
// 二次筛选,判断reducer中传入的值是否合法(!== undefined)
// 获取筛选完之后的所有key
let shapeAssertionError
try {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}
return function combination(state = {}, action) {
let hasChanged = false
const nextState = {}
// 遍历所有的key和reducer,分别将reducer对应的key所代表的state,代入到reducer中进行函数调用
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
// 这里就是reducer function的名称和要和state同名的原因,传说中的黑魔法
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
// 将reducer返回的值填入nextState
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
// 发生改变了返回新的nextState,否则返回原先的state
return hasChanged ? nextState : state
}
}
-
这里
reducer(previousStateForKey, action)执行的就是我们上面定义的todos和filterStatus方法。通过这二个reducer改变state值。 -
以前我一直很奇怪我们的
reducer里的state是怎么做到取当前reducer对应的数据。看到const previousStateForKey = state[key]这里我就明白了。 -
这里还有一个疑问点就是
combineReducers的嵌套,最开始也我不明白,看了源码才知道combineReducers()=> combination(state = {}, action),这里combineReducers返回的combination也是接受(state = {}, action)也就是一个reducer所以可以正常嵌套。
看到这初始化流程已经走完了。这个过程我们认识了 dispatch
和 combineReducers
;接下来我们来看一下我们自己要怎么更新数据。
用户更新数据时,是通过 createStore
后暴露出来的 dispatch
方法来触发的。 dispatch
方法,是 store
对象提供的更改 currentState
这个闭包变量的唯一建议途径( 注意这里是唯一建议途径,不是唯一途径,因为通过getState获取到的是state的引用,所以是可以直接修改的。但是这样就不能更新视图了
)。
正常情况下我们只需要像下面这样
//action creator
var addTodo = function(text){
return {
type: 'add_todo',
text: text
};
};
function TodoReducer(state = [], action){
switch (action.type) {
case 'add_todo':
return state.concat(action.text);
default:
return state;
}
};
// 通过 store.dispatch(action) 来达到修改 state 的目的
// 注意: 在redux里,唯一能够修改state的方法,就是通过 store.dispatch(action)
store.dispatch({type: 'add_todo', text: '读书'});// 或者下面这样
// store.dispatch(addTodo('读书'));
也就是说 dispatch
接受一个包含 type
的对象。框架为我们提供了一个创建 Action
的方法 bindActionCreators
3、bindActionCreators
下面来看下源码
// 核心代码,并通过apply将this绑定起来
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
export default function bindActionCreators(actionCreators, dispatch) {
// 如果actionCreators是一个函数,则说明只有一个actionCreator,就直接调用bindActionCreator
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
// 遍历对象,然后对每个遍历项的 actionCreator 生成函数,将函数按照原来的 key 值放到一个对象中,最后返回这个对象
const keys = Object.keys(actionCreators)
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
bindActionCreators
的作用就是使用 dispatch
把 action creator
包裹起来,这样我们就可以直接调用他们了。这个在平常开发中不常用。
4、applyMiddleware
最后我们回头来看一下之前调到的中间件,
import thunkMiddleware from 'redux-thunk';
// 两个reducer
const todos = (state = INIT.todos, action) => {
// ....
};
const filterStatus = (state = INIT.filterStatus, action) => {
// ...
};
const reducers = combineReducers({
todos,
filterStatus
});
// 这是我们平常使用时创建store
const store = createStore(reducers, state, applyMiddleware(thunkMiddleware));
为了下文好理解这个放一下 redux-thunk
的源码
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
可以看出 thunk
返回了一个接受 ({ dispatch, getState })
为参数的函数
下面我们来看一下 applyMiddleware
源码分析
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
// 每个 middleware 都以 middlewareAPI 作为参数进行注入,返回一个新的链。此时的返回值相当于调用 thunkMiddleware 返回的函数: (next) => (action) => {} ,接收一个next作为其参数
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 并将链代入进 compose 组成一个函数的调用链
// compose(...chain) 返回形如(...args) => f(g(h(...args))),f/g/h都是chain中的函数对象。
// 在目前只有 thunkMiddleware 作为 middlewares 参数的情况下,将返回 (next) => (action) => {}
// 之后以 store.dispatch 作为参数进行注入注意这里这里的store.dispatch是没有被修改的dispatch他被传给了next;
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
// 定义一个代码组合的方法
// 传入一些function作为参数,返回其链式调用的形态。例如,
// compose(f, g, h) 最终返回 (...args) => f(g(h(...args)))
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
} else {
const last = funcs[funcs.length - 1]
const rest = funcs.slice(0, -1)
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}
}
我第一眼看去一脸闷逼,咋这么复杂。但是静下心来看也就是一个三级柯里化的函数,我们从头来分析一下这个过程
// createStore.js
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
也就是说,会变成这样
applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)
-
applyMiddleware(thunkMiddleware)
applyMiddleware
接收 thunkMiddleware
作为参数,返回形如 (createStore) => (...args) => {}
的函数。
-
applyMiddleware(thunkMiddleware)(createStore)
以 createStore
作为参数,调用上一步返回的函数 (...args) => {}
-
applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)
以 (reducer, preloadedState)
为参数进行调用。 在这个函数内部, thunkMiddleware
被调用,其作用是监测 type
是 function
的 action
因此,如果dispatch的action返回的是一个function,则证明是中间件,则将(dispatch, getState)作为参数代入其中,进行action 内部下一步的操作。否则的话,认为只是一个普通的action,将通过next(也就是dispatch)进一步分发
也就是说, applyMiddleware(thunkMiddleware)
作为 enhance
,最终起了这样的作用:
对 dispatch
调用的 action
进行检查,如果 action
在第一次调用之后返回的是 function
,则将 (dispatch, getState)
作为参数注入到 action
返回的方法中,否则就正常对action进行分发,这样一来我们的中间件就完成喽~
因此,当action内部需要获取state,或者需要进行异步操作,在操作完成之后进行事件调用分发的话,我们就可以让action 返回一个以(dispatch, getState)为参数的function而不是通常的Object,enhance就会对其进行检测以便正确的处理
到此 redux
源码的主要部分讲完了,如有感兴趣的同学可以去看一下我没讲到的一些东西转送门 redux
;
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
复杂网络理论及其应用
汪小帆、李翔、陈关荣 / 清华大学出版社 / 2006 / 45.00元
国内首部复杂网络专著 【图书目录】 第1章 引论 1.1 引言 1.2 复杂网络研究简史 1.3 基本概念 1.4 本书内容简介 参考文献 第2章 网络拓扑基本模型及其性质 2.1 引言 2.2 规则网络 2.3 随机图 2.4 小世界网络模型 2.5 无标度网络模型 ......一起来看看 《复杂网络理论及其应用》 这本书的介绍吧!
正则表达式在线测试
正则表达式在线测试
RGB CMYK 转换工具
RGB CMYK 互转工具