redux源码解读--createStore源码解析
createStore源码解析
先看一下这个模块的基本结构:
依赖
对外输出
源码注释 // 判断是不是纯粹对象的模块({}) import isPlainObject from 'lodash/isPlainObject' // 引入observable支持 import $$observable from 'symbol-observable' export const ActionTypes = { INIT: '@@redux/INIT' } 上面这个是 import {createStore,combineReducers,applyMiddleware} from '../src' import logger from 'redux-logger' const actionTypes = '@@redux/INIT' const reducers = (state = {},action) => { switch(action.type) { case actionTypes: console.log('hello @@redux/INIT') return { 'type': actionTypes } default: return state } } const store = createStore(reducers,applyMiddleware(logger)) console.log('*************************************') console.log(store.getState()) //会渲染为 {'type': '@@redux/INIT'} console.log('*************************************') 下面就是 export default function createStore(reducer,preloadedState,enhancer){ //...初始条件的判断和设定 function getState() { // getState方法的实现 } function subscribe() { // subscribe方法的实现 } function dispatch() { // dispatch方法的实现 } function replaceReducer() { // replaceReducer方法的实现 } function observable() { // observable方法的实现 } // store被创建后,自动分发一个'INIT' action。渲染出初始化的state树。 dispatch({ type: ActionTypes.INIT }) } 下面就来一点点分析每一行代码到底做什么: if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined } 在调用
在平常的使用中,我们一般会省略第二个参数。比如,当我们需要使用 const store = createStore(reducer,applyMiddleware(...)) 这个时候就会执行上面源码中的代码,使函数调用满足最基本的形式调用。也就是函数在传递两个或者三个参数的情况下,其内部处理逻辑都是一样的。 // 如果我们指定了reducer增强器enhancer if (typeof enhancer !== 'undefined') { // enhancer必须是一个函数 if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } // 这个函数接收createStore作为参数,并且返回一个函数,这个函数接收的参数是reducer,preloadedState // 直接返回经过enhancer包装的对象 return enhancer(createStore)(reducer,preloadedState) } 想更好的理解这段代码,可以参考applyMiddleware内部的实现。 // 要求传递给createStore的第一个参数必须是一个函数 if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') } // 保存初始的reducer let currentReducer = reducer // 保存初始的state let currentState = preloadedState // 保存所有的事件监听器 let currentListeners = [] // 获取当前监听器的一个副本(相同的引用) let nextListeners = currentListeners // 是否正在派发action let isDispatching = false function ensureCanMutateNextListeners() { // 如果nextListeners和currentListeners具有相同的引用,则获取一份当前事件监听器集合的一个副本保存到nextListeners中 if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } 上面就是 // 直接返回当前store的state function getState() { return currentState } function subscribe(listener) { // 事件监听器必须是函数,否则会抛出异常 if (typeof listener !== 'function') { throw new Error('Expected listener to be a function.') } // 这个事件监听器是否已经被取消的标志(个人感觉:这个初始值应该被设置为false,语意化更好一些。) let isSubscribed = true // 调用这个函数的结果就是生成一份当前事件监听器的一个副本保存到nextListeners中 ensureCanMutateNextListeners() // 将新的事件监听器添加到nextListeners中 nextListeners.push(listener) // 返回一个取消监听的函数 return function unsubscribe() { // 如果这个监听器已经被取消了,则直接return if (!isSubscribed) { return } // 将监听器是否取消的标志设置为false isSubscribed = false // 再次生成一份事件监听器集合的副本 ensureCanMutateNextListeners() // 获取到需要取消的事件监听器的索引 const index = nextListeners.indexOf(listener) // 从事件监听器集合中删除这个事件监听器 nextListeners.splice(index,1) } } 从 function dispatch(action) { // dispatch的参数就是我们需要派发的action,一定要保证这个action是一个纯粹的对象 // 如果不是一个纯粹的对象,则会抛出异常。 if (!isPlainObject(action)) { // 这个方法有坑,在低版本的IE浏览器中性能很差,最后我们会研究这个方法的源码。 throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } // 所派发的action必须有一个type属性(我们可以将这个属性认为就是action的身份证,这样redux才知道你派发的是哪个action,你需要做什么,该怎么为你做) // 如果没有这个属性则会抛出异常 if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } // 如果redux正在派发action,则抛出异常?什么时候会出现这种情况??? if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true // 派发action // 实质就是将当前的state和你需要派发的action传递给reducer函数病返回一个新的state currentState = currentReducer(currentState,action) } finally { isDispatching = false } // 这一块也是一个十分关键的地方,哈哈哈哈哈,又多了一份事件监听器的列表,简单的说一下这三份列表的作用 // nextListeners: 保存这次dispatch后,需要触发的所有事件监听器的列表 // currentListeners: 保存一份nextListeners列表的副本 // listeners: 需要执行的列表 const listeners = currentListeners = nextListeners for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] // 调用所有的事件监听器 listener() } // dispatch的返回值也是十分重要的,如果没有这个返回值,就不可能引入强大的中间件机制。 return action } 到这里,我们就可以回答这个问题了:为什么要维护两份事件监听器列表(nextListeners,currentListeners)? 首先,我们必须要知道的事情就是:我们的监听器在什么时候会执行?在我们的调用dispatch派发action之后。ok,看下面的这个图:
这个图表示,当
所以,在这种情况下。如果我在某个事件监听器函数中调用了取消了某个监听器,那么在这次dispatch后,被取消的这个事件监听器就不会被执行了(?????是吗????)。 import {createStore,action) => { switch(action.type) { case actionTypes: return { 'type': actionTypes } default: return state } } const store = createStore(reducers,applyMiddleware(logger)) const listener1 = store.subscribe(() => { console.log('listener1') }) const listener2 = store.subscribe(() => { // 取消listener3 listener3() console.log('listener2') }) const listener3 = store.subscribe(() => { console.log('listener3') }) store.dispatch({type: actionTypes}) 结果是: listener1 listener2 listener3 结果,就是:即使你在某个事件监听器中,取消了其它的事件监听器,那么被取消的这个事件监听器,在这次dispatch后仍然会执行。也就是说。redux会保证在某个dispatch后,会保证在这个dispatch之前的所有事件监听器全部执行。 这是个bug还是个feature。无从而知,但是从redux源码中,可以知道,这是一个bug。所以,redux作者就利用上面的方法很巧妙的避免了这种情况。其实实现的方法很简单:切断nextListeners和currentListener,listeners相同的引用关系。
下面接着扯: // 提换reducer的方法。(动态加载reducers的时候才用) function replaceReducer(nextReducer) { if (typeof nextReducer !== 'function') { throw new Error('Expected the nextReducer to be a function.') } currentReducer = nextReducer // 替换结束后,重新初始化 dispatch({ type: ActionTypes.INIT }) } // 触发预设action,主要就是为了生成初始的state tree的结构 dispatch({ type: ActionTypes.INIT }) // 这就很熟悉了吧 return { dispatch,subscribe,getState,replaceReducer,// 尼玛 忽略这个 [$$observable]: observable } 这就是对 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |