• 规则树是如何解决样式计算的难点?

    减少我们的工作
    服务器君一共花费 241.658 ms 进行了 2 次数据库查询,努力地为您提供了这个页面。
    广告很萌的

    在前面我们谈到了规则树这东西,那么规则树是如何帮助我们减少工作呢?

    结构划分

    样式上下文可分割成多个结构。这些结构体包含了特定类别(如 border 或 color)的样式信息。

    结构中的属性都是继承的或非继承的。继承属性如果未由元素定义,则继承自其父代。非继承属性(也称为“重置”属性)如果未进行定义,则使用默认值。

    规则树通过缓存整个结构(包含计算出的端值)为我们提供帮助。这一想法假定底层节点没有提供结构的定义,则可使用上层节点中的缓存结构。

    使用规则树计算样式上下文

    在计算某个特定元素的样式上下文时,我们首先计算规则树中的对应路径,或者使用现有的路径。然后我们沿此路径应用规则,在新的样式上下文中填充结构。

    我们从路径中拥有最高优先级的底层节点(通常也是最特殊的选择器)开始,并向上遍历规则树,直到结构填充完毕。如果该规则节点对于此结构没有任何规范,那么我们可以实现更好的优化:寻找路径更上层的节点,找到后指定完整的规范并指向相关节点即可。这是最好的优化方法,因为整个结构都能共享。这可以减少端值的计算量并节约内存。 

    • 如果我们找到了部分定义,就会向上遍历规则树,直到结构填充完毕。
    • 如果我们找不到结构的任何定义,那么假如该结构是“继承”类型,我们会在上下文树中指向父代的结构,这样也可以共享结构。如果是 reset 类型的结构,则会使用默认值。
    • 如果最特殊的节点确实添加了值,那么我们需要另外进行一些计算,以便将这些值转化成实际值。然后我们将结果缓存在树节点中,供子代使用。
    • 如果某个元素与其同级元素都指向同一个树节点,那么它们就可以共享整个样式上下文

    让我们来看一个例子,假设我们有如下 HTML 代码:

    <html>
      <body>
        <div class="err" id="div1">
          <p>
            this is a <span class="big"> big error </span>
            this is also a
            <span class="big"> very  big  error</span> error
          </p>
        </div>
        <div class="err" id="div2">another error</div>
      </body>
    </html>
    

    还有如下规则:

    div {margin:5px;color:black}
    .err {color:red}
    .big {margin-top:3px}
    div span {margin-bottom:4px}
    #div1 {color:blue}
    #div2 {color:green}
    

    为了简便起见,我们只需要填充两个结构:color 结构和 margin 结构。color 结构只包含一个成员(即“color”),而 margin 结构包含四条边。

    形成的规则树如下图所示(节点的标记方式为“节点名 : 指向的规则序号”):

    规则树

    上下文树如下图所示(节点名 : 指向的规则节点):

    上下文树

    假设我们解析 HTML 时遇到了第二个 <div> 标记,我们需要为此节点创建样式上下文,并填充其样式结构。

    经过规则匹配,我们发现该 <div> 的匹配规则是第 1、2 和 6 条。这意味着规则树中已有一条路径可供我们的元素使用,我们只需要再为其添加一个节点以匹配第 6 条规则(规则树中的 F 节点)。

    我们将创建样式上下文并将其放入上下文树中。新的样式上下文将指向规则树中的 F 节点。

    现在我们需要填充样式结构。首先要填充的是 margin 结构。由于最后的规则节点 (F) 并没有添加到 margin 结构,我们需要上溯规则树,直至找到在先前节点插入中计算过的缓存结构,然后使用该结构。我们会在指定 margin 规则的最上层节点(即 B 节点)上找到该结构。

    我们已经有了 color 结构的定义,因此不能使用缓存的结构。由于 color 有一个属性,我们无需上溯规则树以填充其他属性。我们将计算端值(将字符串转化为 RGB 等)并在此节点上缓存经过计算的结构。

    第二个 <span> 元素处理起来更加简单。我们将匹配规则,最终发现它和之前的 span 一样指向规则 G。由于我们找到了指向同一节点的同级,就可以共享整个样式上下文了,只需指向之前 span 的上下文即可。

    对于包含了继承自父代的规则的结构,缓存是在上下文树中进行的(事实上 color 属性是继承的,但是 Firefox 将其视为 reset 属性,并缓存到规则树上)。

    例如,如果我们在某个段落中添加 font 规则:

    p {font-family:Verdana;font size:10px;font-weight:bold}
    

    那么,该段落元素作为上下文树中的 div 的子代,就会共享与其父代相同的 font 结构(前提是该段落没有指定 font 规则)。

    在 Webkit 中没有规则树,因此会对匹配的声明遍历 4 次。首先应用非重要高优先级的属性(由于作为其他属性的依据而应首先应用的属性,例如 display),接着是高优先级重要规则,然后是普通优先级非重要规则,最后是普通优先级重要规则。这意味着多次出现的属性会根据正确的层叠顺序进行解析。最后出现的最终生效。

    前面在 浏览器构建呈现树的流程 提到了样式计算存在的两个难点,第一个是样式数据是一个超大的结构,存储了无数的样式属性,这可能造成内存问题。第二个是应用规则涉及到相当复杂的层叠规则(用于定义这些规则的层次)。共享样式对象(整个对象或者对象中的部分结构)就可以解决这两个问题了。此外,Firefox 规则树还有助于按照正确的顺序应用属性。

更多 推荐条目

Welcome to NowaMagic Academy!

现代魔法 推荐于 2013-02-27 10:23   

本章最新发布
随机专题
  1. [JavaScript程序设计] jQuery与表单操作 2 个条目
  2. [移动开发] ListView 使用相关问题集 1 个条目
  3. [软件工程与项目管理] 呈现树的构建 13 个条目
  4. [PHP程序设计] 声明式编程范式 12 个条目
  5. [PHP程序设计] fsockopen,curl与file_get_contents 12 个条目
  6. [PHP程序设计] PHP数组的遍历 7 个条目
  7. [PHP程序设计] PHP数组探索 4 个条目
  8. [智力开发与知识管理] 整体性学习策略 9 个条目
  9. [PHP程序设计] PHP里的引用 5 个条目
  10. [Python程序设计] 标准库:urllib/urllib2 14 个条目
  11. [运维管理] 路由器与交换机 4 个条目
  12. [PHP程序设计] PHP中的Hash算法 3 个条目
窗口 -- [八点]