
在平时开发的过程中,会应用react-redux这个中间件,但是每次更新时,不管redux中的state是否变化,都会重新更新mapStateToProps里面的值,这会造成大量冗余的操作。reselect作为redux性能优化的中间件已经在广泛使用,只有当redux中的state发生变化时,才会更新mapStateToProps中的值。下面来对其源码进行一下分析,看一下其内部是如何做到这方面性能优化的。
reselect常用的Api是createSelector,那就先来看一下它的源码:
createSelector
1 | export const createSelector = createSelectorCreator(defaultMemoize) |
createSelector的源码就这一行,它调用了createSelectorCreator方法并传入defaultMemoize作为参数。而createSelectorCreator也是relect的一个Api,所以createSelector是根据createSelectorCreator来的,那接下来就看一下defaultMemoize、createSelectorCreator的源码。
在看源码之前,需要先看两个工具函数,它们起着很重要的作用:
1 | function defaultEqualityCheck(a, b) { |
这个函数很简单,就是对两个参数进行强比较。
1 | function areArgumentsShallowlyEqual(equalityCheck, prev, next) { |
这个函数也不复杂,其主要是遍历元素,然后调用equalityCheck方法,如果有一项不满足,就会马上退出。
defaultMemoize
1 | /* |
defaultMemoize是reselect的核心方法,通过上面的源码分析可以知道,每次使用时,都会把参数arguments通过闭包的方式保存起来,这样下次再调用时,可以获取到上次保存的值(也就是prevArguments),然后与本次的arguments(也就是nextArguments)做对比,只有变化的时候,才会再次调用传入的func方法。而这里的arguments其实就是在mapStateToProps中传入的参数
createSelectorCreator
1 | /* |
getDependencies方法:
1 | /* |
creatSelector实例
1 | //selector |
上面是非常简单的例子,来结合源码看一下过程:
1、根据分析,getNumA、getNumB、sumFn作为createSelectorCreator返回函数的参数funcs传入,先将sumFn取出,然后其他参数做是否为函数校验。
2、定义memoizedResultFunc,其值是经过defaultMemoize转换后的值,也就是:
1 | const func1 = function () { |
3、定义selector,其值也是经过defaultMemoize转换后的值:
1 | const func2 = function () { |
4、当在mapStateToProps中调用numSum方法并传入state时,会执行selector方法,所以selector中的arguments就是state,在selector内部会执行func2方法,同时用闭包的方式保存下来arguments,这样在getNumA、getNumB就会接收到state参数,将结果放入params数组中,执行memoizedResultFunc传入params
5、memoizedResultFunc内的arguments就是传入的params,然后执行func1方法,也就以params为参数执行了sumFn,同样会用闭包保存下来arguments,最终得到的num就是sumFn执行后的结果
6、此时保存了两个arguments,一个是state,一个是params。只有当两个都发生变化的时候,num才会更新,否则就一直保持原来的值
createSelectorCreator其它用法
看完源码知道,createSelector是基于createSelectorCreator实现的,传入的参数是defaultMemoize,也就是reselect内部默认的方法。但reselect也允许自定义可配置的selector,也就是传入createSelectorCreator的参数可以自己选择,比如传入lodash、underscore中的比较、对比、缓存等方法,具体用法可以查看github文档,里面有详细介绍。
总结
经过上面的分析,createSelector也就是createSelectorCreator内部最主要是应用了闭包的方式,保存了之前的参数值,然后当再次调用方法传入参数的时候,会将前后两次的值进行对比,只有在发生变化的时候,才会真正调用定义selector时的方法。