【青训营】深入学习 CSS

693 阅读4分钟

当我们试图查看 CSS 转换后的 AST 结构以这个为例

.black{
  width:1rem;
  height:100%;
}

这是 CSSTree 解析的 AST JSON (删除一些不必要的属性之后) 如下

{
  "type": "StyleSheet",
  "children": [
    {
      "type": "Rule",
      "prelude": {
        "type": "SelectorList",
        "children": [
          {
            "type": "Selector",
            "children": [
              {
                "type": "ClassSelector",
                "name": "black"
              }
            ]
          }
        ]
      },
      "block": {
        "type": "Block",
        "children": [
          {
            "type": "Declaration",
            "important": false,
            "property": "width",
            "value": {
              "type": "Value",
              "children": [
                {
                  "type": "Dimension",
                  "value": "1",
                  "unit": "rem"
                }
              ]
            }
          },
          {
            "type": "Declaration",
            "important": false,
            "property": "font-size",
            "value": {
              "type": "Value",
              "children": [
                {
                  "type": "Dimension",
                  "value": "10",
                  "unit": "px"
                }
              ]
            }
          },
          {
            "type": "Declaration",
            "important": false,
            "property": "height",
            "value": {
              "type": "Value",
              "children": [
                {
                  "type": "Percentage",
                  "value": "100"
                }
              ]
            }
          }
        ]
      }
    }
  ]
}

当静态的 CSS 字符串经过转换后的 AST 就可以进行计算了。从 prelude 属性中取出查询器根据其计算权重并递归进行处理为 block 属性提供上下文支持。 block 就是具体属性的内容部分了。

AST 中的关键词

  • SelectorList:他的 Children 属性记录了 CSS 选择器的类型及名字
  • property: css 的属性
  • value: 属性对应的值
  • important: 值可以为 false 或 true , !important 存在时则为 true

因此大致解析情况是在读取 DOM 树的同时解析 CSS ,从

CSS 计算过程

1.drawio (1).png

  1. 原始 DOM 树结构与样式规则

  2. filtering:经过(对应用到该页面的规则用以下条件进行筛选:选择器匹配、属性有效、符合当前 media 等)得到声明值(Declared Values,一个元素的某属性可能有0到多个声明值。如:p{font-size:16px}和p.text{font-size:1.2em})

  3. cascading:(按照来源、!important、选择器特异性、书写顺序等选出优先级最高的一个属性值)得到层叠值(Cascaded Value,在层叠过程中,赢得优先级比赛的那个值。如 1.2em)得到层叠值(Cascaded Value,在层叠过程中,赢得优先级比赛的那个值。如 1.2em)

  4. defaulting:(当层叠值为空时,使用继承或初始值)得到指定值(Specified Value,经过cascading和defaulting之后,保证指定值一定不为空)

  5. resolving:(将一些相对值或者关键字转化成绝对值。如em转为px,相对路径转为绝对路径)得到(Computed Value,一般来说是,浏览器会在不进行实际布局的情况下,所能得到的最具体的值。如60%。继承是继承的计算值)

  6. formatting:(将计算值进一步转换。如关键字、百分比等都转为绝对值)得到使用值(Used Value,进行实际布局时使用的值,不会再有相对值或关键字。如400.2px)

6.constraining:(将小数像素值转为整数)得到实际值(渲染时实际生效的值。如400px)

大体流程可以称之为,从 csstree 中过滤出与当前属性值相关的一个或多个值,经过层叠权重的比较,选出最高权重的值,如果当前值为空则使用默认或初始值,如果当前值为相对值则转为绝对值,得到当前的可计算值,再使用当前计算值进行运算,最后将结果值转为整数。

CSS 书写顺序

浏览器并不是一获取到 CSS 样式就立马开始解析,而是根据 CSS 样式的书写顺序将之按照 DOM 树的结构分布渲染样式,然后开始遍历每个树结点的 CSS 样式进行解析,此时的 CSS 样式的遍历顺序完全是按照之前的书写顺序。

在解析过程中,一旦浏览器发现某个元素的定位变化影响布局,则需要倒回去重新渲染。(详细内容参考回流重绘)

参考以下代码

width: 150px;
height: 150px;
font-size: 24px;
position: absolute;

当浏览器解析到 position 的时候突然发现该元素是绝对定位元素需要脱离文档流,而之前却是按照普通元素进行解析的,所以不得不重新渲染。

渲染引擎首先解除该元素在文档中所占位置,这就导致了该元素的占位情况发生了变化,其他元素可能会受到它回流的影响而重新排列。

我们对可以代码进行调整:

position: absolute;
width: 150px;
height: 150px;
font-size: 24px;

推荐书写顺序

1. 定位属性

position  display  float  left  top  right  bottom   overflow  clear   z-index

2. 自身属性

width  height  padding  border  margin   background

3. 文字样式

font-family   font-size   font-style   font-weight   font-varient   color

4. 文本属性

text-align   vertical-align   text-wrap   text-transform   text-indent    text-decoration   letter-spacing    word-spacing    white-space   text-overflow

5. CSS3中新增属性

当页面发生重绘时,它们会降低浏览器的渲染性能。建议最后书写

content   box-shadow   border-radius  transform