• 豌豆资源网
  • 开引网企业服务
  • 服务外包网
  • CSS 解析原理_你知道浏览器CSS是如何解析吗?

    作为前端,我们每天都在与css打交道,那么css的原理是什么呢? 

     

    一、浏览器渲染

    开篇,我们还是不厌其烦的回顾一下浏览器的渲染过程,先上图:

    正如上图所展示的,我们浏览器渲染过程分为了两条主线:
    其一,html Parser 生成的 DOM 树;
    其二,CSS Parser 生成的 Style Rules ;

    在这之后,DOM 树与 Style Rules 会生成一个新的对象,也就是我们常说的 Render Tree 渲染树,结合 Layout 绘制在屏幕上,从而展现出来。 

     

    二、Webkit CSS 解析器

    浏览器 CSS 模块负责 CSS 脚本解析,并为每个 Element 计算出样式。CSS 模块虽小,但是计算量大,设计不好往往成为浏览器性能的瓶颈。

    CSS 模块在实现上有几个特点:CSS 对象众多(颗粒小而多),计算频繁(为每个 Element 计算样式)。这些特性决定了 webkit 在实现 CSS 引擎上采取的设计,算法。如何高效的计算样式是浏览器内核的重点也是难点。

     

    Webkit 使用了自动代码生成工具生成了相应的代码,也就是说词法分析和语法分析这部分代码是自动生成的,而 Webkit 中实现的 CallBack 函数就是在 CSSParser 中。

    CSS 的一些解析功能的入口也在此处,它们会调用 lex , parse 等生成代码。相对的,生成代码中需要的 CallBack 也需要在这里实现。

    举例来说,现在我们来看其中一个回调函数的实现,createStyleRule(),该函数将在一般性的规则需要被建立的时候调用。

    解析器达到某条件需要创建一个 CSSStyleRule 的时候将调用该函数,该函数的功能是创建一个 CSSStyleRule ,并将其添加已解析的样式对象列表 m_parsedStyleObjects 中去,这里的对象就是指的 Rule 。

    那么如此一来,经过这样一番解析后,作为输入的样式表中的所有 Style Rule 将被转化为 Webkit 的内部模型对象 CSSStyleRule 对象,存储在 m_parsedStyleObjects 中,它是一个 Vector。

    但是我们解析所要的结果是什么?

    1.通过调用 CSSStyleSheet 的 parseString 函数,将上述 CSS 解析过程启动,解析完一遍后,把 Rule 都存储在对应的 CSSStyleSheet 对象中;

    2.由于目前规则依然是不易于处理的,还需要将之转换成 CSSRuleSet。也就是将所有的纯样式规则存储在对应的集合当中,这种集合的抽象就是 CSSRuleSet;

     

    3.CSSRuleSet 提供了一个 addRulesFromSheet 方法,能将 CSSStyleSheet 中的 rule 转换为 CSSRuleSet 中的 rule ;

    4.基于这些个 CSSRuleSet 来决定每个页面中的元素的样式;

     

    三、CSS 选择器解析顺序

    可能很多同学都知道排版引擎解析 CSS 选择器时是从右往左解析,这是为什么呢?

    1.html 经过解析生成 DOM Tree(这个我们比较熟悉);而在 CSS 解析完毕后,需要将解析的结果与 DOM Tree 的内容一起进行分析建立一棵 Render Tree,最终用来进行绘图。Render Tree 中的元素(WebKit 中称为「renderers」,Firefox 下为「frames」)与 DOM 元素相对应,但非一一对应:一个 DOM 元素可能会对应多个 renderer,如文本折行后,不同的「行」会成为 render tree 种不同的 renderer。也有的 DOM 元素被 Render Tree 完全无视,比如 display:none 的元素。

    2.在建立 Render Tree 时(WebKit 中的「Attachment」过程),浏览器就要为每个 DOM Tree 中的元素根据 CSS 的解析结果(Style Rules)来确定生成怎样的 renderer。对于每个 DOM 元素,必须在所有 Style Rules 中找到符合的 selector 并将对应的规则进行合并。选择器的「解析」实际是在这里执行的,在遍历 DOM Tree 时,从 Style Rules 中去寻找对应的 selector。

     

    3.因为所有样式规则可能数量很大,而且绝大多数不会匹配到当前的 DOM 元素(因为数量很大所以一般会建立规则索引树),所以有一个快速的方法来判断「这个 selector 不匹配当前元素」就是极其重要的。

    4.如果正向解析,例如「div div p em」,我们首先就要检查当前元素到 html 的整条路径,找到最上层的 div,再往下找,如果遇到不匹配就必须回到最上层那个 div,往下再去匹配选择器中的第一个 div,回溯若干次才能确定匹配与否,效率很低。

    如果换个思路,我们一开始过滤出跟目标节点最符合的集合出来,再在这个集合进行搜索,大大降低了搜索空间。来看看从右到左来解析选择器:

    1.首先就查找到 的元素;

    2.紧接着我们判断这些节点中的前兄弟节点是否符合 P 这个规则,这样就又减少了集合的元素,只有符合当前的子规则才会匹配再上一条子规则。

    试想一下,如果采用从左至右的方式读取 CSS 规则,那么大多数规则读到最后(最右)才会发现是不匹配的,这样会做费时耗能,最后有很多都是无用的;而如果采取从右向左的方式,那么只要发现最右边选择器不匹配,就可以直接舍弃了,避免了许多无效匹配。

    豌豆资源搜索网站 https://55wd.com 广州vi设计公司http://www.maiqicn.com

    四、CSS 语法解析过程

    1.先创建 CSSStyleSheet 对象。将 CSSStyleSheet 对象的指针存储到 CSSParser 对象中。

    2.CSSParser 识别出一个 simple-selector ,形如 “div” 或者 “.class”。创建一个 CSSParserSelector 对象。

    3.CSSParser 识别出一个关系符和另一个 simple-selecotr ,那么修改之前创建的 simple-selecotr, 创建组合关系符。

    4.循环第3步直至碰到逗号或者左大括号。

    5.如果碰到逗号,那么取出 CSSParser 的 reuse vector,然后将堆栈尾部的 CSSParserSelector 对象弹出存入 Vecotr 中,最后跳转至第2步。如果碰到左大括号,那么跳转至第6步。

    6.识别属性名称,将属性名称的 hash 值压入解释器堆栈。

    7.识别属性值,创建 CSSParserValue 对象,并将 CSSParserValue 对象存入解释器堆栈。

    8.将属性名称和属性值弹出栈,创建 CSSProperty 对象。并将 CSSProperty 对象存入 CSSParser 成员变量m_parsedProperties 中。

    9.如果识别处属性名称,那么转至第6步。如果识别右大括号,那么转至第10步。

    10.将 reuse vector 从堆栈中弹出,并创建 CSSStyleRule 对象。CSSStyleRule 对象的选择符就是 reuse vector, 样式值就是 CSSParser 的成员变量 m_parsedProperties 。

    11.把 CSSStyleRule 添加到 CSSStyleSheet 中。

    12.清空 CSSParser 内部缓存结果。

    13.如果没有内容了,那么结束。否则跳转值第2步。

    posted @ 2020-09-18 12:34  前端一点红  阅读(791)  评论(0编辑  收藏  举报
  • 乐游资源网
  • 热爱资源网
  • 灵活用工代发薪平台
  • 企服知识
  • 355软件知识