面试题答案
一键面试Flutter有状态Widget动态更新在框架底层的工作原理
- Element树
- Flutter采用Widget - Element - RenderObject三层结构。Widget是不可变的配置数据,描述了UI的外观和行为。而Element是Widget在树中的实例,它是可变的,关联了Widget、父Element和子Element,并且可以在Widget更新时被复用。例如,当有状态Widget的状态改变触发重新构建时,新的Widget可能与旧的Widget类型相同但配置不同,此时对应的Element会复用,只是更新关联的Widget。
- Element树负责管理Widget的生命周期,如插入、删除和更新。当Widget状态改变,Element会标记为脏,以便在后续的构建过程中更新。
- BuildOwner
- BuildOwner负责管理Widget的构建过程。它维护了一个需要重新构建的Element列表(dirtyElements)。
- 当有状态Widget调用setState时,会通知其对应的Element,Element进而标记自己为脏,并将自身添加到BuildOwner的dirtyElements列表中。
- BuildOwner在适当的时机(如在下一帧绘制前),遍历dirtyElements列表,调用每个脏Element的rebuild方法,重新构建Widget树的相关部分。
- 脏检查机制
- 脏检查机制是Flutter实现状态更新的核心。当有状态Widget的状态改变调用setState时,Widget对应的Element被标记为脏。
- 在构建阶段,BuildOwner从根Element开始深度优先遍历dirtyElements列表。对于每个脏Element,它会调用该Element的build方法来构建新的Widget。
- 在构建新Widget时,Flutter会对比新旧Widget的类型和Key(如果有)。如果类型相同且Key相同,Element会复用,只更新Widget的配置;否则,会创建新的Element。例如,一个ListView中的ListItem,当数据改变但ListItem的类型和Key不变时,对应的Element会复用,只是更新显示的数据。
自定义有别于setState的状态更新机制的入手点及实现思路
- 数据管理
- 状态存储:可以考虑使用独立于Widget的状态管理类,例如类似于Redux的状态存储方式,将应用的状态集中存储在一个状态容器中。这样所有需要访问或更新状态的部分都从这个容器获取或修改数据,而不是像setState那样分散在各个Widget中。
- 数据监听:为状态容器添加监听机制,类似于Rx - Dart的Observable模式。当状态发生变化时,通知相关的Widget进行更新。
- Widget更新触发
- 自定义通知方式:创建一种新的通知机制来代替setState。可以是事件总线的方式,当状态改变时,通过事件总线发布事件,订阅了该事件的Widget接收到事件后进行更新。
- Widget标识:为了精准通知需要更新的Widget,需要一种方式标识哪些Widget依赖哪些状态。可以在Widget中注册其依赖的状态,当这些状态变化时,对应的Widget才进行更新,避免不必要的重建。
- Element树管理
- 手动标记脏Element:在自定义机制中,需要手动标记那些因状态变化需要更新的Element为脏。可以通过扩展Element类,添加自定义的标记方法,在状态变化且确定需要更新某个Widget对应的Element时,调用该方法标记其为脏,让BuildOwner在合适时机重新构建。
- 优化Element复用:在自定义更新机制下,进一步优化Element的复用逻辑。例如,根据自定义的状态依赖关系,更精准地判断是否可以复用Element,而不仅仅依赖于Widget的类型和Key,以提高性能。