1.5 原理浅析——3棵重要的树
在学习Flutter前,作为理论基础,我们还需要理解一些会经常提及的概念,本节就揭秘Flutter框架层中最核心的概念。了解HTML的读者一定听说过“DOM树”这个概念,它由页面中一个个的标签构成,这些标签所形成的一种天然的嵌套关系使它们可以表示为“树”状结构。例如,下面这一段HTML代码就可以使用图1.15所示的HTML DOM树的结构来表示。
图1.15 HTML DOM树的结构
<html>
<body>
<h1>...</h1>
<p>...</p>
<div>
<a>...</a>
<p>...</p>
</div>
</body>
</html>
Flutter中虽然没有HTML、XML这类配置语言,但DOM树同样可以应用在Flutter中。例如,在计数器应用中很多组件可以通过child属性设置它们的子组件,因此我们可以用一棵树(见图1.16)来表示计数器应用的整体结构。
图1.16 计数器应用的组件树
和HTML中的标签不同,Flutter中的这棵树由一个个组件组成,因此我们可以也将它称为组件树(widget tree),它就表示在Dart代码中所写的一个个组件所组成的结构。然而,前面提到,应用运行后,组件渲染的任务并不在组件层完成,而在渲染层完成,从这里我们可以简单地推断出一个结论,这棵组件树并不是真正意义上展示在手机屏幕上的各个组件。
Flutter官方文档中组件的定义是不可变的UI描述信息。这意味着组件在创建后将不能再改变,当我们想要更新页面的状态时,也无法主动改变页面信息,因此,为了解决这类问题,Flutter又引入了元素树和RenderObject树。
元素树与组件树相对应,它由一个个元素(element)构成。大部分情况下,其实我们可以把元素理解为展示在屏幕上的真正UI组件,它会根据我们在代码中配置的组件和属性生成。因此,应用开发者可以在代码中创建的组件仅仅作为Flutter创建的元素的配置信息。当应用运行并调用build()方法后,Flutter就会根据这些配置信息生成一个个与组件对应的元素实例,在这个过程中创建了元素树。
创建组件树和元素树有很多值得我们深思的益处,我们也会在之后的章节中持续地关注这个话题。
这里举一个形象的例子来帮助读者更深刻地理解组件和元素的含义。类似于公司的总经理,组件的任务就是把近期的战略部署(即配置信息)写在纸上并下发给经理人——元素,元素看到详细的配置信息就开始干活。我们还需要注意一点,总经理随时会改变战略部署,而由于组件的不可变性,它并不会在原有的纸上修改,而只能拿一张新的白纸并重新写下配置信息。这时,经理人——元素为了减少工作量需要将新的计划与旧的计划仔细比较,再采取相应的更新措施。这就是Flutter框架层在此基础上做的一部分优化操作。问题又来了,元素作为经理人很体面。当然,元素不会把活全干完,于是又找了一个叫作RenderObject的员工来帮它做粗重的工作。
RenderObject在Flutter当中负责页面中组件的绘制和布局,其中会涉及的布局约束和绘制等技术会在第4章继续深究。同时,由RenderObject组成对应的RenderObject树(也称为渲染树)。最后,如果我们运行如下这段带有Center和Text两个组件的代码,最终Flutter内部就会生成图1.17所示的3棵树,并最终显示在手机屏幕中。
Center(
child: Text('MeandNi'),
)
图1.17 Flutter中的3棵树