前面我们分析了Widget,发现源码非常简单,主要逻辑就是构建子树,以及是创建Element。 那Element有何神奇?且看Element。
首先我们看下Element家族的继承关系:
我们可以看到Element系列庞大,但跟我们平常开发关系比较大却不多。接下来我们会主要围绕这个继承关系图,逐个分析主要的类。
DiagnosticableTree
这个没什么好说的,就是一堆方便处理调试信息的。Widget也继承这个。
BuildContext
这个我们可能熟悉一些,Widget的build方法有个BuildContext参数,事实上,那个context就是Element,而Element是实现了BuildContext,并暴露给Widget使用
整理了一下BuildContext的方法,主要是一系列获取方法。 访问指定类型祖先和child之类的
//注册依赖祖先InheritWigdet
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect });
//获取指定类型的祖先InheritWidget
T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object aspect });
//获取指定类型祖先InteritWidget的Element
InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();
//获取指定类型的祖先Widget
T findAncestorWidgetOfExactType<T extends Widget>();
//获取指定类型的祖先State
T findAncestorStateOfType<T extends State>();
//获取指定类型的最远祖先State
T findRootAncestorStateOfType<T extends State>();
//获取指定类型的RendObject
T findAncestorRenderObjectOfType<T extends RenderObject>();
//访问祖先Element
void visitAncestorElements(bool visitor(Element element));
//访问孩子Element
void visitChildElements(ElementVisitor visitor);
我们可以看到,通过BuildContext可以获取很多关于所在的Widget树、Element树、Reder树的特定信息。
Element
Element是Widget和RederObject的桥梁。关键方法主要有一下这些:
//根据新的newWidget,更新Element。如果无法更新,就新建一个Element
Element updateChild(Element child, Widget newWidget, dynamic newSlot)
//根据newWidget新建一个Element.
Element inflateWidget(Widget newWidget, dynamic newSlot)
//新建的Element挂载到parent时调用此方法.
void mount(Element parent, dynamic newSlot)
//Element真正执行更新的方法
void update(covariant Widget newWidget)
增加一些说明:
updateChild
和inflatWidget
主要作用是当Widget树发生变化时, 需要对应地创建Element。 不同的是updateChild
做了一些优化,尽可能地复用之前的旧Element。 只有在无法复用的情况下,才调用inflatWidget
去直接创建一个Element。 那什么情况可以复用Element呢?主要有:
- 新的Widget和旧Widget相等
- 新的Wiget和旧Widget类型相同,且key相等(如果key都为空也相等)
我们平时写Widget时,有时候会忽略key,但关系不大,如果新旧Widget的key都为空,也可以复用Element。
2. 一般新创建一个Element时,会马上调用mount
方法。 如果可以复用的场景,则调用update(widget)
方法。
接下来的篇章再分析Element的左子树。ComponentElment、StatelessElement、StatefulElement