我是 Ian Kilpatrick,是 Blink 布局团队的工程主管,跟 Koji lshii 是同事。在加入 Blink 团队之前,我是一名前端工程师(在 Google 设立独立的“前端工程师”角色之前),在 Google Docs、Google Drive 和 Gmail 团队工作。在担任该职位大约五年后,我赌了一把,转投 Blink 团队。我在 Blink 团队中高效地学习了 C++,并开始研究大规模的复杂的 Blink 代码库。即使在今天,我也只了解其中的一小部分。我很感激在这个阶段给我的时间,其实很多“前端工程师”转为“浏览器工程师”比我快。
在 Blink 团队中,我以前作为前端工程师的经验帮助了我。作为一名前端工程师,我经常遇到浏览器不一致、性能问题、渲染 bug 和缺少功能的情况。所以 LayoutNG 对我来说是个机会,我可以系统地解决 Blink 布局系统中的这些问题。LayoutNG 也代表了许多工程师多年来的共同努力。
在这篇文章中,我将解释如何通过大型架构的更改来减少和缓解各种类型的 bug 和性能问题。
布局引擎的宏观架构
以前,Blink 的布局树是“可变树”。
布局树中的每个对象都有输入信息,例如在父对象中的可用大小、浮动的位置。也有输出信息,例如对象的最终宽度和高度、x 和 y 的位置。
一次新的渲染会保留并修改这些对象。当样式发生变化时,我们将这个对象标记为脏对象,并将其所有祖先对象也都标记为脏对象。当渲染流水线的布局阶段运行时,遍历所有脏对象,然后运行布局使它们进入干净状态。
这种架构导致了许多问题,这些问题将在下面进行描述。但首先,让我们先考虑一下布局的输入和输出分别是什么。
在树中的一个节点上运行布局,输入是“样式和 DOM”,以及来自父节点布局系统(Grid、Block 或 Flex)的一些约束,然后运行布局约束算法产生输出。
我们在新架构中正式确定了这个概念模型。新架构中仍然有布局树,但主要使用布局树来保存布局的输入和输出。对于输出,会生成一个全新的不可变对象,称为片段树。
我之前介绍了不可变片段树,描述了它是如何设计的,它让我们可以在增量布局时重用布局树中的大部分节点。
同时,还存储了生成片段树的父约束对象,将其用作缓存键,我们将在下面详细讨论。
为了匹配新的不可变架构,我们也重写了内联(文本)布局算法。它为内联布局生成不可变的 flat l