Flutter(三十四)-Widget树、Render树和Element树
在Flutter
渲染过程中有三棵树:Widget树
、Render树
和Element树
;
Widget树
什么是Widget树
呢,其实在我们的Android Studio
中能够直观的看到某一个页面渲染之后Widget树
的样子,我们以聊天界面为例看一下页面的Widget树
;
点击Android Studio
右侧的Flutter Inspector
工具,就可以呈现出当前页面的Widget树
的层级关系:
需要注意的是,Flutter
的渲染引擎并不是来直接渲染Widget树
的,因为如果直接渲染Widget树
,那么一旦build
方法执行,那么整个Widget
都需要重新渲染将会是非常消耗性能的(Widget
是经常发生变化的);
Render树
除了Widget树
,还有一个Render树
,其实**Flutter**
引擎渲染的是**RenderObject树**
,它里边是一个个的RenderObject
的对象;并非所有的Widget
都会生成RenderObject
,比如Container
;只有最终继承自RenderObjectWidget
的才会生成RenderObject
,比如Column
;
在RenderObjectWidget
中有几个需要我们注意的地方,我们来看一下RenderObjectWidget
的源码:
abstract class RenderObjectWidget extends Widget {
const RenderObjectWidget({ Key? key }) : super(key: key);
@override
@factory
RenderObjectElement createElement();
@protected
@factory
RenderObject createRenderObject(BuildContext context);
@protected
void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }
@protected
void didUnmountRenderObject(covariant RenderObject renderObject) { }
}
在RenderObjectWidget
中有createElement()
和createRenderObject()
两个抽象方法;这两个方法需要RenderObjectWidget
的子类来实现,我们可以通过Stack
的继承关系来查看发现RenderObjectWidget
的子类:
- 子类
MultiChildRenderObjectWidget
中实现了createElement
方法:
@override
MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this);
而MultiChildRenderObjectElement
继承自RenderObjectElement
;
- 子类
Stack
中实现了createRenderObject
方法:
@override
RenderStack createRenderObject(BuildContext context) {
assert(_debugCheckHasDirectionality(context));
return RenderStack(
alignment: alignment,
textDirection: textDirection ?? Directionality.maybeOf(context),
fit: fit,
clipBehavior: overflow == Overflow.visible ? Clip.none : clipBehavior,
);
}
在这个方法中创建了一个RenderStack
,而根据RenderStack
的继承链发现其继承自RenderObject
;
综上,继承自RenderObjectWidget
的对象,既会创建Element树
,又会创建RenderObject树
;
Element树
我们根据继承链可以发现StatelessWidget
直接继承自Widget
,其内部没有RenderObject
对象,但是有Element
对象:
在Widget
的源码中存在createElement()
的抽象方法,也就是所有继承自Widget
的对象都有Element
对象:Widget树
和Element树
是一一对应的关系;
同样的,在StatefulWidget
中实现了相同的方法:
我们在StatefulWidget
中的createElement
方法打上断点,然后使用Debug
模式运行项目,查看一下断点的情况:
我们发现,从整个项目运行开始,第一个调用createElement
的是MaterialApp
这个组件;此时的this
就是MaterialApp
;
那么如果,我们将断点打在StatelessWidget
中,我们来看一下执行的情况:
此时的this
将会指向MyApp
;我们的main.dart
文件如下:
这个时候,我们单步执行一下:
这个时候代码将会执行到:
此处接下来将会执行mount
方法,我们来看一下mount
方法,我们主要看一下注释:
从注释我们可以得知,当有一个新的Element
被添加到Element树
的时候,mount
方法将会被调用;因为Widget
与Element
是一一对应的,所以我们也可以理解为当有一个Widget
被创建的时候,mount
方法就会被调用;
新添加的
Element
与原来的旧Element
是有区别的;
我们从上文已经知道StatefulWidget
和StatelessWidget
是没有RenderObject
对象的,那么我们继续查看一下有RenderObject
对象的组件的mount
方法:
单步断点执行:
接下来断点进入mount
方法:
我们发现此时的mount
方法调用了super.mount
,我们进入super.mount
方法:
我们发现在super.mount
方法中调用了creatRenderObject
方法,此时的mount
方法是由RenderObjectElement
实现的;
那么我们可以得出一下结论:
在
Flutter
渲染流程中,最终是针对Render树
中的对象进行渲染;当一个Widget
被创建时,都会通过createElement
方法创建一个Element
加入到Element树
中,然后会执行mount
方法,此时如果含有RenderObject
(Element
是否继承自RenderObjectElement
),则会在mount
方法中通过createRenderObject
方法创建RenderObject树
,反之则不创建;
转载自:https://juejin.cn/post/7054711662619656205