likes
comments
collection
share

Flutter 学习笔记 Element 流程

作者站长头像
站长
· 阅读数 37

年初开始学习使用Flutter,对于setState()、element与widget的关系了解的不透彻,有一段时间的上手经验之后再看大佬的文章有了一些感觉,所以写了一些自己的学习总结防止忘记,也不一定正确敬请指正

以下是大佬的两篇文章,建议先多看几遍,当然其他大佬的文章也很多,关键一定多看几遍看仔细

Element

inflateWidget
Element inflateWidget(Widget newWidget, dynamic newSlot){    
    final Element newChild = newWidget.createElement();
    newChild.mount(this, newSlot);
    return newChild;
  }

通过子widget创建新的子element,并将子element挂载到element树中,源码的注释中解释,此方法通常由[updateChild]调用,但可以由需要对创建元素进行更细粒度控制的子类直接调用。简单理解是有updateChild调用的,为什么命名变量叫newChild,就是因为一般在updateChild中调用inflateWidget()返回给父element(也就是当前element)

mount

  @mustCallSuper
  void mount(Element parent, dynamic newSlot) {
    _parent = parent;
    _slot = newSlot;
    _depth = _parent != null ? _parent.depth + 1 : 1;
    _active = true;
    _updateInheritance();
  }

_updateInheritance方法顾名思义应该是inheritwidget相关,先不管这个 注意到函数参数parent就是inflateWidget的this,这也佐证了上面inflateWidget通常由父element.updateChild调用

updateChild
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
  if (newWidget == null) {
    if (child != null)
      //child == null && newWidget == null
      deactivateChild(child);
    //child != null && newWidget == null
    return null;
  }
  if (child != null) {
    if (child.widget == newWidget) {
      //child != null && newWidget == child.widget
      if (child.slot != newSlot)
        updateSlotForChild(child, newSlot);
      return child;
    }
    if (Widget.canUpdate(child.widget, newWidget)) {
      if (child.slot != newSlot)
        updateSlotForChild(child, newSlot);
      //child != null && Widget.canUpdate(child.widget, newWidget)
      child.update(newWidget);
      return child;
    }
    deactivateChild(child);
  }
  // child != null && !Widget.canUpdate(child.widget, newWidget)
  return inflateWidget(newWidget, newSlot);
}
  • 如果之前的位置child为null
    • 如果newWidget为null的话,说明这个位置始终没有子节点,直接返回null即可。
    • 如果newWidget不为null,说明这个位置新增加了子节点调用inflateWidget(newWidget, newSlot)生成一个新的Element返回
  • 如果之前的child不为null
    • 如果newWidget为null的话,说明这个位置需要移除以前的节点,调用 deactivateChild(child)移除并且返回null
    • 如果newWidget不为null的话,先调用Widget.canUpdate(child.widget, newWidget)对比是否能更新。这个方法会对比两个Widget的runtimeType和key,如果一致则说明子Widget没有改变,只是需要根据newWidget(配置清单)更新下当前节点的数据child.update(newWidget);如果不一致说明这个位置发生变化,则deactivateChild(child)后返回inflateWidget(newWidget, newSlot)
update
@mustCallSuper
  void update(covariant Widget newWidget) {
    _widget = newWidget;
  }

抽象方法,内容也很简单,简单理解就是子element更新自身的widget,相当于复用子element,重新赋值了子widget

rebuild和performRebuild
void rebuild() {
    performRebuild();
  }
@protected
  void performRebuild();

这两个方法就更简单了就是要给子类重写的....

疑问

大概理解了上面的内容之后,又出现了几个新的问题

  1. updateChild什么时候调用呢
  2. Element没有_child变量,updateChild之后生成的element又给了谁呢
  3. 假如A的child是B,A通过updateChild生成B之后,B如何生成B自己的child

上面这些问题都可以在源码中找到答案,ComponentElement、StatelessElement、StatefulElement这些子类中重写上述的方法,我们的疑问可以在子类的得到解决

ComponentElement

mount
  @override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    _firstBuild();
  }

这里新增了一个方法

_firstBuild
void _firstBuild() {
    rebuild();
  }

看到重新调用了rebuild(); 在之前文章中,buildOwner.buildScope(renderViewElement)方法中遍历脏element数组执行_dirtyElements[index].rebuild()

performRebuild
@override
  void performRebuild() {
    Widget built;
    built = build();
    _child = updateChild(_child, built, slot);
  }

Element中performRebuild是空,ComponentElement重写了方法,同时创建了Element _child变量,并在updateChildinflateWidget生成新的子element并赋值给_child

firstBuild 顾名思义第一次创建挂载element时触发 rebuild 顾名思义刷新界面时触发 在ComponentElement中都是触发performRebuild,并没什么不同,Stateless与Stateful中会有不同的处理

StatelessElement

update
@override
  void update(StatelessWidget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    _dirty = true;
    rebuild();
  }

StatelessElement中只有update重写,调用了rebuild,之后的流程就是performRebuild等等

看到这里梳理一下思绪 假如StatelessWidget A child StatelessWidget B 那么StatelessElement Aelement,Belement 当第一次加载的时候调用顺序是

parent.inflateWidget => A.createElement => Aelement.mount => Aelement.firstBuild => Aelement.rebuild => Aelement.performRebuild => Aelement.updateChild => 因为此时_child是空 所以Belement也走一遍重新创建的流程

当设置Aelement.markneedBuildAelement.rebuild, Belement已经存在,Aelement._child不为空,那么Aelement.updateChild比较,canUpdate==true_child.update => _child.rebuild

下面这张图对照着看加深下印象

Flutter 学习笔记 Element 流程

回答疑问
  1. updateChild什么时候调用呢
  • rebuild的时候调用performRebuild,接着调用updateChildrebuild是什时候调用呢?是在firstBuild和系统刷新回调脏数组遍历脏数组调用rebuild
  1. Element没有_child变量,updateChild之后生成的element又给了谁呢
  • 子类中定义_childupdateChild赋值给_child
  1. 假如A的child是B,A通过updateChild生成B之后,B如何生成B自己的child
  • update方法中rebuild自己更新child
转载自:https://juejin.cn/post/6955734850833743880
评论
请登录