浅析redux

一 redux 思想

  首先,每一个webApp有且只有一个state tree,为方便管理和跟踪state的变化,也为了减少混乱,redux只允许通过发送(dispatch)action的方式来改变state。旧state在action的作用下产生新state,这个过程叫做reduce,具体逻辑由用户自定义,称为reducer函数。

  另外,redux允许在dispatch action与action到达reducer之间拦截action,做更多的动作,即中间件(middleware),这就像插件机制一样,允许多样的扩展,其中一类重要中间件的功能就是处理异步(与服务端通信)。

二、redux使用

  redux用起来是这样的:用户先写好处理state各个子部分的多个reducers,通过redux提供的combineReducers函数合并成一个rootReducer;然后选好需要的中间件;reducer和中间件作为参数,通过createStore函数创建Store对象。Store是redux运用起来的核心对象,通过store可以获取state--store.getState(), dispatch action以及订阅state变化store.subscribe()。

1 const rootReducer = combineReducers(reducers);
2 const store = createStore(rootReducer, applyMiddleware(...middlewares))
3 
4 const state0 = store.getState();
5 
6 const action0 = actionCreator('do something');
7 store.dispatch(action0);
8 
9 const unSubscribeHandler = store.subscribe(callback);

三、redux代码解析

  参考文章(2)以及源代码(3),实现redux核心逻辑的代码如下(自己掰的):

  1 const createStore = (reducer, enhancer) => {
  2     if (typeof enhancer === 'function') {
  3         return enhancer(createStore)(reducer);
  4     }
  5 
  6     let currentState = undefined;
  7     let currentReducer = reducer;
  8     let subscribers = [];
  9 
 10     function dispatch(action) {
 11         if (typeof currentReducer === 'function') {
 12             currentState = currentReducer(currentState, action);
 13             subscribers.forEach(sfn => sfn());
 14         }
 15         return action;
 16     }
 17 
 18     function getState() {
 19         return currentState;
 20     }
 21 
 22     function unSubscribe(fn) {
 23         subscribers = subscribers.filter(sfn => sfn === fn);
 24     }
 25 
 26     function subscribe(fn) {
 27         if (typeof fn !== 'function') {
 28             throw new Error('subscriber must be a function!');
 29         }
 30         const oldFn = subscribers.find(fnx => fnx === fn);
 31         if (!oldFn) {
 32             subscribers.push(fn);
 33         }
 34         return () => unSubscribe(fn);
 35     }
 36 
 37     dispatch({ type: 'init' });
 38     return { dispatch, getState, subscribe };
 39 };
 40 
 41 // combine multiple reducers into one.
 42 //  const rootReducer = combineReducers(reducerX, reducerY, reducerZ);
 43 //  const store = createStore(rootReducer);
 44 const combineReducers = reducers => (state = {}, action) => {
 45     const currentState = state;
 46     reducers.forEach((reducer) => {
 47         const partialStateName = reducer.name;
 48         currentState[partialStateName] = reducer(currentState[partialStateName], action);
 49     });
 50     return currentState;
 51 };
 52 
 53 // const actionA = ActionCreators.doA('xxx');
 54 // dispatch(actionA);
 55 // const actionB = ActionCreators.doB('yyy');
 56 // dispatch(actionB);
 57 // -->
 58 // const Action = bindActionCreators(ActionCreators, dispatch);
 59 // Action.doA('xxx');
 60 // Action.doB('yyy');
 61 const bindActionCreators = (actions, dispatch) => {
 62     const newActions = {};
 63     for (const key of Object.getOwnPropertyNames(actions)) {
 64         newActions[key] = args => dispatch(actions[key](args));
 65     }
 66     return newActions;
 67 };
 68 
 69 // funcs = [fa, fb, fc]
 70 // compose(...funcs)(...args) <=> fa(fb(fc(...args)))
 71 const compose = (...funcs) => {
 72     return funcs.reduce((a, b) => (...args) => a(b(...args)));
 73 };
 74 
 75 // 返回一个enhancer: enhancer(createStore)(reducer)
 76 const applyMiddleware = (...middlewares) => {
 77     return createStore => reducer => {
 78         const store = createStore(reducer);
 79         let dispatch = store.dispatch;
 80         //包装dispatch
 81         const middlewareAPI = {
 82             getState: store.getState,
 83             dispatch: action => dispatch(action)
 84         };
 85         const chain = middlewares.map(middleware => middleware(middlewareAPI));
 86         const enhancedDispatch = compose(...chain)(dispatch);
 87         return { ...store, dispatch: enhancedDispatch };
 88     };
 89 };
 90 
 91 const logger = ({ getState, dispatch }) => next => action => {
 92     console.log('logger: before action dispatch: ', getState());
 93     console.log('logger: action: ', action);
 94     const result = next(action);
 95     console.log('logger: after action dispatch: ', getState());
 96     return result;
 97 };
 98 
 99 const logger1 = ({ getState, dispatch }) => next => action => {
100     console.log('logger1: before action dispatch: ', getState());
101     console.log('logger1: action: ', action);
102     const result = next(action);
103     console.log('logger1: after action dispatch: ', getState());
104     return result;
105 };

   主要实现这几个函数:

  createStore(reducer, enhancer);  以及store.dispatch()  store.getState()

  combineReducers(reducers);

  applyMiddleware(...middlewares); 以及 compose(...funcs);

  (1) createStore(reducer, enhancer)

  关注enhancer这个参数,enhancer是中间件经过applyMiddleware之后的函数,其实是参数版的装饰器。

