redux核心思想

一个是reducer, 另一个是middlewares

reducer 和 reduce

reducer可以说是redux的精髓所在。我们先来看下它。reducer被要求是一个纯函数

  • 被要求很关键,因为reducer并不是定义在redux中的一个东西。而是用户传进来的一个方法。

  •  纯函数也很关键,reducer应该是一个纯函数,这样state才可预测(这里应证了我开头提到的Redux is a predictable state container for JavaScript apps.)。

日常工作我们也会用到reduce函数,它是一个高阶函数。reduce一直是计算机领域中一个非常重要的概念。

reducer和reduce名字非常像,这是巧合吗?

我们先来看下reducer的函数签名:

function reducer(state, action) {
  const nextState = {};
  // 
  return nextState;
}

再看下reduce的函数签名

[].reduce((state, action) => {
  const nextState = {};
  // 
  return nextSate;
}, initialState)

可以看出两个几乎完全一样。最主要区别在于reduce的需要一个数组,然后累计变化。
reducer则没有这样的一个数组。

更确切地说,reducer累计的时间上的变化,reduce是累计空间上的变化。

如何理解reducer是累计时间上的变化?

我们每次通过调用dispatch(action)的时候,都会调用reducer,然后将reducer的返回值去更新store.state。

每次dispatch的过程,其实就是在空间上push(action)的过程,类似这样:

[action1, action2, action3].reduce((state, action) => {
  const nextState= {};
  // 
  return nextState;
}, initialState)

因此说,reducer其实是时间上的累计,是基于时空的操作。

middlewares

关于middleware的概念我们不多介绍,
感兴趣可以访问这里查看更多信息。

如下可以实现一个redux的middlewares:

store.dispatch = function dispatchAndLog(action) {
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', store.getState())
  return result
}

上述代码会在dispatch前后进行打印信息。类似的middleware我们可以写很多。
比如我们还定义了另外几个相似的中间件。

我们需要将多个中间件按照一定顺序执行:

// 用reduce实现compose,很巧妙。
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)))


// applyMiddleware 的源码
function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    let dispatch = () => null;
    let chain = [];

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 将middlewares组成一个函数
    // 也就是说就从前到后依次执行middlewares
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

// 使用
let store = createStore(
  todoApp,
  // applyMiddleware() tells createStore() how to handle middleware
  applyMiddleware(logger, dispatchAndLog)
)

上面就是redux关于middleware的源码,非常简洁。但是想要完全读懂还是要花费点心思的。首先redux通过createStore生成了一个原始的store(没有被enhance),然后最后将原始store的dispatch改写了,在调用原生的reducer之间,插入中间件逻辑(中间件链会顺序依次执行). 代码如下:

function applyMiddleware(...middlewares) {
  return createStore => 
    (...args) => { const store = createStore(...args);
    // let dispatch = xxxxx; return { ...store, dispatch } } 
} 

然后我们将用户传入的middlewares顺序执行,这里借助了compose,compose是函数式编程中非常重要的一个概念,他的作用就是将多个函数组合成一个函数,compose(f, g, h)()最终生成的大概是这样:

function(...args) { f(g(h(...args))) } 

因此chain大概长这个样子:

chain = [
  function middleware1(next) {
    // 内部可以通过闭包访问到getState和dispath },
  function middleware2(next) {
    // 内部可以通过闭包访问到getState和dispath },
... ]

 

有了上面compose的概念之后,我们会发现每一個middleware的input 都是一个参数next为的function,第一个中间件访问到的next其实就是原生store的dispatch。代码为证:dispatch = compose(...chain)(store.dispatch)。从第二个中间件开始,next其实就是上一个中间件返回的 action => retureValue 。有没有发现这个函数签名就是dispatch的函数签名。output是一個参数为action的function, 返回的function签名为 action => retureValue 用來作为下一个middleware的next。这样middleware就可以选择性地调用下一個 middleware(next)。社区有非常多的redux middleware,最为经典的dan本人写的redux thunk,核心代码只有两行, 第一次看真的震惊了。从这里也可以看出redux 的厉害之处。基于redux的优秀设计,社区中出现了很多非常优秀的第三方redux中间价,比如redux-dev-tool, redux-log, redux-promise 等等,有机会我会专门出一个redux thunk的解析。

 

posted @ 2019-10-25 18:02  单先生  阅读(485)  评论(0编辑  收藏  举报