浏览器工作原理~DOM/CSSOM

3,399 阅读5分钟

网页渲染大致流程

从网络中获取到HTML,CSS和JavaScript资源后是如何转换成屏幕上呈现的像素内容?

首先获取HTML并且开始构建文档对象模型(DOM),然后获取CSS构建CSS对象模型(CSSOM),然后将两者结合形成渲染树(Render Tree),然后浏览器根据渲染树知道了每个元素的内容和位置(Layout)。最后渲染引擎将元素绘制在屏幕上(Paint).

构建对象模型(HTML转成DOM)

浏览器渲染页面要先构建DOM和CSSOM,因此,要尽快将HTML,CSS提供给浏览器。
当我们在浏览器输入 URL 的时候,浏览器会向服务器请求资源拿到HTML等资源,然后拿到的HTML文档头部规定了浏览器按照什么样的规范来处理HTML文``件。

<!-- 按照Java thymeleaf 模板引擎的规则解析 -->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<!-- 按照xhtml 规则解析 -->
<html xmlns="http://www.w3.org/1999/xhtml"></html>

每当解析遇到标签,浏览器会生成一个令牌(Token),一开始是标签HTML的令牌 StartTag:HTML ,然后是 StartTag:head ,这一整个流程由Token生成器来完成,当Token生成器在执行这一过程的时候,另一个进程正在消耗这些Token,并将他们转化成节点对象,我们创建了html节点之后消耗下一个令牌创建了head节点,由于head的结束令牌EndTag:head标签,在endTag:html,之前说明 head是html子节点,所以最后所有的Token都消费完的时候,就生成了文档对象模型(DOM,document object model),生成的DOM树表示了HTML的内容和属性,以及各个节点之间的关系。

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
    <title>Critical Path</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
  </body>
</html>

图片来源Google

  1. 转换: 浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成各个字符。
  2. 令牌化: 浏览器将字符串转换成 W3C HTML5 标准规定的各种令牌,例如,<html><body> 以及其他尖括号内的字符串。每个令牌都具有特殊含义和一组规则。
  3. 词法分析: 发出的令牌转换成定义其属性和规则的“对象”。
  4. DOM 构建: 最后,由于 HTML 标记定义不同标记之间的关系(一些标记包含在其他标记内),创建的对象链接在一个树数据结构内,此结构也会捕获原始标记中定义的父项-子项关系:HTML 对象是 body 对象的父项,body 是 paragraph 对象的父项,依此类推。

生成CSSOM

浏览器也会根据css规范来解析css,与DOM不同的是css会向下层叠,因此也叫层叠样式表或者层叠样式规则,即子节点可能会继承父节点的一些属性,比如body中定义了字体大小16px。其他的子属性会继承这一大小。而且浏览器解析css过程是阻塞的,浏览器需要解析完所有的css才会使用css样式(和浏览器的回流重绘一样)。

CSS它只显示了我们决定在样式表中替换的样式。每个浏览器都提供一组默认样式(也称为User Agent 样式,如下图是 chrome 浏览器 h1 标签 User Agent 样式),即我们不提供任何自定义样式时所看到的样式,我们的样式只是替换这些默认样式(例如默认 IE 样式)。

chrome浏览器h1标签默认样式

形成RenderTree

从DOM树的根节点开始,去匹配对应的CSS样式,然后把CSS样式复制到对应DM节点中,作为DOM节点的属性,如果该节点是DOM根节点,就会形成RenderTree 根节点,渲染过程中遇到display:none的节点,会先不处理他和他的子节点。即不把display:none的节点,加载到RenderTree中

布局(Layout)

<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> 布局的宽度应该等于设备的宽度,如果没有可能会采用默认的宽度例如width:100% 会变成980px

通过浏览器控制台分析布局事件,如下图是某网页加载过程,可找出事件耗时比较大的过程,分页原因给予优化,优化布局和代码,尽量做到批量布局,避免出现多个布局事件。 RenderTree加载过程

绘制页面(Paint)

同上我们可以获取到,网页Paint的过程,可见下图网页主要耗时是渲染层合并的过程(Composite Layers,了解更多)

Paint过程图

最后

首先我们接收到HTML(本地或者浏览器),然后开始解析它,DOM会逐步构建,并非一次性响应。在head中如果发现css和js链接,就会发请求,为了形成RenderTree,所以会先解析CSS形成CSSOM,解析CSS文件的过程会屏蔽JS引擎,相当于给DOM上锁,防止CSS,JS同时修改的现象发生。完成CSSOM会取消屏蔽 JS引擎,然后接收JS,然后执行JS,JavaScript解析完成后,我们就可以继续构建DOM的构建。获取DOM和CSSOM后, 我们将合并二者并构建RenderTree,然后运行布局绘制网页。

Google习题

  • 浏览器优化应当讲究,先权衡再优化的发展,因此就需要用Google Devtools 具体分析。
  • 默认情况下,CSS 被视为阻塞渲染的资源,这意味着浏览器将不会渲染任何已处理的内容,直至 CSSOM 构建完毕。请务必精简您的 CSS,尽快提供它,并利用媒体类型和查询来解除对渲染的阻塞。在渲染树构建中,我们看到关键渲染路径要求我们同时具有 DOM 和 CSSOM 才能构建渲染树。这会给性能造成严重影响:HTML 和 CSS 都是阻塞渲染的资源

教程中大多数图片和文字资源来源于 Google官网