深入Flutter(三) Element树和RenderObject树

文章系列
深入Flutter(一) 积极推进“组合”方式
深入Flutter(二) 线性时间复杂度的 build 和 layout
深入Flutter(四) Infinite scrolling – 无限滚动
深入Flutter(五) 专业化API

原文:Inside Flutter

译者额外添加flutter的渲染流程图,有助于理解本文:
图一、
在这里插入图片描述
图二、
在这里插入图片描述

Tree surgery – 树surgery(遍历和局部重建)

Reusing elements is important for performance because elements own two critical pieces of data: the state for stateful widgets and the underlying render objects. When the framework is able to reuse an element, the state for that logical part of the user interface is preserved and the layout information computed previously can be reused, often avoiding entire subtree walks. In fact, reusing elements is so valuable that Flutter supports non-local tree mutations that preserve state and layout information.
重复使用element是性能优化中的重要项,因为element拥有两个关键的数据: stateful widget对应的state渲染树的renderObject。 当framework能够重复使用element时,将保留state的用户界面的逻辑成分,并且可以重复使用先前计算的layout信息,可经常性的避免整个子树遍历。 实际上,重复使用element非常有价值,以至于Flutter能够支持non-local树变化的同时,保留state和layout信息。

Developers can perform a non-local tree mutation by associating a GlobalKey with one of their widgets. Each global key is unique throughout the entire application and is registered with a thread-specific hash table. During the build phase, the developer can move a widget with a global key to an arbitrary location in the element tree. Rather than building a fresh element at that location, the framework will check the hash table and reparent the existing element from its previous location to its new location, preserving the entire subtree.
开发人员可以这样执行non-local树变化:把GlobalKey与其中一个widget相关联。 每个GlobalKey在整个app中都是唯一的,并向特定线程的哈希表注册。 在build阶段,开发人员可以将带有GlobalKey的widget移动到element树中的任意位置。 framework会检查哈希表,并将现有element从其先前位置重新定位到新位置,而不是在该位置构建一个新的element,从而保留整个子element树。

The render objects in the reparented subtree are able to preserve their layout information because the layout constraints are the only information that flows from parent to child in the render tree. The new parent is marked dirty for layout because its child list has changed, but if the new parent passes the child the same layout constraints the child received from its old parent, the child can return immediately from layout, cutting off the walk.
父级的子树中的渲染对象能够保留其layout信息,因为layout的constraint是渲染树中从父对象流到子对象的唯一信息。 新的父级由于其子级列表已更改而被标记为 layout dirty,但是如果新的父级传递给子级的layout constraint,和子级从旧父级接收到的layout constraint是同一个,则该子级可以立即从中return,无需继续往下走。

Global keys and non-local tree mutations are used extensively by developers to achieve effects such as hero transitions and navigation.
开发人员广泛使用GlobalKeynon-local树变化来实现各种效果,例如 hero transition和navigation。

Constant-factor optimizations --常数因子优化

