从源码解析 flutter_redux 的精准局部刷新
前言
对于非顶级的 Store
,我们测试的时候会发现一个有趣的现象,那就是 StoreConnector
构建的 Widget
在状态发生改变的时候,并不会重建整个子组件,而是只更新依赖于 converter
转换后对象的组件。这说明 StoreConnector
能够精准地定位到哪个子组件依赖状态变量,从而实现精准刷新,提高效率。这和 Provider
的 select
方法类似。本篇我们就来分析一下 StoreConnector
的源码,看一下是如何实现精准刷新的。
验证
我们先看一个示例,来验证一下我们上面的说法,话不多说,先看测试代码。我们定义了两个按钮,一个点赞,一个收藏,每次点击调度对应的 Action 使得对应的数量加 1。两个按钮的实现基本类似,只是依赖状态的数据不同。
按正常的情况,状态更新后应该是整个子组件rebuild
,但是实际运行我们发现只有依赖于状态变量的TextButton
和其子组件 Text
进行了 rebuild
。我们在两个按钮的 build
方法打印了对应的信息,然后在 TextButton
(build
方法在其父类ButtonStyleButton
中)和 Text
组件的 build
中打上断点,来看一下运行效果。
从运行结果看,点击按钮的时候 TextButton
和 Text
的 build
方法均被调用了,但是 FavorButton
和 PraiseButton
的 build
方法并没有调用(未打印对应的信息)。这说明 StoreConnector
进行了精准的局部更新。接下来我们从源码看看是怎么回事?
StoreConnector 源码分析
StoreConnector
的源码很简单,本身 StoreConnector
继承自 StatelessWidget
,除了定义的构造方法和属性(均为 final)外,就是一个 build
方法,只是 build 方法比较特殊,返回的是一个_StoreStreamListener<S, ViewModel>
组件。来看看这个组件是怎么回事。
_StoreStreamListener
是一个StatefulWidget
,核心实现在_StoreStreamListenerState<S, ViewModel>
中,代码如下所示。
关键的设置都在 initState 方法中。在 initState 方法中,如果设置了 onInit
方法,就会将 store
传递过去调用状态的初始化方法,例如下面就是我们在购物清单应用中对 onInit
属性的使用。
接下来是调用_computeLatestValue
方法,实际是通过converter
方法获得转换后的ViewModel
对象的值,这个值存储在ViewModel _latestValue
属性中。然后是,如果定义了 onInitialBuild
方法,就会使用 ViewModel
的值做初始化构造。
最后调用了_createStream
方法,这个方法很关键!!!实际上就是吧 Store
的onChange
事件按照一定的过滤方式转变了成了 Stream<ViewModel>
对象,其实相当于是只监听了 Store
中经过 converter
方法转换后那一部分ViewModel
对象的变化——也就是实现了局部监听。处理数据变化的方法为_handleChange
。实际上就是将变化后的 ViewModel
加入到流中:
因为 build
方法中使用的是 StremaBuilder
组件,并且会监听_stream
对象,因此当状态数据转换后的 ViewModel
对象发生改变时,会触发 build
方法进行重建。而这个方法最终会调用 StoreConnector
中的 builder
属性对应的方法。这部分代码正好是 PraiseButton
或 FavorButton
的下级组件,这就是为什么状态发生变化时 PraiseButton
和 FavorButton
不会被重建的原因,因为他们不是StoreConnector
的下级组件,而是上级组件。
也就是说, 使用StoreConnector
这种方式时,当状态发生改变后,之后刷新它的下级组件。因此,从性能考虑,我们可以做最小范围的包裹,比如这个例子,我们可以只包裹 Text
组件,这样 Container
和 TextButton
也不会被刷新了。
为了对比,我们只修改了 PraiseButton
的代码,实际打断点发现点击点赞按钮的Container
不会被刷新,而TextButton
会刷新,分析发现是TextButton
的外观样式在点击的时候改变导致的,并不是Store
状态改变导致。也就是说,通过最小范围使用 StoreConnector
包裹子组件,我们可以将刷新的范围缩到最小,从而最大限度地提升性能。具体代码可以到这里下载(partial_refresh
部分):Redux 状态管理代码。
总结
很多时候我们在使用第三方插件的时候,都是跑跑 demo,然后直接上手就用。确实,这样也能够达到功能实现的目的,但是如果真的遇到性能上面的问题的时候,往往不知所措。因此,对于有些第三方插件,还是有必要保持好奇心,了解其中的实现机制,做到知其然知其所以然。
版权声明: 本文为 InfoQ 作者【岛上码农】的原创文章。
原文链接:【http://xie.infoq.cn/article/38f6ab053c0a4774356249fc7】。文章转载请联系作者。
评论