zoukankan      html  css  js  c++  java
  • redux源码阅读之compose,applyMiddleware

    我的观点是,看别人的源码,不追求一定要能原样造轮子,单纯就是学习知识,对于程序员的提高就足够了。在阅读redux的compose源码之前,我们先学一些前置的知识。

    redux源码阅读之compose,applyMiddleware

    看别人的源码就是学习知识。我们先学一些东西。

    rest参数

    形式为...变量名,用于获取函数的多余参数 ,该变量将多余的参数放入数组中, 只能是参数的最后一个。

    function rest(...args){
      console.log(args);
    };
    rest('arg1','arg2')
    

    那么args变量就是一个数组['arg1','arg2']。

    扩展运算符

    扩展运算符也是三个点。好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列

    arr.reduce(callback[initialValue])

    个人感觉reduce用法非常魔幻,要搞明白它之后,才能明白compose。
    MDN的reduce文档:
    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

    使用

    const array1 = [1, 2, 3, 4];
    const reducer = (accumulator, currentValue) => accumulator + currentValue;
    
    // 1 + 2 + 3 + 4
    console.log(array1.reduce(reducer));
    // 10
    
    // 5 + 1 + 2 + 3 + 4
    console.log(array1.reduce(reducer, 5));
    // 15
    
    array1.reduce((accumulator, currentValue) => accumulator + currentValue,5)
    

    根据MDN的定义,
    数组的reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。

    arr.reduce(callback[initialValue])

    实际就是arr.reduce(callback, initialValue);

    callback 函数接收4个参数:

    Accumulator (acc) (累加器)
    Current Value (cur) (当前值)
    Current Index (idx) (当前索引)
    Source Array (src) (源数组)
    

    initialValue 可选

    作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 不传这个初始值initialVale,且在空数组上调用 reduce 将报 Uncaught TypeError: Reduce of empty array with no initial value的错误。

    pip

    研究compose之前,我们再来看MDN上reduce的应用,一个功能型函数管道。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#功能型函数管道

    类似的我们来写一个。

    const pipe = (...fns) => 
      (args) => fns.reduce(
        (args, fn) => fn(args)
      ,args)
    

    吐槽一下,就是这种es6写法,完全不写return,很多es5过来的人不习惯,所以很多源码很难看清楚。我们改写一下,传入参数字符串‘test’,然后执行这个函数。就能明白pip函数的作用就是顺序执行了addFont,toUpper。

    args就是'test',...fns就是rest参数,pip函数里的fns参数就是数组[addfont,toUpper]。

    
    const pipe = (...fns) => {
      return (args) => {
        return fns.reduce((args, fn) => {
          return fn(args)
        }, args)
      }
    }
    
    const toUpper = (value) => {
      return value.toUpperCase();
    }
    
    const addFont = (value) => {
      return 'hello plus!' + value;
    }
    
    console.log(pipe(addFont,toUpper)('test'));
    // HELLO PLUS!TEST
    
    

    pipe(addFont,toUpper)之后,返回一个函数.
    pipe(addFont,toUpper)('test')形如

    (args) => {
        return fns.reduce((args, fn) => {
          return fn(args)
        }, args)
      }('test');
    

    再执行就是

    fns.reduce(('test',fn)=>{
        return fn('test');
    },'test')
    

    这样子看代码,这里的reduce应该似乎比较好懂的。

    compose

    compose源码

    /**
     * Composes single-argument functions from right to left. The rightmost
     * function can take multiple arguments as it provides the signature for
     * the resulting composite function.
     *
     * @param {...Function} funcs The functions to compose.
     * @returns {Function} A function obtained by composing the argument functions
     * from right to left. For example, compose(f, g, h) is identical to doing
     * (...args) => f(g(h(...args))).
     */
    
    export default function compose(...funcs) {
      if (funcs.length === 0) {
        return arg => arg
      }
    
      if (funcs.length === 1) {
        return funcs[0]
      }
    
      return funcs.reduce((a, b) => (...args) => a(b(...args)))
    }
    

    跟pip函数对比

    console.log(pipe(addFont,toUpper)('test'));
    // HELLO PLUS!TEST
    
    console.log(compose(addFont,toUpper)('test'));
    // hello plus!TEST
    

    compose正好相反,后添加的方法先执行。然后写的也很玄学让人费解。让我们改成pip函数一样,也是同样的效果,感觉这样子就跟上面的pip一样好理解一些了呢。

    export default function compose(...funcs) {
      if (funcs.length === 0) {
        return arg => arg
      }
    
      if (funcs.length === 1) {
        return funcs[0]
      }
    
      return (...funcs) => (...args) => funcs.reduce((a, b)  => a(b(...args)))
    }
    console.log(compose(addFont,toUpper)('test'));
    // hello plus!TEST
    

    applyMiddleware

    我们再来看redux引入中间件的用法,

    const store = createStore(
      reducer,
      applyMiddleware(...middleware)
    );
    

    以及applyMiddleware方法源码

    export default function applyMiddleware(...middlewares) {
      return createStore => (...args) => {
        const store = createStore(...args)
        let dispatch = () => {
          throw new Error(
            `Dispatching while constructing your middleware is not allowed. ` +
              `Other middleware would not be applied to this dispatch.`
          )
        }
    
        const middlewareAPI = {
          getState: store.getState,
          dispatch: (...args) => dispatch(...args)
        }
        const chain = middlewares.map(middleware => middleware(middlewareAPI))
        dispatch = compose(...chain)(store.dispatch)
    
        return {
          ...store,
          dispatch
        }
      }
    }
    

    实际上所谓redux的中间件就是改写了store.dipatch方法。比如我们自己实现一个实现一个打印action的type的redux中间件,我们就可以这样写。

    ./redux-middlewares/log.js

    const logger = ()=>{
        return ({dispatch,getState}) => next => action =>{
            console.log('dispatching', action);
            return next(action);
        }
    };
    
    export default logger;
    

    跟别的redux中间件一起引入

    import createSaga from 'redux-saga';
    import createLog from './redux-middlewares/log';
    const saga = createSaga(); 
    const log = createLog();
    const middlewares = [saga,log]
    const store = createStore(
      rootReducer,
      applyMiddleware(...middlewares)
    );
    store.dispatch({type:'myAction'});
    
    // 打印 dispatching {type: "myAction"}
    

    这里
    redux的添加中间件
    applyMiddleware(...middleware) 就等于这里的
    dispatch = compose(...chain)(store.dispatch).
    跟我们实行
    compose(addFont,toUpper)('test')是一样的效果。

    参考文章:

    https://www.cnblogs.com/cloud-/p/7282188.html

  • 相关阅读:
    求助
    第五次作业
    第四次作业
    第三次作业
    第二次作业(四则运算)
    关于软件工程相关疑问
    小组成员名单()
    第四次作业
    第二次作业
    第一次作业
  • 原文地址:https://www.cnblogs.com/liaozhenting/p/10166671.html
Copyright © 2011-2022 走看看