内容简介:store有四个基础方法: dispatch、subscribe、getState、replaceReducer用于将多个reducer组合成一个reducer,接受一个对象,对象的每个属性即是单个reducer,各个reducer的key需要和传入该reducer的state参数同名。其实就是改变action发起的方式,之前是dispatch的方式,用bindActionCreators将actionCreator包装后,生成一个
-
createStore是一个函数,接收三个参数
recdcer,initState,enhancer
-
enhancer
是一个高阶函数,用于增强create出来的store,他的参数是createStore
,返回一个更强大的store生成函数。(功能类似于middleware)。 -
我们mobile仓库中的
storeCreator
其实就可以看成是一个enhancer,在createStore的时候将saga揉入了进去只不过不是作为createStore的第三个参数完成,而是使用middleware
完成。
function createStore(reducer, preloadedState, enhancer) { if (typeof enhancer !== 'undefined') { // createStore 作为enhancer的参数,返回一个被加强的createStore,然后再将reducer, preloadedState传进去生成store return enhancer(createStore)(reducer, preloadedState); } // ...... return { dispatch: dispatch, subscribe: subscribe, getState: getState, replaceReducer: replaceReducer }; } 复制代码
-
-
applyMiddleware 与 enhancer的关系。
- 首先他们两个的功能一样,都是为了增强store
- applyMiddleware的结果,其实就是一个enhancer
function applyMiddleware() { // 将传入的中间件放入middlewares for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) { middlewares[_key] = arguments[_key]; } // return了一个 enhancer函数,参数为createStore,内部对store进行了增强 return function (createStore) { return function () { // 将createStore的参数传入createStore,并生成store var store = createStore.apply(undefined, args); // 增强 dispatch var _dispatch = compose.apply(undefined, chain)(store.dispatch); // return 一个被增强了dispatch的store return _extends({}, store, { dispatch: _dispatch }); }; }; } 复制代码
store
store有四个基础方法: dispatch、subscribe、getState、replaceReducer
- store.dispatch (发起action)
function dispatch(action) { // 校验 action 格式是否合法 if (typeof action.type === 'undefined') { throw new Error('action 必须有type属性'); } // 不可以在 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; } // 把上次subscribe时得到的新的监听函数列表,赋值成为当前的监听函数列表 var listeners = currentListeners = nextListeners; // dispatch 的时候会依次执行 nextListeners 的监听函数 for (var i = 0; i < listeners.length; i++) { var listener = listeners[i]; listener(); } return action; } 复制代码
- store.subscribe (用于监听 store 的变化)
function subscribe(listener) { // 如果是在 dispatch时注册subscribe,抛出警告 if (isDispatching) { throw new Error('......'); } // 将监听函数放入一个队列 nextListeners.push(listener); // return 一个函数,用于注销监听事件 return function unsubscribe() { // 同样的,不能再 dispatch 时进行注销操作 if (isDispatching) { throw new Error('......'); } var index = nextListeners.indexOf(listener); nextListeners.splice(index, 1); }; } 复制代码
- store.getState (获取当前的state)
function getState() { if (isDispatching) { throw new Error('不允许在reducer执行中获取state'); } // retuen 上次 dispatch 时所更新的 currentState return currentState; } 复制代码
- store.replaceReducer (提换当前的reducer)
function replaceReducer(nextReducer) { // 检验新的 reducer 是否是一个函数 if (typeof nextReducer !== 'function') { throw new Error('Expected the nextReducer to be a function.'); } // 替换掉当前的 reducer currentReducer = nextReducer; // 发起一次新的 action, 这样可以使 sisteners 函数列表执行一遍,也可以更新一遍 currentState dispatch({ type: ActionTypes.REPLACE }); } 复制代码
combineReducers(组合reducer)
用于将多个reducer组合成一个reducer,接受一个对象,对象的每个属性即是单个reducer,各个reducer的key需要和传入该reducer的state参数同名。
function combineReducers(reducers) { // 所有传入 reducers 的 key var reducerKeys = Object.keys(reducers); var finalReducers = {}; // 遍历reducerKeys,将合法的 reducers 放入 finalReducers for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i]; if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key]; } } // 可用的 reducers的 key var finalReducerKeys = Object.keys(finalReducers); var unexpectedKeyCache = void 0; { unexpectedKeyCache = {}; } var shapeAssertionError = void 0; // 将每个 reducer 都执行一遍,检验返回的 state 是否有为undefined的情况 try { assertReducerShape(finalReducers); } catch (e) { shapeAssertionError = e; } // return 一个组合过的 reducer 函数,返回值为 state 是否有变化 return function combination() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var action = arguments[1]; // 如果有返回的state不合法的reducer,抛出错误 if (shapeAssertionError) { throw shapeAssertionError; } { // 校验 state 与 finalReducers 的合法性 var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache); if (warningMessage) { warning(warningMessage); } } var hasChanged = false; var nextState = {}; // 遍历所有可用的reducer,将reducer的key所对应的state,代入到reducer中调用 for (var _i = 0; _i < finalReducerKeys.length; _i++) { var _key = finalReducerKeys[_i]; var reducer = finalReducers[_key]; // reducer key 所对应的 state,这也是为什么 reducer 名字要与 state 名字相对应的原因 var previousStateForKey = state[_key]; // 调用 reducer var nextStateForKey = reducer(previousStateForKey, action); // reducer 返回了新的 state,调用store.getState时返回的就是他 nextState[_key] = nextStateForKey; // 新旧 state 是否有变化 ? hasChanged = hasChanged || nextStateForKey !== previousStateForKey; } return hasChanged ? nextState : state; }; } 复制代码
bindActionCreators
其实就是改变action发起的方式,之前是dispatch的方式,用bindActionCreators将actionCreator包装后,生成一个 key为actionType,value为接受 payload 的函数 的对象,发起action的时候直接调用这里面名为跟action的type同名的函数
- 它的核心其实就是将actionCreator传入然后返回一个可以发起dispatch的函数,函数中的dispatch接受一个已经生成的action,和在使用它的时候传入的playload
function bindActionCreator(actionCreator, dispatch) { return function () { return dispatch(actionCreator.apply(this, arguments)); }; } 复制代码
- 将多个 actionCreators 进行包装,最终返回一个被包装过的actionCreators
function bindActionCreators(actionCreators, dispatch) { // 如果传入一个函数,说明只有一个,actionCreator,返回一个可以进行 dispatch 的函数 if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch); } if ((typeof actionCreators === 'undefined' ? 'undefined' : _typeof(actionCreators)) !== 'object' || actionCreators === null) { throw new Error('校验actionCreators是否是对象'); } // 检索出 actionCreators 的 key var keys = Object.keys(actionCreators); var boundActionCreators = {}; // 循环将 actionCreators 中的项用 bindActionCreator 包装一遍,放入 boundActionCreators 对象中并return for (var i = 0; i < keys.length; i++) { var key = keys[i]; var actionCreator = actionCreators[key]; if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); } } return boundActionCreators; } 复制代码
compose
将多个函数组合成一个,从右往左依次执行
function compose() { // 获取传入参数的映射 for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) { funcs[_key] = arguments[_key]; } // 如果参数为0,return 一个 所传即所得的函数 if (funcs.length === 0) { return function (arg) { return arg; }; } // 如果只有一个,返回此参数 if (funcs.length === 1) { return funcs[0]; } // 使用 reduce 将所有传入的函数组合为一个函数,每一次执行reduce,a作为前一个函数都会被这个return的函数重新赋值 return funcs.reduce(function (a, b) { // 每次执行 reduce 都会返回这个函数,这个函数里返回的前一个函数接受下一个函数的返回值作为参数 return function () { return a(b.apply(undefined, arguments)); }; }); } 复制代码
applyMiddleware (增强dispatch)
其实applyMiddleware就是将传入的中间件进行组合,生成了一个接受 createStore为参数的函数(enhancer)。
// applyMiddleware将传入的中间件组合成一个enhancer // 然后再传入createStore改造成一个增强版的createStore // 最后传入reducer 和 initialState 生成 store。 const store = applyMiddleware(...middlewares)(createStore)(reducer, initialState); // 其实跟这样写没什么区别 const store = createStore(reducer, initialState, applyMiddleware(...middlewares)); 复制代码
- 代码分析
function applyMiddleware() { // 将传入的中间件组合成一个数组 for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) { middlewares[_key] = arguments[_key]; } // 返回一个接受 createStore 为参数的函数,也就是 enhancer return function (createStore) { // 其实这就是最终返回的被增强的 createStore return function () { for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } // 生成一个 store var store = createStore.apply(undefined, args); // 声明一个_dispatch,用于替换 store 的 dispatch var _dispatch = function dispatch() { throw new Error('不允许在构建中间件时进行调度'); }; // 返回一个middlewareAPI,下一步将会被带入中间件,使得每一个中间件中都会有 getState 与 dispatch (例如redux-thunk) // 这里面的 dispatch中,将会执行_dispatch(被增强的dispatch) var middlewareAPI = { getState: store.getState, dispatch: function dispatch() { return _dispatch.apply(undefined, arguments); } }; // 每一个中间件都执行一遍 middlewareAPI var chain = middlewares.map(function (middleware) { return middleware(middlewareAPI); }); // 将 chain 用compose进行组合,所以传入的中间件依赖必须是倒序的 // 并传入 store.dispatch,生成一个被增强的 dispatch _dispatch = compose.apply(undefined, chain)(store.dispatch); // 生成 store, 使用 _dispatch 替换 store 原始的 dispatch return _extends({}, store, { dispatch: _dispatch }); }; }; } 复制代码
结合中间件 redux-thunk
感受一下applyMiddleware
redux-thunk 可以使dispatch接受一个函数,以便于进行异步操作
import { createStore, applyMiddleware } from 'redux'; import reduxThunk from 'redux-thunk'; import reducer from './reducers'; const store = createStore( reducer, {}, applyMiddleware(reduxThunk), ); 复制代码
- 源码
function createThunkMiddleware(extraArgument) { // reuturn 一个接受dispatch, getState的函数, // 这个函数返回的函数又接受上一个中间件的返回值,也就是被上一个中间件包装过的dispatch // 如果接受的action是个函数,那么就将dispatch, getState传进去 return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); export default thunk; 复制代码
- 如果把thunk跟applyMiddleware组装起来,就是这样的
function applyMiddleware() { ... var middlewareAPI = { getState: store.getState, dispatch: function dispatch() { return _dispatch.apply(undefined, arguments); } }; var chain = middlewares.map(function () { // 这是middleware将middlewareAPI传进去后return的函数 return function(next) { return function(action) { if (typeof action === 'function') { return action(dispatch, getState); } return next(action); } } }); // 将store.dispatch,也就是next传进去 _dispatch = compose.apply(undefined, chain)(store.dispatch); } 复制代码
react-redux
用于绑定react 与 redux,其主要提供了两个功能
Provider
用于包装组件树,将store传入context中,使其子节点都可以拿到store,不需要一级一级的往下传。
class Provider extends Component { // 将 store 放入 context 中 getChildContext() { return { store: this.store} } constructor(props, context) { super(props, context) this.store = props.store; } render() { return Children.only(this.props.children) } } 复制代码
connect
connect 用于state与容器组件之间的绑定。
connect 接受三个参数 mapStateToProps, mapDispatchToProps, mergeProps
用于定义需要传入容器组件的state与dispatch,然后return一个接受容器组件的函数(高阶组件),这个高阶函数会对将组合好的props混入进容器组件。
var containerComponent = connect(mapStateToProps,mapDispatchToProps)(someComponent); ReactDOM.render( <Provider store={store}> <HashRouter> <div> <Route exact path="/" component={containerComponent} /> </div> </HashRouter> </Provider>, document.querySelector('.doc') ); 复制代码
-
connect 接收的参数
- mapStateToProps 返回需要传入容器组件的 state 的函数
const mapStateToProps = (state) => { return { stateName: state[stateName], }; } 复制代码
- mapDispatchToProps 返回需要传入容器组件dispatch的函数 or 对象(如果是对象的话传入的需要是个actionCreator,因为connect 内会用 bindActionCreators 将这个对象包装)
// mapDispatchToProps 是个函数 const mapDispatchToProps = (dispatch) => { return { dispatchName: (action) => { dispatch(action); }, }; } // or 当 mapDispatchToProps 是个对象时源码中的处理 export function whenMapDispatchToPropsIsObject(mapDispatchToProps) { return (mapDispatchToProps && typeof mapDispatchToProps === 'object') ? wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch)) : undefined } 复制代码
- mergeProps 规定容器组件props合并方式的函数
// 默认是将 mapStateToProps, mapDispatchToProps 与组件自身的props进行 merge const mergeProps = (stateProps, dispatchProps, ownProps) => { return { ...ownProps, ...stateProps, ...dispatchProps }; } 复制代码
-
connect 内的核心函数
-
finalPropsSelectorFactory(dispatch, {...options})
return 一个
pureFinalPropsSelector
函数,这个函数接受两个参数,(state, props)并返回一个 mergeProps, 他将会在高阶组件wrapWithConnect
中使用并传入store.getState()和props,并以此对比当前的 props 以决定在shouldComponentUpdate
时是否需要更新 -
connectAdvanced(finalPropsSelectorFactory)
return 一个接受 容器组件为参数的高阶组件(wrapWithConnect)。 wrapWithConnect需要的变量与属性,这也就是
connect
最终 return 的结果。 -
wrapWithConnect(WrappedComponent)
这个就是被
connectAdvanced
返回的高阶组件,其接受一个容器组件作为参数,在内部创建一个Connect
组件并在 render 的时候将整合好的 props 传入 容器组件。 -
hoistStatics(a, b) 将 b 的属性复制到 a
用于在包装组件的时候,将传入的容器组件内的属性都复制到 Connect 组件
function hoistNonReactStatics(targetComponent, sourceComponent, blacklist) { // 如果传入的 b 是字符串,直接return a if (typeof sourceComponent !== 'string') { // 层层递归,直到拿到 sourceComponent 的构造函数 var inheritedComponent = Object.getPrototypeOf(sourceComponent); if (inheritedComponent && inheritedComponent !== Object.getPrototypeOf(Object)) { hoistNonReactStatics(targetComponent, inheritedComponent, blacklist); } // b 的所有自有属性的 key ,包括 Symbols 属性 var keys = Object.getOwnPropertyNames(sourceComponent); keys = keys.concat(Object.getOwnPropertySymbols(sourceComponent)); // 过滤掉某些属性,并将 b 的属性复制给 a for (var i = 0; i < keys.length; ++i) { var key = keys[i]; if (!REACT_STATICS[key] && !KNOWN_STATICS[key] && (!blacklist || !blacklist[key])) { var descriptor = Object.getOwnPropertyDescriptor(sourceComponent, key); Object.defineProperty(targetComponent, key, descriptor); } } // return 一个被添加了 b 的属性的 a return targetComponent; } return targetComponent; } 复制代码
-
- connect 源码分析
function connect( mapStateToProps, mapDispatchToProps, mergeProps){ // 对传入的参数进行类型校验 与 封装 const initMapStateToProps = match(mapStateToProps, defaultMapStateToPropsFactories, 'mapStateToProps') const initMapDispatchToProps = match(mapDispatchToProps, defaultMapDispatchToPropsFactories, 'mapDispatchToProps') const initMergeProps = match(mergeProps, defaultMergePropsFactories, 'mergeProps') // return 一个接受 容器组件 为参数的高阶组件(wrapWithConnect) return connectAdvanced(finalPropsSelectorFactory) } // 接受的其实是 `finalPropsSelectorFactory` function connectAdvanced(selectorFactory) { const storeKey = 'store'; // 用于说明订阅对象 const subscriptionKey = storeKey + 'Subscription'; // 定义 contextTypes 与 childContextTypes 用于返回的高阶函数里的包装组件 Connect const contextTypes = { [storeKey]: storeShape, [subscriptionKey]: subscriptionShape, } const childContextTypes = { [subscriptionKey]: subscriptionShape, } // 返回一个高阶组件 return function wrapWithConnect(WrappedComponent) { // 这是一个接受真假 与 提示语 并抛出错误的方法,这里用来校验传入的是否是个函数 invariant(typeof WrappedComponent == 'function', `You must pass a component to the function`) // 将要传入 finalPropsSelectorFactory 的 option const selectorFactoryOptions = { getDisplayName: name => `ConnectAdvanced(${name})`, methodName: 'connectAdvanced', renderCountProp: undefined, shouldHandleStateChanges: true, storeKey: 'store', withRef: false, displayName: getDisplayName(WrappedComponent.name), wrappedComponentName: WrappedComponent.displayName || WrappedComponent.name, WrappedComponent } // 用于生成一个 selector,用于Connect组件内部的更新控制 function makeSelectorStateful(sourceSelector, store) { // wrap the selector in an object that tracks its results between runs. const selector = { // 比较 state 与 当前的selector的props,并更新selector // selector 有三个属性: // shouldComponentUpdate: 是否允许组件更新更新 // props: 将要更新的props // error: catch 中的错误 run: function runComponentSelector(props) { try { const nextProps = sourceSelector(store.getState(), props) if (nextProps !== selector.props || selector.error) { selector.shouldComponentUpdate = true selector.props = nextProps selector.error = null } } catch (error) { selector.shouldComponentUpdate = true selector.error = error } } } return selector } // 最终 return 的组件,用于包装传入的WrappedComponent class Connect extends Component { constructor(props, context) { super(props, context) this.store = props['store'] || context['store'] this.propsMode = Boolean(props['store']) // 校验是否传入了 store invariant(this.store, `Could not find store in either the context') this.initSelector() this.initSubscription() } componentDidMount() { // 会把 onStateChange 挂载到对store的订阅里 // 内部调用了 store.subscribe(this.onStateChange) this.subscription.trySubscribe() // 更新一遍 props this.selector.run(this.props) if (this.selector.shouldComponentUpdate) this.forceUpdate() } // 每次更新 props 都去对比一遍 props componentWillReceiveProps(nextProps) { this.selector.run(nextProps) } // 根据 selector 来进行组件更新的控制 shouldComponentUpdate() { return this.selector.shouldComponentUpdate } // 初始化 selector,用于组件props更新的控制 initSelector() { // 用于比较state与props的函数。并返回 merge 后的props const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions) this.selector = makeSelectorStateful(sourceSelector, this.store) this.selector.run(this.props) } // 初始化订阅模型: this.subscription initSubscription() { // 定义需要订阅的数据源,并将其传入 Subscription 生成一个 subscription 对象 const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey] this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this)) this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription) } // 数据的监听函数 onStateChange() { this.selector.run(this.props) } // 将 selector.props 传入到传入的组件 render() { const selector = this.selector selector.shouldComponentUpdate = false return createElement(WrappedComponent, selector.props) } } // 上面定义的 type 将作为 Connect 组件的属性 Connect.WrappedComponent = WrappedComponent Connect.displayName = displayName Connect.childContextTypes = childContextTypes Connect.contextTypes = contextTypes Connect.propTypes = contextTypes // 将传入的组件的属性复制进父组件 return hoistStatics(Connect, WrappedComponent) } } 复制代码
实用进阶
动态加载 reducer
场景:
- 随着项目的增大与业务逻辑越来越复杂,数据状态与业务组件(WrappedComponent)也会越来越多,在初始化 store 的时候将所有reducer注入的话会使得资源很大
- 在入口将所有 reducer 注入的话,由于业务比较多,所以不一定都能用到,造成资源与性能浪费
方案:
利用 redux.combineReducers
与 store.replaceReducer
组合与更新reducer
// 初始化 store 的时候,将 reducer 记录下来 // initReducer: 初始化时的 reducer 对象 var reducers = combineReducers(initReducer); const store = createStore( reducers, initState ); store.reducers = initReducer; // 加载子组件的时候,动态将新的 reducer 注入 function assignReducer(reducer) { // 合并新老 reducer const newReducer = Object.assign(store.reducers, reducer); // 经 combineReducers 组合后进行替换 store.replaceReducer(combineReducers(newReducer)); } 复制代码
时间旅行
场景:
- redux 是一个状态管理器,我们的对 action 以及 reducer 的操作,其实最终的目的就是为了更新状态,而时间旅行就是记录我们的状态更新的轨迹,并能回到某一个轨迹的节点。
- 比较容易理解的一个场景比如说翻页,记录每页的数据,页码变化的时候直接恢复数据不用再次请求接口(并不适合实际业务场景)
- 时间旅行更大的作用其实在于我们可以监控状态的变化,更方便的调试代码
方案:
利用 store.subscribe, 监听 dispatch 时记录下此时的 状态
const stateTimeline = [ initState ]; // 记录状态的时间线 let stateIndex = 0; // 当前所处状态的索引 // 当时间节点发生改变的时候,更替 state const reducer = (state, action) => { switch (action.type) { case 'CHANGE_STATE_INDEX': const currentState = action.playload.currentState; return currentState; default: return state; } }; const saveState = () => { // 将当前状态push进时间线 stateTimeline.push(store.getState); stateIndex++; }; // 注册监听事件 store.subscribe(saveState); // 获取某个时间节点的 state const getSomeNodeState = () => { return stateTimeline[stateIndex]; }; // 时间线控制器 const timeNodeChangeHandle = (someIndex) => { stateIndex = someIndex; store.dispatch({ type: 'CHANGE_STATE_INDEX', playload: { currentState: getSomeNodeState(); } }); }; 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- ReactNative源码解析-初识源码
- Spring源码系列:BeanDefinition源码解析
- Spring源码分析:AOP源码解析(下篇)
- Spring源码分析:AOP源码解析(上篇)
- 注册中心 Eureka 源码解析 —— EndPoint 与 解析器
- 新一代Json解析库Moshi源码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
码出高效:Java开发手册
杨冠宝、高海慧 / 电子工业出版社 / 2018-10 / 99.00元
《码出高效:Java 开发手册》源于影响了全球250万名开发工程师的《阿里巴巴Java开发手册》,作者静心沉淀,对Java规约的来龙去脉进行了全面而彻底的内容梳理。《码出高效:Java 开发手册》以实战为中心,以新颖的角度全面阐述面向对象理论,逐步深入地探索怎样成为一位优秀开发工程师。比如:如何驾轻就熟地使用各类集合框架;如何得心应手地处理高并发多线程问题;如何顺其自然地写出可读性强、可维护性好的......一起来看看 《码出高效:Java开发手册》 这本书的介绍吧!