不小心被推到前端话题了我,下面只是一些临时的记录而已,不推荐阅读,打扰了
还很乱套,做个记录先。。。
Flutter 的三层结构
- widget 层:用来定义state、props 和组件之间的关系,以及 widget 上下级关系
- element 层:更像是 widget 层的 shadow layer,element 实际上负责构成了 flutter 内部的 UI 树
- render object 层:用于封装 UI 底层的渲染逻辑
常见的 widget 和 element 基本上是一对一的关系,如下图所示:
图中 w1/w2 表示 widget,其中 w1 是 w2 的父节点。E1 和 E2 表示 Element。上下级的 Widget 之间具有 build 关系,w2 是通过调用 w1 的 build 方法创建的。
实际在界面渲染过程中,flutter 并没有直接使用 Widget 这个数据结构来表示组件树,而是额外用 Element 类来表示。如上图,每个 Element 实例是通过调用 Widget 的 createElement 方法创建的,创建的 Element 保留了 Widget 实例的引用。
在渲染整个 UI 时,widget 和 element 之间的调用关系是:
- 系统先创建最上层的 w1
- 调用 w1.createElement 方法创建同级关系的 e1
- e1 再调用 w1 的 build 方法,创建 w1 的子节点 w2
- 然后在 w2 上重复步骤 1 ~ 3,逐级的将一层层的 widget 和 element 创建出来,形成整个 UI 结构
可以看到的是 Widget 上下级之间是没有直接的引用关系的,而 Element 上下级之间是有直接引用关系的,最后的 UI 树效果如下图所示:
其中,图中蓝色的节点表示 Element 实例,绿色节点表示 Widget 实例。
上面介绍的 Widget 和 Element 其实是主要是封装一些 “容器” 类组件,像 Text 等“叶子”节点类型的组件,则没有 _child 而是有 renderObject 属性。就以 Text 为例。创建 Text 节点的时候其实会创建以下几个对象:
- Widget: Text 实例
- Widget: RichText 实例,作为 Text 示例的子节点
- Element: LeafRenderObjectElement 实例
- Render Object: RenderParagraph
渲染流程也和上面介绍的流程不一样:
- Text 创建 ComponentElement 实例
- ComponentElement 实例反过来调用 Text build 方法,创建 RichText 实例,然后 ComponentElement 实例 updateChild 方法去更新 RichText 实例,这个过程会调用到 RichText 实例的 update 方法
- RichText 实例的 update 方法当中会调用 RichText 实例的 updateRenderObject 方法
- 调用 updateRenderObject 方法,而不是更新 child 节点,因为是叶子节点嘛,本来就没有子节点了
- updateRenderObject 方法当中会更新 RenderParagraph 的属性,比如 textAlign、text 等
- RenderParagraph 实例的属性更新时,会将自身登记到渲染模块的 dirty nodes 当中去
- 等 UI 树更新完以后,渲染模块会遍历 dirty nodes,进行最后的 UI 渲染环节
结合上面的内容,UI 树的结构直观上应该是下图这个样子:
其中红色的对象表示的是各种 RenderObject 子类实例
总结:
- 大部分的 Widget 和 Element 是一对一关系
- Element 实例组成了 UI 树
- 叶子节点的 Element 实例会包含 RenderObject 实例
- RenderObject 的各种子类封装了最终的绘制逻辑,底层用的是 canvas 接口来绘制
- 更新 UI 树的过程中:
- widget 用来构建新的 Element 树
- 叶子节点类型 Element 实例会包含 RenderObject 实例
- 叶子节点属性更新时,实际更新的是 RenderObject 中的实例
- 当 Element UI 树更新完成之后,渲染模块会遍历 dirty nodes 来重新布局和渲染
问题
- Text 节点是如何渲染的呢
- 首先调用 pipeline owner 的 flushLayout
- 然后调用它的 flushPaint 进行渲染
- pipeline owner 会将 dirty nodes 根据深度来进行排序,层次浅的先绘制
- 绘制过程当中会调用 RenderObject 的 paint 方法
- paint 方法当中封装的绘制 canvas 的逻辑
- RenderParagraph 方法当中封装的是将文本绘制到 canvas 上面的逻辑,主要是用了一个叫做 TextPainter 的模块