作为前端,我们每天都在与CSS打交道,那么CSS的原理是什么呢?
一、浏览器渲染
开篇,我们还是不厌其烦的回顾一下浏览器的渲染过程,先上图:
正如上图所展示的,我们浏览器渲染过程分为了两条主线:
其一,HTML Parser 生成的 DOM 树;
其二,CSS Parser 生成的 Style Rules ;
在这之后,DOM 树与 Style Rules 会生成一个新的对象,也就是我们常说的 Render Tree 渲染树,结合 Layout 绘制在屏幕上,从而展现出来。
本文的重点也就集中在第二条分支上,我们来探究一下 CSS 解析原理。
二、Webkit CSS 解析器
浏览器 CSS 模块负责 CSS 脚本解析,并为每个 Element 计算出样式。CSS 模块虽小,但是计算量大,设计不好往往成为浏览器性能的瓶颈。
CSS 模块在实现上有几个特点:CSS 对象众多(颗粒小而多),计算频繁(为每个 Element 计算样式)。这些特性决定了 webkit 在实现 CSS 引擎上采取的设计,算法。如何高效的计算样式是浏览器内核的重点也是难点。
先来看一张图:
Webkit 使用 Flex 和 Bison 解析生成器从 CSS 语法文件中自动生成解析器。
它们都是将每个 CSS 文件解析为样式表对象,每个对象包含 CSS 规则,CSS 规则对象包含选择器和声明对象,以及其他一些符合 CSS 语法的对象,下图可能会比较明了:
Webkit 使用了自动代码生成工具生成了相应的代码,也就是说词法分析
和语法分析
这部分代码是自动生成的,而 Webkit 中实现的 CallBack 函数就是在 CSSParser 中。
CSS 的一些解析功能的入口也在此处,它们会调用 lex , parse 等生成代码。相对的,生成代码中需要的 CallBack 也需要在这里实现。
举例来说,现在我们来看其中一个回调函数的实现,createStyleRule(),该函数将在一般性的规则需要被建立的时候调用,代码如下:
CSSRule* CSSParser::createStyleRule(CSSSelector* selector)
{
CSSStyleRule* rule = 0;
if (selector) {
rule = new CSSStyleRule(styleElement);
m_parsedStyleObjects.append(rule);
rule->setSelector(sinkFloatingSelector(