对于 Redux 结构与代码实现的剖析,以及项目中的高级用法,不进行对于API的介绍react
recdcer,initState,enhancer
enhancer
是一个高阶函数,用于加强create出来的store,他的参数是createStore
,返回一个更强大的store生成函数。(功能相似于middleware)。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
};
}
复制代码
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有四个基础方法: dispatch、subscribe、getState、replaceReducerredux
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;
}
复制代码
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);
};
}
复制代码
function getState() {
if (isDispatching) {
throw new Error('不容许在reducer执行中获取state');
}
// retuen 上次 dispatch 时所更新的 currentState
return currentState;
}
复制代码
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 });
}
复制代码
用于将多个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;
};
}
复制代码
其实就是改变action发起的方式,以前是dispatch的方式,用bindActionCreators将actionCreator包装后,生成一个key为actionType,value为接受 payload 的函数的对象,发起action的时候直接调用这里面名为跟action的type同名的函数app
function bindActionCreator(actionCreator, dispatch) {
return function () {
return dispatch(actionCreator.apply(this, arguments));
};
}
复制代码
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;
}
复制代码
将多个函数组合成一个,从右往左依次执行异步
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就是将传入的中间件进行组合,生成了一个接受 createStore为参数的函数(enhancer)。ide
// 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
感觉一下applyMiddlewareredux-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;
复制代码
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,其主要提供了两个功能源码分析
用于包装组件树,将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 用于state与容器组件之间的绑定。
connect 接受三个参数mapStateToProps, mapDispatchToProps, mergeProps
用于定义须要传入容器组件的state与dispatch,而后return一个接受容器组件的函数(高阶组件),这个高阶函数会对将组合好的props混入进容器组件。ui
var containerComponent = connect(mapStateToProps,mapDispatchToProps)(someComponent);
ReactDOM.render(
<Provider store={store}> <HashRouter> <div> <Route exact path="/" component={containerComponent} /> </div> </HashRouter> </Provider>, document.querySelector('.doc') ); 复制代码
const mapStateToProps = (state) => {
return {
stateName: state[stateName],
};
}
复制代码
// 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
}
复制代码
// 默认是将 mapStateToProps, mapDispatchToProps 与组件自身的props进行 merge
const mergeProps = (stateProps, dispatchProps, ownProps) => {
return { ...ownProps, ...stateProps, ...dispatchProps };
}
复制代码
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;
}
复制代码
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) } } 复制代码
场景:
方案:
利用
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));
}
复制代码
场景:
方案:
利用 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();
}
});
};
复制代码