Redux是业内鼎鼎有名的状态管理工具,听说做者Dan Abramov就是凭借这个做品在业内名声大振的。一开始我觉得这个被广为流传使用的库代码,必然十分复杂,于是一直没有去研究它的设计与实现。直到有天心血来潮翻了下它的源码,发现不只暴露出来的API少,内部的API也少,总共不超过10个的API加起来竟然不到六百行代码。。。前端
本篇文章讨论的Redux源码是基于版本v4.0.1,只讨论其中核心API的实现并简单发表下对Redux框架发展的见解。首先,先上张Redux的数据流向图。react
就不详细的介绍这个架构了,简单的说下这个数据流中Redux实现的部分和须要咱们实现的部分分别是哪些:git
Redux实现: store.dispatch
、store.subscribe
、combineReducers
、applyMiddleware
程序员
咱们实现: actionCreator
、reducer
github
本篇文章咱们只分析Redux实现部分的代码编程
dispatch在store对象中,而store又由createStore生成,所以找dispatch方法得从createStore中翻起。打开createStore文件,咱们能够看到createStore中内部的同名方法,即为咱们所寻。redux
抛开头部的判断Action是否为纯对象,以及判断调用dispatch时是否dispatch正在调用中(不容许reducer中再次调用dispatch,不然容易形成无限递归)。能够看到dispatch直接调用了reducer,来得到一个新的state,并在得到了新state以后调用手动触发监听state改变的函数。数组
subscribe也在store对象中,翻阅createStore能够发现同名函数subscribe。架构
逻辑很明显将监听的函数放入对应的数组中,再返回一个解除监听的函数。app
isDispatching
:防止在中间件reducer中加入/解除监听函数isSubscribed
:用于减小屡次解除监听逻辑形成的性能损耗ensureCanMutateNextListeners
:这个函数内容是当currentListeners和nextListeners相同时,从currentListeners浅复制一份给nextListeners。由于dispatch后执行的是currentListeners中监听器的内容,因此能够理解为在当前触发的监听函数中若是调用解除监听函数要下一次再触发时才能生效。function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
复制代码
看完了store中的dispatch和subscribe已经看完了基本的redux骨架。剩下的都是给redux加强功能的。
咱们再看一个咱们组织reducer都会使用上的函数combineReducers。它的逻辑就在同名文件中
虽然代码看起来挺多,可是一点都不复杂,里面有很大一部分代码是用来肯定,主要能够当作两个部分。一个部分是获取reducer进行预处理并返回一个统一的reducer处理函数,另外一个部分就是实际调用reducer的处理逻辑了。(话外音:感受全部高阶函数都能这么分--!)
预处理除了判断逻辑,真正的执行逻辑也只有两步。一是将合并的reducer拷贝到一个const对象中(我想之因此这么作是防止,reducer对象被意外修改了吧)。二是获取合并的reducer的全部key,放入数组中以备后续处理。
调用执行部分的逻辑也很直白,直接遍历全部reducer来得到对应reducer产生的新状态。除这以外就是判断是否状态发生改变,若是不发生改变就反回原始state。关于这一步我以为是为了监听函数判断state是否发生变化,而nextState已经生成了应该没有什么优化的意义在其中了。
要看懂applyMiddleware须要结合着createStore的enhancer才行,由于applyMiddleware是特殊的enhancer。
能够看到enhancer
接受createStore
做为参数,而且返回的函数是一个接受dispatch
、preloadedState
(初始化的状态)做为参数的函数,而且调用后返回符合Redux规定API的store。
如今再来看applyMiddleware函数就能清楚它的函数签名是为了彻底符合enhancer的接口。咱们再看下applyMiddleware的内部逻辑:
要看懂applyMiddleware咱们还应该看看,它提供给外界编写middleware的API是什么样的。下面的logger是官方提供的一个例子。next表明的是下一个中间件。
咱们能够看到applyMiddleware前面的逻辑是在设计一个假的dispatch,为了防止在dispatch中再次dispatch形成死循环。核心的代码是我红圈圈出来的部分,如今还差一个compose素材就集齐了。
compose方法的做用是让参数函数逆序序执行并将结果做为下一个函数的参数,对于compose(f, g, h)
会得出(...args) => f(g(h(...args)))
。
如今再来看applyMiddleware中的核心代码,首先是使用map方法将middleware调用一遍,将返回值在组成一个数组。咱们看logger签名,能够发现这至关因而将参数getState注入其中,再返回传入next参数的函数。
而后再将这一组函数通过compose组成一个链式调用,而后调用生成包装好的dispatch函数。compose是逆序调用,即写在前头的函数后调用,写后头的先调用。然而这一步调用的函数实际上是做为前一个函数的next参数,还记得吗它是表明下一个中间件。在真正调用dispatch时仍是先执行第一个中间件的逻辑,顺序又正回来了。顺便说下做为第一个参数传入的dispatch函数会做为最后的中间件被调用,高明高明!
关于redux源码的分析就上面那么多了,下面主要是经过git对源码的追综,简述下本身对于redux这个框架发展的见解。
__时间:__2015-05-30
最初的代码目录结构为:
- src
- redux
connect.js
createDispatcher.js
flux.js
- stores
CounterStore.js
index.js
- App.js
复制代码
能够看到最初的代码是没有脱离view的,而且灵感有flux的影子在其中。
__时间:__2015-06-02
而后到了0.2.0版本,目录变成以下:
- src
createDispatcher.js
observers.js
performs.js
providers.js
index.js
复制代码
虽然如今没有了样板文件,可是阅读其readme文档能够发现它仍是根具体的view框架(react)绑定在一块儿的。这个版本有了初步的数据流想法。
// We're gonna need some decorators
import React from 'react';
import { observes } from 'redux';
// Gonna subscribe it
@observes('CounterStore')
export default class Counter {
render() {
const { counter } = this.props; // injected by @observes
return (
<p> Clicked: {counter} times </p>
);
}
}
复制代码
__时间:__2015-06-09
在此版本出现了与目前想类型的概念,已经出现了dispatch、getState方法。
- src
- utils
- components
createDispatcher.js
createRedux.js
index.js
Redux.js
复制代码
其中的Redux与createDispatcher与如今的createStore概念相相似,可是尚未中间件和enhancer。
__时间:__2015-08-15
此版本与如今已经很类似,在这个版本中store概念改成了reducer,出现了createStore方法,虽然没有出现enhancer,可是有了middleware的概念。而且将与react相挂钩的内容剥离到了react-redux库中。
- src
- util
createStore.js
index.js
复制代码
首先不得不为大佬的高产而叹服啊,不到3个月的时间内,就对代码进行了十次较大的版本变更。。。
其次咱们能够发现,做者一开始只是根据已有的状态管理规范作的包装。而后逐步迭代才有了今天的redux。而且一开始状态管理是跟react捆绑的,直到后来才将它拆分到react-redux中。对于redux的接口开放也从简单逐渐到强大(先有applyMiddleware后有enhancer)。不断与时俱进,从15年开始一直更新到了19年,除了考虑与其余框架的兼容性,还将目前出现的与其相关未彻底兼容的新特性(Symbol.observable)加入其中。
总结下redux的出现,不只仅是做者有着广阔的视野(对react和状态管理工具)、深入的认知(函数式编程),而且也由于他保持着热心与时俱进。
公众号二分之一程序员,分享前端、计算机基础知识,欢迎关注 :)
复制代码