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的解析。