Flutter之三棵树(Widget树、Element树、Render树)

440 阅读5分钟

和谐学习!不急不躁!!我是你们的老朋友小青龙~

前言

Flutter有三棵树

  • Widget树 (经常变化)
  • Element树
  • Render树(flutter引擎,渲染的是Render树)

不是所有的Widget都会有Render,比如

image.png

只有继承自RenderObjectWidget的Widget对象才有Render树

RenderObjectWidget

我们看到RenderObjectWidget有两个抽象方法:

  • RenderObjectElement createElement();
  • RenderObject createRenderObject(BuildContext context);

image.png

从字面意思应该是创建Element、创建RRenderObject。

createElement()

我们在RenderObjectWidget的子类MultiChildRenderObjectWidget类,发现它实现了createElement方法:

image.png

MultiChildRenderObjectElement继承自RenderObjectElement

image.png

createRenderObject(BuildContext context)

我们在【RenderObjectWidget】的子类Flex类源码里,看到它实现了父类的抽象方法createRenderObject:

image.png

并且看到它返回一个「RenderFlex」类,点进去发现层级关系:

RenderFlex -> RenderBox -> RenderObject

Flex实现父类的抽象方法createRenderObject,创建RenderObject对象并加入到RenderObject树。

即,`只有实现了createRenderObject方法创建了RenderObject对象的widget才会被独立渲染。`

RenderObjectWidget小结

所以我们大致有了这么一个结论:继承自RenderObjectWidget的Widget,会创建三棵树:

  • Widget树
  • Element树
  • Render树

那些没有继承RenderObjectWidget的Widget又会创建什么树呢?

首先Widget树肯定的,毕竟万物皆Widget。

其次,我们找几个Widget:

image.png

image.png

我们发现,无论是StatelessWidget还是StatefulWidget,都会实现createElement方法,只不过不同的Widget调用createElement方法,返回的类型不同,即:

  • StatelessWidget调用createElement方法,返回StatelessElement类型的Element;

  • StatefulWidget调用createElement方法,返回StatefulElement类型的Element;

  • MultiChildRenderObjectWidget调用createElement方法,返回MultiChildRenderObjectElement类型的Element;

  • RenderObjectWidget调用createElement方法,返回RenderObjectElement类型的Element;

查看StatelessElement

image.png

查看StatefulElement

image.png

而我们日常写法如下

image.png

都需要通过build方法来返回一个【最终Widget】,区别是:

  • 一个在StatelessElement子类里实现build方法(比如图片里的lesWidget)

  • 一个在State类里实现build方法

这刚好对应着前面图片源码里的两行代码:

/// StatelessElement源码
Widget build() => widget.build(this);
/// StatefulElement源码
Widget build() => state.build(this);

所以这就很好的解释了,为什么我们平时写代码的时候,实现StatefulWidget子类要多实现一个State类

再次查看StatelessElement

image.png再次查看StatefulElement

image.png

让我们捋一下思路

1. 无论是哪种Widget都会实现createElement方法;
2. createElement方法会返回各自的Element子类;
3. 各自的Element子类都会有一个build方法且返回类型是Widget4. StatelessElement实现build方法是通过widget.build(this);
5. StatefulElement实现build方法是通过state.build(this);

看到这里,我们隐约知道,Widget和Element存在着一定关系。再结合源码里的一段注释:

image.png

我们知道:Element是用来管理此小部件(Widget)在Widget树中的位置。

Widget、Element、Render三棵树的执行顺序

开启debug运行

image.png

进入断点后,点击进入下一步

image.png

进入mount

image.png

查看mount注释

image.png

所以我们知道:有新的Element被添加到树时,框架会调用mount函数。

前面的铺垫是为了找到mount的注释,我们知道RenderObjectWidget才会创建三棵树。

RenderObjectWidget类的【createElement】方法和【createRenderObject】方法是抽象方法,所以我们来到它的子类MultiChildRenderObjectWidget下断点:

image.png

MultiChildRenderObjectWidget没有实现【createRenderObject】方法,所以来到它的子类Flex下断点:

