这是我参与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的意图。

对于Widget的生命周期,这篇文章有介绍:/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 树

Flutter-三棵树(Widget、Element、RenderObject)的渲染原理

  • 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介绍时,也举了例子说明晰会存在复用:/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…