In addition to these algorithmic optimizations, achieving aggressive composability also relies on several important constant-factor optimizations. These optimizations are most important at the leaves of the major algorithms discussed above.
除了这些算法优化之外,实现激进的可组合能力还依赖于几个重要的常数因子优化。 这些优化是前文和上文讨论的主要算法的最重要部分。

  1. Child-model agnostic. Unlike most toolkits, which use child lists, Flutter’s render tree does not commit to a specific child model. For example, the RenderBox class has an abstract visitChildren() method rather than a concrete firstChild and nextSibling interface. Many subclasses support only a single child, held directly as a member variable, rather than a list of children. For example, RenderPadding supports only a single child and, as a result, has a simpler layout method that takes less time to execute.
    子模型不可知。与使用子列表的大多数工具包不同,Flutter的渲染树不会commit特定的子模型。 例如,RenderBox类具有抽象的visitChildren()方法,而不是具体的firstChild和nextSibling接口。 许多subclasse只支持一个child,直接持有作为成员变量,而不是支持child list。 例如,RenderPadding仅支持一个child,因此,它的layout方法更简单,执行时间更少。

  2. Visual render tree, logical widget tree. In Flutter, the render tree operates in a device-independent, visual coordinate system, which means smaller values in the x coordinate are always towards the left, even if the current reading direction is right-to-left. The widget tree typically operates in logical coordinates, meaning with start and end values whose visual interpretation depends on the reading direction. The transformation from logical to visual coordinates is done in the handoff between the widget tree and the render tree. This approach is more efficient because layout and painting calculations in the render tree happen more often than the widget-to-render tree handoff and can avoid repeated coordinate conversions.
    **视觉渲染树,逻辑widget树。**在Flutter中,渲染树在与设备无关的视觉坐标系中运行,这意味着x坐标中的小端值始终向左,即使当前读数 方向是从右到左。 widget通常在逻辑坐标中操作,这意味着其起始值和结束值的视觉转换取决于读取方向。 从逻辑坐标到视觉坐标的转换是在widget树和渲染树之间的切换中完成的。 这种方法效率更高,因为渲染树中的layout和绘画(paint)计算比widget到渲染树的切换发生得更多,并且可以避免重复的坐标转换。

  3. Text handled by a specialized render object. The vast majority of render objects are ignorant of the complexities of text. Instead, text is handled by a specialized render object, RenderParagraph, which is a leaf in the render tree. Rather than subclassing a text-aware render object, developers incorporate text into their user interface using composition. This pattern means RenderParagraph can avoid recomputing its text layout as long as its parent supplies the same layout constraints, which is common, even during tree surgery.
    text有专属渲染对象来处理.绝大多数渲染对象都直接忽略text的复杂性。 由专用的渲染对象** RenderParagraph 处理来text,该对象是渲染树中的叶子(一个分支节点)。 开发人员不使用子类来识别text的渲染对象,而是使用合成的方式将text合并到其用户界面中。 这种模式意味着 RenderParagraph **可以避免重新计算其text的layout,只要其父级提供相同的layout constraint,这即使在树的surgery期间也是一种很常规的做法。

  4. Observable objects. Flutter uses both the model-observation and the reactive paradigms. Obviously, the reactive paradigm is dominant, but Flutter uses observable model objects for some leaf data structures. For example, Animations notify an observer list when their value changes. Flutter hands off these observable objects from the widget tree to the render tree, which observes them directly and invalidates only the appropriate stage of the pipeline when they change. For example, a change to an Animation<Color> might trigger only the paint phase rather than both the build and paint phases.
    观察者对象。 Flutter使用观察者对象模型和响应式。 显然,响应式占主导地位,但是Flutter对某些叶子数据结构使用了观察者对象模型。 例如,“Animations”值更改时会通知观察者列表。 Flutter将这些可观察对象从widget树移交给渲染树,后者直接对其进行观察,并在更改它们时仅在pipeline的特性阶段无效。 例如,对 Animation<Color>的更改可能仅触发paint阶段,而不在build阶段和paint阶段都触发。

Taken together and summed over the large trees created by aggressive composition, these optimizations have a substantial effect on performance.
综合而言,这由激进的可组合能力所生成的庞大的树,上述优化项对性能有重大影响作用。

Separation of the Element and RenderObject trees --Element树和RenderObject树的分离

The RenderObject and Element (Widget) trees in Flutter are isomorphic (strictly speaking, the RenderObject tree is a subset of the Element tree). An obvious simplification would be to combine these trees into one tree. However, in practice there are a number of benefits to having these trees be separate:
Flutter中的RenderObject和Element(Widget)树是同构的(严格来说,RenderObject树是Element树的子集)。 一个明显的简化就是将这些树合并为一棵树。 但是,实际上,将这些树分开是有很多好处的:

  1. Performance. When the layout changes, only the relevant parts of the layout tree need to be walked. Due to composition, the element tree frequently has many additional nodes that would have to be skipped.
    性能。当layout发生变化时,仅需要遍历layout树的相关部分。 由于组合结构的原因,element树经常有许多其他必须跳过的节点。

  2. Clarity. The clearer separation of concerns allows the widget protocol and the render object protocol to each be specialized to their specific needs, simplifying the API surface and thus lowering the risk of bugs and the testing burden.
    清晰。更加清晰的关注点,分离使widget协议和渲染对象协议可以各自专用于其特定需求,从而简化了API,从而降低了bug的风险和测试负担。

  3. Type safety. The render object tree can be more type safe since it can guarantee at runtime that children will be of the appropriate type (each coordinate system, e.g. has its own type of render object). Composition widgets can be agnostic about the coordinate system used during layout (for example, the same widget exposing a part of the app model could be used in both a box layout and a sliver layout), and thus in the element tree, verifying the type of render objects would require a tree walk.
    类型安全。渲染对象树可以更加安全,因为它可以在运行时保证子对象为适当的类型(例如,每个坐标系统,都有自己的渲染对象类型)。 组合widget可能与layout期间使用的坐标系无关(例如,同样暴露app一部分模型的widget可以在box的ayout和sliver 的layout中都能使用),因此在Element树中,需要遍历树,才能验证渲染对象类型 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值