看了一篇讲 Flutter 的文章, 然后将这篇的内容浓缩了一下.

结构

Flutter Community.

参考这个链接, 也是 Flutter 社区中的一篇文章.

比如有如下代码:

dart
void main() => runApp(SimpleApp());

class SimpleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text('Hello World'),
    );
  }
}

下面就来对流程进行分析.

三棵树

在 runApp首次执行时, 会发生如下动作:

  1. Flutter 创建一颗包含上面代码中三个 Widget 的树, 即 App–>Container–>Text.
  2. Flutter 遍历 Widget 树, 针对每个 Widget 调用其 createElement 方法, 从而生成一颗对应的 Element 树.
  3. 同时, Flutter 调用每个 Element 的 createRenderObject 方法, 从而生成第三棵 RenderObject 树.

在这里面, 每个 Element 会持有其对应的 Widget 和 RenderObject 的引用.

RenderObject

它内部包含渲染 Widget 的所有逻辑, 包括布局, 绘制, 以及点击处理.

且它的初始化过程十分“昂贵”, 故策略就是尽量去重用已创建的 RenderObject.

而 Element 正是这样重用的媒介, 它作为不可变的 Widget 和可变的 RenderObject 间的桥梁, 在它内部包含了高效的比较算法, 通过比较 Widget 和 RenderObject 间的差异, 从而确定 RenderObject 的改变.

这样的话就可以尽量重用 RenderObject, 而且可以把改变只限制在某些节点或子树中, 从而达到很高的性能.

Widget 由于十分轻量, 故它们非常适合用于描述当前 APP 的状态(有时也称为App 的 configuration).

而在日常开发中, 很少直接接触 Element 对象的原因是: Flutter 将 Element 对象包装在 BuildContext 接口对象内, 在 build(BuildContext context) 方法中获取封装了不同 Element 对象的 BuildContext.

这也是为什么不同的 Widget 会对应不同的 BuildContext 的原因.

下一帧

由于 Widget 对象是不可变的, 当 configuration 发生改变(即 Widget 或其属性发生改变), Flutter 将会重建 Widget 树. 树重建时, 根据 Element 树从上往下依次对每个节点进行改变检测(对比 Widget 和对应的 RenderObject), 流程如下:

  • 从根节点开始,

  • 若对应位置节点 Widget 类型发生改变, 则将三棵树中对应位置的对象均移除掉, 然后替换为新的(Widget, Element, RenderObject).

  • 若 Widget 类型没有改变, 则只是更新 RenderObject 的配置(避免新建), 然后请求它进行重绘, 从而更新显示.

  • 没有改变的节点不会进行任何操作.

  • 往后遍历继续执行上述两条规则,

具体的还需要看源码以完全确定.