浏览器渲染原理

寻技术 Html/CSS 2024年01月09日 78

渲染是指什么?

渲染 (render),是指将HTML代码转换为像素信息的过程。

当用户在浏览器上输入url之后,访问的服务器返回html文件,本质上是html代码,是字符串。渲染这个过程的任务就是:识别这段字符串,并且转换为像素信息。

渲染时间点

image-20231015172523665

用户打开网页的过程可以简单概括为:

  1. 网络:拿HTML。

    这里概括为拿HTML,是因为在HTML文件中可以通过<style>标签和<script>标签引入 CSS 和 JS 文件。

    事实上网络的过程也很复杂, 但是不是这篇笔记的重点讨论内容。

  2. 渲染:解析HTML代码并最终转换为像素信息。


浏览器有很多进程,其中有网络进程,而网络进程又包含网络线程

网络线程完成网络请求任务之后,拿到了一个html文件,但是它没有解析的能力,于是将html文件包装成一个任务,通过消息队列,转交给渲染主线程

渲染主线程拿到渲染任务之后,就开始了渲染流程,就是本篇笔记的重点内容。

渲染流程

image-20231015175452682

解析HTML - Parse HTML

image-20231015180439085

DOM树(Document Object Model):页面中的元素和文本,以树形结构相关联。

在 JS 代码中,通过document对象可以访问和修改DOM树。而上图中的DOM树指的是浏览器底层由C++生成的DOM树。

image-20231015182209370

这一个转换步骤是为后续步骤做准备的,因为字符串难处理,而对象结构容易处理。

CSS也会被解析成CSSOM(CSS Object Model),也是树形结构,根节点(StyleSheetList)是网页中所有的样式表,二级子节点可能包含内部样式表外部样式表内联样式表浏览器默认样式表(取决于代码中是否有这些内容),如果有两个<link>,则会出现两个外部样式表节点。

可以在github上的chromium源码找到浏览器默认样式表的内容。

除了浏览器默认样式表内部样式表外部样式表内联样式表都可以通过 JS 访问到。

  • 内部样式表和外部样式表:使用document.styleSheets可以访问到一个数组,元素是样式表对象。
    • 使用document.styleSheets[0].addRule("div", "border: 1px solid red important")可以让页面上的所有div标签的边框变成红色,这种做法与传统的“获取所有div标签,再设置其style”的做法不同。
  • 内联样式表:使用dom.style访问

image-20231015195021172

HTML解析过程遇到CSS怎么办?

为了提高解析效率,浏览器会启动一个预解析器率先下载和解析CSS。

渲染主线程在解析HTML的时候,会关注每一个标签;而预解析线程只关注外部样式表的标签<link>,尽快地完成CSS的下载与解析。

这样做的目的是防止CSS的解析阻塞了HTML的解析。

image-20231015215414216

HTML解析过程遇到JS怎么办?

渲染主线程遇到 JS 的script标签时必须暂停一切行为,等待下载 JS 文件,并且启用V8引擎解析执行 JS 代码,然后才能继续解析 HTML。

原因:JS 代码可能修改 DOM 树。

预解析线程可以分担一点下载 JS 的任务。

image-20231015222008522


样式计算 - Recalculate Style

image-20231015230406588

样式计算过程计算每一个DOM节点的最终样式(Computed Style)。

计算样式如何查看:在浏览器上打开开发者工具,查看“计算样式”,并选择“全部显示”。

image-20231016165419278

通过上一过程,得到的 DOM 树和 CSSOM 树。通过遍历 DOM 树,为每一个 DOM 节点,计算它的所有 CSS 属性。

属性值的计算过程,分为如下4个步骤:

  1. 确定声明值;
  2. 层叠冲突;
  3. 使用继承;
  4. 使用默认值。

确定声明值

如果先不考虑冲突的话,那么通过 页面作者书写的CSS样式用户代理样式表(浏览器内置的样式表) 的声明值相加得到全部的声明值,并且将部分值进行转换。

例如,将color: red;转换为color: rgb(255, 0, 0);,将font-size: 2em;转换为font-size: 14px;

层叠冲突

在确定声明值时,可能出现一种情况,那就是声明的样式规则发生了冲突。

此时会进入解决层叠冲突的流程。而这一步又可以细分为下面这三个步骤:

  • 比较源的重要性
  • 比较优先级
  • 比较次序
比较源的重要性

样式有三种来源:

  1. 浏览器会有一个基本的样式表来给任何网页设置默认样式。这些样式统称用户代理样式
  2. 网页的作者可以定义文档的样式,这是最常见的样式表,称之为页面作者样式
  3. 浏览器的用户,可以使用自定义样式表定制使用体验,称之为用户样式

对应的重要性顺序依次为:页面作者样式 > 用户样式 > 用户代理样式。

可以在 MDN 中找到更详细的说明:CSS 层叠 - CSS:层叠样式表 | MDN (mozilla.org)

比较优先级

如果在同一源中出现了样式声明冲突,则比较其优先级。

简单来说就是:ID选择器 > 类名选择器 > 标签选择器。

更详细的说明可以查阅 MDN 的文章:优先级 - CSS:层叠样式表 | MDN (mozilla.org)

比较次序

如果出现同源同权重的情况,则比较样式的声明次序。

后声明的样式会覆盖先声明的样式。

p{
    /* 会被覆盖 */
    color: red;
}

p{
    /* 生效 */
	color: green;
}

显然,不存在次序相同的情况。至此,样式声明中存在冲突的所有情况都解决了。

使用继承

上文提到了,对于每一个 DOM 节点,都会去计算它的所有 CSS 属性。

层叠冲突这一步骤完成之后,声明值已全部确定。

而对于未声明的属性,并不是直接使用默认值,而是使用继承值。

例如:

<div>
	<p>hello world</p>
</div>
div{
	color: red;
}

这里<p>标签会继承来自<div>color: red样式。

继承原则

  • 继承谁的?答:就近原则,谁近就继承谁的,与权重无关。

  • 哪些属性能够继承?答:大部分字体相关的属性都是可继承的,可以在MDN上查找属性是否可继承。

    image-20231018135903596

使用默认值

如果经过上述过程仍不能确定属性值,则使用默认值。


布局 - Layout

image-20231019160522446

根据 DOM 树里每个节点的样式,计算出每个节点的尺寸和位置

有一些数值,例如:百分比,或者auto,在上一步骤无法算出来,在布局这个过程才能算出来。

对于一个元素来说,它的尺寸和位置经常与它的包含块(containing block)有关。

这里简单地记录包含块的知识,更详细的说明可以查阅 MDN 文档:

关闭

用微信“扫一扫”