开始岁岁年为什么要翻译这篇文章,一开始我只是好奇,基本上我知道Firefox在做前端之后是用Rust语言开发一个新的浏览器引擎技术圈,作为一名前端开发者,我对Firefox的感情还是很多的(虽然现在已经离不开chrome了),但我还是希望Firefox能够再次引领Web的革命。可以说前端这几年解决了很多前端工程的痛点,但是性能这个坎还是存在的。希望webassembly早日普及,但对前端来说肯定是一场血战,前端也不会像现在这样永远是前端。既然webassembly出现了,那css呢?没听说有什么新技术可以替代它(虽然它确实不适合现代前端),所以只能开发新的引擎来提高性能。这就是Firefox的量子引擎:QuantumCSS(也称为Stylo)。加速器这是Firefox正在开发的Quantum项目。目的当然是为了让浏览器更快。从上图中可以看到每个模块,中间是QuantumCSS,这和它在整个渲染过程中的位置是一样的。使用Rust可以相当有效地利用现代处理器的多核特性,并且可以将速度提高数倍。既然如此强大,那么在哪里可以体验呢:在FirefoxNightly版中输入about:config,将属性layout.css.servo.enabled设置为true,就可以体验这个神奇的引擎了。站在巨人的肩膀上,当然除了利用现代处理器的并行能力外,还要借鉴各种浏览器积累的一些优化技术。接下来,我们将一一分析这些优化技术,以及如何让引擎更快。CSS引擎是做什么的CSS引擎是浏览器渲染引擎的一部分,渲染引擎将我们的HTML和CSS转换成屏幕上的像素(也就是图片)。每个浏览器都会有自己的渲染引擎,比如Google的Blink、Edge的EdgeHTML、Safari的Webkit、Firefox的Gecko。尽管有这么多引擎,但它们都做同样的事情:将HTML和CSS渲染到我们的Sensible界面中。而他们内部的工作需求:首先解析HTML文档,生成DOM节点树,让浏览器知道页面的结构和各个节点之间的关系。那么就要搞清楚每个元素长什么样子,是圆的还是方的,有没有边框等等;所以这个时候,你需要知道每个元素需要使用什么样式。除了知道它长什么样子,你还需要知道每个节点的位置和布局。引擎会为所有可见的节点创建一个盒子,但盒子并不是只为DOM节点创建的,DOM节点内部还可以有其他的盒子,比如几行文本。了解了应用程序的样式和位置,现在是绘制框的时候了,这可能发生在多个图层上。它可以被认为是一种古老的动画技术,在一张透明胶片上绘制背景,然后在另一张胶片上绘制人物或其他元素。这样如果人物会动,只需要重画人物的片子,其他的就不用画了,省了很多麻烦。此时既然我们有了不同的影片(层),就该开始合成(composite)了,不过在合成之前,我们可能还会应用一些transforms,rotations,offsets等等,最后按照层级叠加在一起,背景在后,人物在前等等,最后渲染到屏幕上。综合之后我们可以知道,CSS引擎在开始计算样式的时候需要两个东西:DOM的节点树和一系列的样式规则。CSS引擎会遍历所有的DOM节点并计算应用于每个节点的样式。它将使DOM节点的每个CSS属性都有一个值。即使你没有在样式表中声明,它也可能来自继承或默认值,或者客户端的样式表(UserAgentStyle)。可以认为引擎就像填表一样,将这些最后计算出来的值一个一个填入。为了得到上表,CSS引擎需要做两件事:弄清楚应用于每个节点的样式规则(选择器匹配)添加一些你没有声明的值,根据继承或默认值(thecascade)Selectormatchingfirst找出配置当前节点的样式规则,并将它们放在一个列表中,其中还包括客户端的样式表。然后计算各个样式规则之间的权重并根据权重排序。根据权重的大小,得到最终要应用的style属性的值。级联(Thecascade)的目的是让CSS更容易编写和维护。由于级联的存在,可以在body上设置color属性,li、p、span等元素可以直接使用同一种颜色。每个元素都需要定义一次。为了实现这个功能,CSS引擎会从表中寻找一些还为空的属性值。如果属性默认是继承的,CSS引擎会从父节点继承属性值。如果所有父节点都没有定义属性值,则使用默认值。现在我们的表都是以上表的形式填充stylestructsharing,这只是一种表达方式,真正的引擎内部并不是这样的。CSS有上百个属性,如果引擎为每个节点生成这样一个表,它会很快耗尽所有内存。相反,引擎内部通常使用stylestructsharing,将style数据集中在不同的对象(stylestruct)中,然后用指针指向这些对象。这样会在很大程度上节省内存,因为每个节点很可能有相似的属性值(比如兄弟节点之间),并且因为很多属性也是通过继承获得的,所以父节点可以与子节点共享这些属性价值。如何让这些工作更快如果我们不优化这些工作,整个样式计算工作将是这样的:这是一个巨大的工作量,而且不仅是在页面加载时发生,它会一直发生与用户交互的时间存在(例如悬停一个元素,CSS引擎需要重新计算样式)。这意味着必须优化样式的计算。在过去的20年里,测试了不同的优化策略,而QuantumCSS就是这些最优优化策略的组合。全部并行运行我们现在的CPU大多都是多核的,而QuantumCSS会将不同DOM节点的样式计算工作分散到不同的核上,但是实现起来也相当困难。原因之一是DOM节点树不是并行的。不一定均匀,这会导致某些内核的工作负载大于其他内核。为了让每个核心的工作量更加合理,QuantumCSS使用了一种叫做工作窃取的技术。当一个DOM节点被处理时,引擎可以将它的子节点计算工作分成若干个“工作单元”放入队列中。中间。当其中一个核清除自己队列的工作时,它可以在其他队列上找到其他工作单元并执行它们,这意味着我们不需要提前分配工作,在运行时将达到最高的工作效率.在大多数浏览器中,很难让这种机制不出错地运行,而且CSS引擎本身非常复杂,它处于渲染引擎中两个最复杂的模块(DOM和布局)的中间。这个过程很容易出现bug,而并行程序带来的bug是非常难调试的。您可以通过本文了解更多信息。使用规则树加速重新样式化对于每个DOM节点,CSS引擎需要遍历所有样式规则来执行选择器匹配,并且对于大多数节点来说,这种匹配不会经常发生变化。例如,当用户悬停在父节点上时,它的样式规则可能会发生变化,但我们仍然需要重新设置子节点的样式规则来处理继承的属性值,而子节点的匹配规则可能不会发生变化。如果我们记录子节点匹配的样式规则而不必每次都进行选择器匹配,则可以大大优化这一点。这就是所谓的规则树,Firefox之前的引擎就是这么做的。CSS引擎会遍历样式规则帮助DOM节点找到匹配的选择器,然后根据权重排序创建一个样式规则链表,然后将这个链表添加到规则树中。CSS引擎会尽量使用已有的分支,以保证规则树的分支数量最少。如果链表中的大部分选择器与已有的分支相同,则引擎会沿着这条路径走,除非到达一个节点,规则树中不存在相同的分支,引擎会添加一个新的分支。在重新计算样式的过程中,引擎会快速检查父节点的变化是否会引起子节点匹配的样式规则的变化。如果不是,则子节点可以根据自己指向规则树节点的指针计算样式。引擎会查找规则树的节点,得到整个匹配规则,从最大权重到最小权重。这样就可以轻松跳过选择器匹配的步骤。但是还有很多工作要做。毕竟一个页面有几万个节点。这时候,并行计算的魔力又可以大显身手了。使用样式共享缓存加速初始渲染(和级联)由于整个页面中可能有数万个节点,其中许多节点匹配相同的规则。例如,wiki页面中的每个p元素实际上都匹配相同的样式规则并具有相同的计算样式。如果这里没有优化,每个段落可能都要重新计算,但是如果有办法证明每个段落的样式规则是一样的,引擎只需要计算一次就可以了。这就是所谓的样式共享缓存,是Chrome和Safari发明的一种优化方法。引擎处理完一个节点后,会将计算出的样式放入缓存中,然后开始计算下一个节点,引擎会先检查缓存中计算出的值是否已经存在。而这些检查包括:两个节点是否有相同的ID和Class等,是否匹配相同的规则。对于非选择器内联样式,它们是否具有相同的值。它们的父节点是否指向同一个计算样式对象,如果是的话,它们继承的值将是相同的。但是还有许多其他情况会导致这些检查失败。例如,如果一个CSS规则使用了:first-child选择器,即使两个节点已经满足上述规则,结果仍然会检查失败。在Webkit和Blink中,样式共享缓存在这些情况下放弃了检查,不使用缓存。由于大多数网站都使用这些现代选择器(CSS3),这种优化变得越来越没有用,因此Blink团队最近将其删除。在QuantumCSS,我们收集了所有这些奇怪的选择器(CSS3)并使它们可检查。我们将结果存储为0和1,如果两个元素具有相同的0和1,那么我们知道它们匹配相同的样式规则。这样我们就可以继续享受样式共享缓存带来的优化了。结尾的前半部分可以让我们了解CSS引擎的工作内容,后半部分让我们了解新引擎是如何优化性能的。我真的学到了很多东西,我想你也一样。