enhancer作用在createStore函数上,就是在原来的创建store之外还做了些事情,具体就是改造了store的dispatch函数,加入了中间件。

参考以下代码:比较装饰器decorator直接作为装饰器使用以及作为参数的装饰器。

 1 function funcA(x, enhancer) {
 2     if (typeof enhancer === 'function') {
 3         return enhancer(funcA)(x);
 4     }
 5 
 6     console.log('in funcA');
 7     return 2 * x;
 8 }
 9 
10 const decorator = (fn) => (x) => {
11     console.log('before');
12     let result = fn(x);
13     console.log('after');
14     return result * 10;
15 };
16 
17 
18 // commonly used decorator
19 console.log(decorator(funcA)(5));
20 console.log('===========');
21 // decorator as argument
22 console.log(funcA(5, decorator));

  (2)compose(...funcs)

  这个函数的作用是把多个函数(funcs)连成一个函数,其效果等同于逆序地逐个把函数作用于参数上:

    compose(fa, fb, fc)(x)   等价于    fa(fb(fc(x)))

这里的技巧是使用reduce函数:通常的reduce是数据与数据之间reduce,这里用在了函数与函数之间,

注意reduce里面的函数的返回值也是函数,体味一下。

const compose = (...funcs) => {
    return funcs.reduce((a, b) => (...args) => a(b(...args)));
};

   (3)applyMiddleware(...middlewares)

  applyMiddleware(...middlewares)的结果是产生createStore函数的装饰器enhancer。

具体的实现是先创建原生的store,然后增强dispatch函数。

 1 const applyMiddleware = (...middlewares) => {
 2     return createStore => reducer => {
 3         const store = createStore(reducer);
 4         let dispatch = store.dispatch;
 5         //包装dispatch
 6         const middlewareAPI = {
 7             getState: store.getState,
 8             dispatch: action => dispatch(action)
 9         };
10         const chain = middlewares.map(middleware => middleware(middlewareAPI));
11         const enhancedDispatch = compose(...chain)(dispatch);
12         return { ...store, dispatch: enhancedDispatch };
13     };
14 };

中间件的实际形式是生成器(creator),它接收一个包含dispatch和getState方法的对象,

返回next函数的装饰器(decorator):

  someMiddleware = ({ getState, dispatch }) => next => action => {};

这里面的next,就是下一个next函数的装饰器,前一层装饰后一层,层层装饰直到最后一个next,就是原生的dispatch函数本身了。具体例子参考代码中的logger中间件。

  上面代码中chain中的元素就都是装饰器了,然后经过compose,再作用到dispatch上就产生了增强版的dispatch,

用这个enhancedDispatch替换原生store中的dispatch就大功告成了

   尾声

  为了验证上述逻辑理解的是否正确,加几个middleware玩玩。

const interceptor = ({ getState, dispatch }) => next => action => {
    if (action.type === 'DECREMENT') {
        console.log(`action: ${action.type} intercepted!`);
        return null;
    } else {
        return next(action);
    }
};

const actionModifier = ({ getState, dispatch }) => next => action => {
    if (action.type === 'DECREMENT') {
        console.log(`action: ${action.type} got and modified to type = INCREMENT!`);
        action.type = 'INCREMENT';
    }
    return next(action);
};

const useNativeDispatch = ({ getState, dispatch }) => next => action => {
    if (action.type === 'DECREMENT') {
        console.log('shortcut to native dispatch!');
        return dispatch(action);
    } else {
        return next(action);
    }
};

  interceptor 会拦截特定类型的action,然后不再向后传播,结果就是之前的中间件还能执行,后面的中间件就不执行了;

  actionModifier 会修改特定类型action的数据,再向后传播,所以前后中间件会看到不同的action;

  useNativeDispatch 在遇到特定类型的action时,会跳过后面所有中间件,直接调用原生dispatch。

完毕!

参考文章:

(1)redux官方basic&advanced tutorial

(2)一起学习造轮子(二):从零开始写一个Redux

(3)redux github

原文地址:https://www.cnblogs.com/tlz888/p/10584473.html