阅读redux源码
redux源码解析什么是reduxRedux 是 JavaScript 状态容器,提供可预测化的状态管理。 为什么需要使用redux提供了和双向绑定思想不同的单向数据流,应用状态可以预测,可以回溯,易于调试。使用redux之初的人可能会很不适应,改变一个状态,至少写三个方法,从这点上不如写其他框架代码易于理解,但是自从配合使用redux-logger一类的logger插件,就感觉到了redux的优势。状态改变很清晰,很容易了解发生了什么。 源码解析注意: 如果没有使用过redux,建议先去看看redux文档 api方法export { createStore,combineReducers,bindActionCreators,applyMiddleware,compose } 可以看到我们在react代码中使用到的api,一般主动调用的就是 combineReducers打开combineReducers.js,先看export的方法,也就是combineReducers方法 var reducerKeys = Object.keys(reducers) var finalReducers = {} for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i] if (process.env.NODE_ENV !== 'production') { if (typeof reducers[key] === 'undefined') { warning(`No reducer provided for key "${key}"`) } } if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } 首先看到这个函数接收的是一个对象,而这个这个对象的内部数据值必须是一个函数,不然会警告。循环了一遍这个对象,得到一个新值,对象值全部是函数的一个新reducers var finalReducerKeys = Object.keys(finalReducers) if (process.env.NODE_ENV !== 'production') { var unexpectedKeyCache = {} } var sanityError try { assertReducerSanity(finalReducers) } catch (e) { sanityError = e } 这里好像还在判断这个最后reducers的合法性,那这里是在判断什么呢?我们来看看 function assertReducerSanity(reducers) { Object.keys(reducers).forEach(key => { var reducer = reducers[key] var initialState = reducer(undefined,{ type: ActionTypes.INIT }) if (typeof initialState === 'undefined') { throw new Error( `Reducer "${key}" returned undefined during initialization. ` + `If the state passed to the reducer is undefined,you must ` + `explicitly return the initial state. The initial state may ` + `not be undefined.` ) } var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.') if (typeof reducer(undefined,{ type }) === 'undefined') { throw new Error( `Reducer "${key}" returned undefined when probed with a random type. ` + `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` + `namespace. They are considered private. Instead,you must return the ` + `current state for any unknown actions,unless it is undefined,` + `in which case you must return the initial state,regardless of the ` + `action type. The initial state may not be undefined.` ) } }) } 这块其实就两个判断,reducer被执行了两次,一个是判断没有初始化state的,reducer的返回值,一个判断action没有type的时候的返回值。一个没有返回值都会有警告,所以我们写reducer的时候都会指定一个默认返回值。 reducer会被执行多次,这也是我们为什么要保证reducer的纯粹性,不能做任何其他的操作的原因 继续往下看 combineReducers 可以看到返回了一个函数 那我们看 这块想明白还是有点复杂,所有的reducer都是一个相同的函数combination,接收state参数,内部执行同样是combination,直到没有combineReducers为止,才开始执行我们自己写的reducer函数,得到的值使用combineReducers参数的对象的key作为state的key,我们自己写的reducers执行结果得到的值作为state的value。最终得到的就是一个巨大的Object,这就是我们的store中的state。 createStore一般这个方法我们可以直接从demo中复制过来,不需要太过了解,但是既然要深入了解redux,必然要掌握这个方法 跟之前一样,先找到 export default function createStore(reducer,preloadedState,enhancer) { 第一个reducer: 上文讲到的combineReducer返回的reducer函数 第二个preloadedState:redux初始化state,可以不传 第三个enhancer:中间件 if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } return enhancer(createStore)(reducer,preloadedState) } if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') } 可以看到第一个判断的意思是当没有第二个参数是函数的时候,默认第二个参数就是中间件,并且默认state置为undefined 第二个判断的意思是当有中间件参数,但是中间参数类型不是function的时候,抛出一个非法错误,如果是函数,先执行中间件,退出。后续在讲中间件是怎么执行的 第三个判断reducer是否是函数,否则抛出错误退出 var currentReducer = reducer // 当前reducer var currentState = preloadedState // 当前state var currentListeners = [] // 当前监听器 var nextListeners = currentListeners // 下一个监听器 var isDispatching = false // 重复dispatch的状态标记 再看看createStore的返回值 return { dispatch,subscribe,getState,replaceReducer,[$$observable]: observable } 这不是store的方法嘛,挨个看看 function getState() { return currentState } 这个没什么好说的。 function subscribe(listener) { if (typeof listener !== 'function') { throw new Error('Expected listener to be a function.') } var isSubscribed = true ensureCanMutateNextListeners() nextListeners.push(listener) return function unsubscribe() { if (!isSubscribed) { return } isSubscribed = false ensureCanMutateNextListeners() var index = nextListeners.indexOf(listener) nextListeners.splice(index,1) } } 发布订阅模式,熟悉事件系统的应该比较明白,注册一个方法而已,结果返回一个取消监听方法 function dispatch(action) { if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true currentState = currentReducer(currentState,action) } finally { isDispatching = false } var listeners = currentListeners = nextListeners for (var i = 0; i < listeners.length; i++) { listeners[i]() } return action } 老几样啊,先做一些判断,我们写代码的时候好像没这么严谨哈。执行reducer,触发所有listeners。这个比较简单。 这样子,看起来createStore没什么复杂的,复杂的在哪呢?我们掠过的中间件退出的环节。所以来烧脑吧,看看中间件 想想我们创建store的时候是怎么操作的 const finalCreateStore = compose( applyMiddleware(thunk,logger) )(createStore) const store = finalCreateStore(rootReducer,initialState) 这种堆在一起的代码不是太好看,分开,分开 const middlewares = applyMiddleware(thunk,logger) const composeResult = compose(middlewares) const finalCreateStore = composeResult(createStore) const store = finalCreateStore(rootReducer,initialState) 这就条理清晰多了,看代码一定要看懂流程,按照顺序看,不然一头雾水,先看第一步 export default function applyMiddleware(...middlewares) { return (createStore) => (reducer,enhancer) => { var store = createStore(reducer,enhancer) var dispatch = store.dispatch var chain = [] var middlewareAPI = { getState: store.getState,dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store,dispatch } } } 可以看到这个方法返回一个函数,既然这个函数没有被执行到,我们就先不看,现在我们得到了一个 接着看 export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } const last = funcs[funcs.length - 1] const rest = funcs.slice(0,-1) return (...args) => rest.reduceRight((composed,f) => f(composed),last(...args)) } 代码更少,可是redux精髓全在这了。 compose 执行接收参数,如果参数个数是1,直接执行,上文的 显然 composeResult 接收到 createStore之后返回一个函数: finalCreateStore,从代码中可以看出也是可以接收中间件方法的,不过应该不会有人再在这里重复添加中间件了。 进入到
再次进入到 const last = funcs[funcs.length - 1] const rest = funcs.slice(0,last(...args)) compose 同样只是返回了一个函数。这个函数接收的参数在 这里巧妙的利用了js Array的reduce方法,reduce方法的原理就是回调函数的返回值作为后一个回调函数的第一个参数,第一个回调函数的第一个参数的值是 reduce方法的第二个参数值。 args就是dispatch方法,这里看的出中间件函数还得返回函数,这个函数得接收类似dispatch方法的函数 看看redux-chunk这个中间件的实现吧 function createThunkMiddleware(extraArgument) { return ({ dispatch,getState }) => next => action => { if (typeof action === 'function') { return action(dispatch,extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk; 看到 next 方法,使用过express的同学应该会很熟悉,这个next和express的next很像,原理也类似。 每个中间件的最后一层函数都是一个next,才可以在reduce里面作为参数传递,才可以实现中间件的传递 这也是redux名称的由来。 redux代码短小精悍,设计精巧,真好。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- c – 生成(不是这样)具有特定字符串出现的随机字符串
- iphone – 如何更改启动的默认xcode版本?
- 在c#中对List进行排序,忽略该符号
- iphone – 架构armv7的未定义符号:“_SCNetworkReachabili
- 设置、读取联络人头像 ---- save UIImage to ABAddressBook
- nand flash读取函数的理解
- norflash启动和nandflash启动
- c# – 任务取消异常(ThrowForNonSuccess)
- ruby-on-rails – Rails – 从对象散列中创建一个select标签
- Cocos2dx C++与Lua合并开发的VS配置环境搭建