image.png

开启debug调试(这里以Column为例子),如果发现不是我们要探究的就下一步:

image.png

找到了Column真正调用【createElement】

image.png

继续放过断点,发现进入了方法Column的【createRenderObject】

image.png

点击RenderObjectElement.mount (framework.dart:5459)这一行

image.png

我们前面分析过,当Element被创建并添加到树时,会调用mount方法,而上图【mount】方法里调用了一句代码:

_renderObject = widget.createRenderObject(this);

所以由此可见,【createRenderObject】是在【createElement】之后才被调用的;

结论

  • Element和mount方法一一对应

  • 三棵树的创建先后顺序: Widget树-> Element树 -> Render树

1、调用Widget构造函数
2Widget会调用【createElement】创建Element树,紧接着调用mount方法;
3、如果Widget继承自RenderObjectWidget,mount方法内部会调用【createRenderObject】创建Render树;
4、如果Widget不是继承自RenderObjectWidget,mount方法内部就不会调用【createRenderObject】

// 即:只有继承自RenderObjectElement的Element,才会去创建Render。

三棵树具体是干啥的?

了解了三棵树的创建顺序,但是我们不禁发声:这些树创建有啥用?

目前我们只知道,Element树的mount决定着要不要创建Render树。

StatelessWidget的build方法的调用过程

image.png

进入StatelessWidget

image.png

进入StatelessElement

image.png

进入ComponentElement

image.png

进入rebuild

image.png

选中performRebuild,然后按住快捷键组合:command + option + B

image.png

选择(ComponentElement那个)进入

image.png

进入build,快捷键组合:command + option + B

image.png

选择(StatelessElement那个)进入

image.png

debug跑了下

image.png

可以看出来,StatelessElement的build调用,实际上是调用自定义StatelessWidget(比如MyApp)的build方法,也就是这个方法

image.png

捋一下思路:

1、调用StatelessWidget构造函数
2、调用createElement方法
3、调用ComponentElement类的mount方法
4、mount方法调用_firstBuild方法,也就是调用rebuild方法
5、rebuild方法调用ComponentElement类的performRebuild方法
6、performRebuild方法内部调用build()方法,build()方法在子类(StatelessElement)实现
7StatelessElement类的build()方法在自定义StatelessWidget类实现(例如MyApp),参数thisStatelessElement

// 【StatelessElement】继承自【ComponentElement】
// 即:StatelessWidget构造函数 -> createElement -> Element调用build(Element)

StatefulWidget的build方法的调用过程

进入StatefulWidget

image.png

进入StatefulElement

// 去掉了assert断言,代码省略部分用...
class StatefulElement extends ComponentElement {
    StatefulElement(StatefulWidget widget)
        : _state = widget.createState(),
          super(widget) {
    state._element = this;
    state._widget = widget;//标记1
    }
    @override
    Widget build() => state.build(this);
    ...
}

【标记1】这行代码说明,state保存了StatefulWidget对象,所以在我们平时写代码的时候,可以在State类里通过【widget】去访问StatefulWidget里定义的成员变量。比如:

image.png

发现相比StatefulWidget,这里多了一步创建_state

 _state = widget.createState()

而调用build方法,中间部分跟StatelessWidget差不多,最后还是会通过state.build(this)去调用(this指的是StatefulElement)。

总结:

1、调用StatefulWidget构造函数
2、调用createElement方法,内部创建_state对象,_state对象保存了当前StatefulWidget对象
3、调用ComponentElement类的mount方法
4、mount方法调用_firstBuild方法,也就是调用rebuild方法
5、rebuild方法调用ComponentElement类的performRebuild方法
6、performRebuild方法内部调用build()方法,build()方法在子类(StatefulElement)实现
7StatefulElement类的build()方法在自定义的State类实现(例如_MineAPPState),参数thisStatefulElement

// 【StatefulElement】继承自【ComponentElement】
// 即:StatefulWidget构造函数 -> createElement -> _state调用build(Element)