CSS 布局原理之盒子模型

前言 · 浏览器渲染过程

下图是浏览器渲染过程中一些关键步骤:

图片来自:developers.google.com/web/fundame…

当浏览器在解析完毕 HTML 和 CSS 之后,会将生成的 DOM 树和 CSSOM 树合并成 Render 树,如下图:

图片来自:www.html5rocks.com/zh/tutorial…

在上图两棵树合并(Attachment)的过程中,DOM 树中某一些节点不会出现在 Render 树上(如 display: none)的元素,某一些不存在于 DOM 树的节点可能会出现在 Render 树上(如 <li> 的 marker 等伪元素等)。

组成 Render 树的基本单元是 Render Object,而这个 Render Object 就是本次要介绍的 CSS box。

当浏览器拿到这个 Render 树之后,会根据 Render 树来做后续的 布局 和 绘图 等流程。

以上描述的是在浏览器渲染的场景下,在更通用的场景下:

CSS 拿到一棵 tree of element(这棵树可以包含各种 (html) element,文字节点等),然后将其渲染到屏幕(canvas),纸(打印),以及屏幕阅读器(无障碍设备)等。一般场景下,这一棵 tree of element ****就是平时所说的 DOM 树。

为了实现最终的渲染(因为一般不能从 tree of element 一步到位渲染),CSS 会生成一个中间的树来表示准备要渲染的元素,这棵树称为 box tree。webkit 场景下,就是上文提到的 Render Tree(下文会统一称为 box tree)。

box tree 的基本组成单元是 boxtext run,这个 box 就是本文要讨论的主角(text run 此处暂不介绍,只需要知道它是代表着文本节点即可)。

盒模型简介

先来看一个例子:

WechatIMG393.jpeg

上面的例子代表了一个常规的 CSS box,其包含一个 content area,其周围依次被 padding areaborder areamargin area 包围。

这四个区域分别有四个边界(edge):

他们共同划分了下面几个区域:

  • content box -> 由 content edge 或者 inner edge 划分
  • padding box -> 由 padding edge 划分
  • border box -> 由 border edge 划分
  • margin box -> 由 margin edge 或者 outer edge 划分

这几个属性在 box-sizingbackground-clip中都有相应的使用,如上方的例子便使用了 content-box 当成背景颜色所在的区域。

Box Tree 生成细节

在生成 box tree 的时候,CSS 首先会使用层叠和继承等特性,给 element tree 的每一个 CSS 属性都计算出来一个 computed value。

然后 CSS 会根据每一个元素(element)的布局规则(一般是根据 display 属性)来生成一个或者多个 box。一般而言,每一个元素都会有一个 principal box(主要盒子),以及 0 个或者多个子 box。

这些其他 box 将会是 principal box 的后代元素。

比如,display: list-item 会额外生成多一个 marker box,又或者平时熟悉的伪元素(如 ::after),也会生成一个额外的 box(这里是否可以理解,为什么伪元素没有在 DOM 出现,但是却可以设置宽高,这是因为它是一个 box,处于 box tree 中)。

也有一些属性会使得这个元素不生成 box,比如 display: none 的元素及其子元素都不会在 box tree 生成 box,display: contents 的元素不会生成 box,但是其子元素会生成 box(这里是否可以理解为什么有的元素在 DOM 树上有,但是在 box tree 上没有的情况)。

一般情况下,元素的样式(包含继承样式)会应用到该元素的 principal box ****上,一般继承元素也会被该元素生成的其他 box 继承,非继承属性会取当前 box 的默认值。

但是也有一些特殊情况,有时候一些属性也会被应用到一个非 principal box ****上,例如:

display: table 会生成一个 table wrapper box (principal box) 和 table grid box,但是 border 属性是应用到 table grid box 上,而不是 table wrapper box。

如果调用类似 getComputedStyle 类似方法,那么这个元素会反射(Reflect)这些 box 的样式属性。

同样,对于文本节点(text node),会生成一个 text run 来包含这些连续的文本节点,如果里面没有包含文本节点,那么便不会生成 text run。(text run 可以看成一种 inline 的布局,属于 run-in 布局的一种)。

anonymous box(匿名盒子)是指一些自动生成的 box,并没有相应的 element 与之关联。anonymous box 一般是在特定的环境中生成的,一般是为了满足布局需要的 box tree 嵌套层级,而这个嵌套层级并没有在 element tree 中定义。

anonymous box 一般会从 box tree 上继承样式(如 color)。值得注意的是,与其相对的 element-generated boxes 将会严格按照 element tree 来进行继承。

比如:

上方 div 盒子生成一个 DIV box,包含了 p 元素生成的 P box 以及一个匿名盒子(anonymous box),这个 anonymous box 又包含里面的 text-run box。

稍微插播一个小知识(后面的分享会有介绍),为什么要这样嵌套盒子:

因为一个 div 生成的盒子,要不里面都是 block-level box,要不里面都是 inline-leve box,一般不存在 inline-level box 和 block-level box 同时存在的场景。

要使得当前结构满足上述层级,那么必须给 inline-level box 再嵌套一个 block-level box

另外,有一些布局场景下可能会出现 fragmentation,当出现 fragmentation 的时候,比如在 多列布局(multiple-column)中,或者打印的时候出现分页时候,一个盒子会横跨两个区域,这个时候就会造成 fragmentation,这时候的盒子由一个或者多个 fragments 来组成。而每一个 fragments 又各自拥有自己的 content-box、padding-box 等。

References

  1. CSS Box Model Module Level 3
  1. CSS Display Module Level 3
  1. 浏览器的工作原理:新式网络浏览器幕后揭秘
  1. Rendering Performance

猜你喜欢

转载自juejin.im/post/7107191147549687844