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 可选

扫描二维码关注公众号,回复: 4625849 查看本文章

作为第一次调用 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#%E5%8A%9F%E8%83%BD%E5%9E%8B%E5%87%BD%E6%95%B0%E7%AE%A1%E9%81%93

类似的我们来写一个。

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

猜你喜欢

转载自www.cnblogs.com/liaozhenting/p/10166671.html
今日推荐