• 浅析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

  • 相关阅读:
    在markdown中使用html
    乘车路线
    渔民的烦恼
    GEDIT外部工具
    模板匹配,以图找图(九)
    SpringBoot起飞系列-国际化(六)
    [Lyndon分解] HDU 6761 Minimum Index
    [数论]HDU 6750 Function 百度之星2020初赛第一场H题
    【雅思】【口语】描述一个可笑的场合
    面试回答数据库优化问题-数据库优化思路八点
  • 原文地址:https://www.cnblogs.com/tlz888/p/10584473.html
Copyright © 2020-2023  润新知