Flutter-Widget、Element、RenderObject的渲染原理

这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战

前言

之前看过一篇文章介绍 Flutter 的渲染原理,里面讲到了渲染的三棵树,后面就整理了关于这三棵树的知识点,Flutter 的三棵树是什么?这三棵树的对应关系是怎么样的?Widget 频繁的创建和更改会影响性能吗?以及为什么要创建三棵树?

Flutter 的三棵树

这三棵树分别是:Widget、Element、RenderObject

  • Widget树:存放渲染内容、视图布局信息;

  • Element树:根据 Widget 的布局属性进行 layout 和绘制;

  • RenderObject树:存放上下文,通过 Element 遍历视图树,Element 同时持有 Widget 和RenderObject;

Widget 树

Widget 是用户页面的描述,表示了 Element 的配置信息,Flutter 页面都是由各种各样的 Widget 组合声明成的。Widget本身是不可变的 immutable

这也就是说,所有它直接声明或继承的变量都必须为 final 类型的。如果想给 widget 关联一个可变的状态,就要考虑使用 StatefulWidget ,它会通过 createState 创建一个State对象,然后每当它转化成一个 Element 时会合并到树上。

而对于 Widget 又分为无状态的 StatelessWidget 和有状态的 StatefullWidget

  • StatelessWidget:无中间状态变化的widget,需要更新展示内容就得通过重新创建,flutter推荐尽量使用StatelessWidget;
  • StatefullWidget:存在中间状态变化,那么问题来了,widget不是都immutable的,状态变化存储在哪里?flutter 引入state的类用于存放中间态,通过调用state.setState()进行此节点及以下的整个子树更新;

State

一个StatefulWidget类会对应一个State类,State表示与其对应的StatefulWidget要维护的状态,State中的保存的状态信息可以:

在 Widget 构建时可以被同步读取。 在 Widget 生命周期中可以被改变,当State被改变时,可以手动调用其setState()方法通知Flutter framework状态发生改变,Flutter framework 在收到消息后,会重新调用其build方法重新构建Widget树,从而达到更新UI的目的。

扫描二维码关注公众号,回复: 13673801 查看本文章

对于Widget的生命周期,这篇文章有介绍:juejin.cn/post/703469…

Element 树

Widget 树是非常不稳定的,经常会执行 build 方法,一旦调用 build 方法意味着这个 Widget 依赖的所有其他 Widget 都会重新创建,如果 Flutter 直接解析 Widget树,将其转化为 RenderObject 树来直接进行渲染,那么将会是一个非常消耗性能的过程,那对应的肯定有一个东西来消化这些变化中的不便,来做cache

所以,这里就有另外一棵树 Element 树。Element 树这一层将 Widget 树的变化做了抽象,可以只将真正需要修改的部分同步到真实的 RenderObject 树中,最大程度降低对真实渲染视图的修改,提高渲染效率,而不是销毁整个渲染视图树重建。

RenderObject 树

渲染树的任务就是做组件的具体的布局渲染工作,渲染树上每个节点都是一个继承自 RenderObject 类的对象,在 Element 中有 createRenderObject 生成RenderObject,该对象内部提供多个属性及方法来帮助框架层中的组件如何布局渲染。RenderObject 用于应用界面的布局和绘制,保存了元素的大小,布局等信息,实例化一个 RenderObject 是非常耗费性能的。

Flutter三棵树关系

首先要知道,启动App整个创建树的流程是什么:

  • 创建 widget 树
  • 调用 runApp(rootWidget),将 rootWidget 传给 rootElement ,做为 rootElement 的子节点,生成 Element 树,再由 Element 树生成 Render 树

1568905393689-b4be3b8b-8b41-4b00-9dbd-8840ee23743d.png

  • Widget:存放渲染内容、视图布局信息,widget的属性最好都是immutable

  • Element:存放上下文,通过Element遍历视图树,Element同时持有Widget和RenderObject

  • RenderObject:根据Widget的布局属性进行layout,paint Widget传人的内容

从创建到渲染的大体流程是:

  • 根据 Widget生成 Element,然后创建相应的RenderObject并关联到Element.renderObject属性上,最后再通过RenderObject来完成布局排列和绘制
  • Element就是Widget在UI树具体位置的一个实例化对象,大多数Element只有唯一的renderObject,但还有一些Element会有多个子节点,如继承自RenderObjectElement的一些类,比如MultiChildRenderObjectElement
  • 最终所有Element的RenderObject构成一棵树,我们称之为Render Tree即渲染树

总结一下,我们可以认为Flutter的UI系统包含三棵树:Widget树、Element树、RenderObject树。他们的依赖关系是:Element树根据Widget树生成,而渲染树又依赖于Element树,最终的UI树其实是由一个个独立的Element节点构成。

Widget 频繁的创建和更改会影响性能吗?

Widget 的频繁创建和更改并不会影响性能,性能的影响主要就是,复用 Element 从而减少频繁创建和销毁 RenderObject 对象,因为频繁的实例化和销毁 RenderObject 对性能的影响比较大。

static bool canUpdate(Widget oldWidget, Widget newWidget) {
  return oldWidget.runtimeType == newWidget.runtimeType
      && oldWidget.key == newWidget.key;
}
复制代码

Flutter遵循一个最基本的原则:判断新的 Widget 和旧的 Widget 是否是同一个类型以及key是否一样,也就是看 canUpdate 函数返回值:

  • 如果返回 false,那就把 Widget、Element、RenderObject 分别从它们的树(包括它们的子树)上移除,然后创建新的对象;
  • 如果返回 true ,那就仅仅修改RenderObject中的配置,然后继续向下遍历;

在写Flutter的key介绍时,也举了例子说明了会存在复用:juejin.cn/post/705000…

为什么要设计三棵树?

复用 Element 对性能非常重要,因为 Element 拥有两份关键数据:Stateful widget 的状态对象及底层的 RenderObject。当应用的结构很简单时,或许体现不出这种优势,一旦应用复杂起来,构成页面的元素越来越多,重新创建三棵树的代价是很高的,所以需要最小化更新操作。当 Flutter 能够复用 Element 时,用户界面的逻辑状态信息是不变的,并且可以重用之前计算的布局信息,避免遍历整棵树。

总结:

并不是所有的Widget都会被独立渲染,只有继承至RenderObjectWidget的才会创建RenderObject对象。在Flutter渲染的过程中有三棵重要的树,Flutter引擎是针对Render树进行渲染。

Widget树、Element树、Render树

  • 每一个Widget都会创建一个Element对象

  • 隐式调用createElement方法,它会创建三种Element

  • RenderElement主要是创建RenderObject对象,继承RenderObjectWidget的Widget会创建RenderElement

    • 创建RanderElement 

    • Flutter会调用mount方法,调用createRanderObject方法

  • StatefulElement继承ComponentElement,StatefulWidget会创建StatefulElement 

    • 调用createState方法,创建State 

    • 将Widget赋值给state 

      • 调用state的build方法 并且将自己(Element)传出去,build里面的context 就是Widget的Element
  • StatelessElement继承ComponentElement,StatelessWidget会创建StatelessElement 

    • 主要就是调用build方法 并且将自己(Element)传出去

    参考文章:

    zhuanlan.zhihu.com/p/135969091

    www.yuque.com/xytech/flut…

猜你喜欢

转载自juejin.im/post/7055458665972645925
今日推荐