阅读 1236

浏览器页面渲染机制

分享目的: 解释浏览器如何将 HTML、CSS 和 JavaScript 转换为我们可以与之交互的网站,了解这个过程,可以帮助我们优化 Web 应用程序,从而获得更快的速度和更好的性能。

问题: 浏览器如何渲染网站? (接下来会解构这个过程,但是首先,有必要了解一些基础概念)

Web 浏览器是一种软件,它从远程服务器(或者本地磁盘)加载文件并将其显示——使用户可以与之交互。浏览器中有一个软件叫浏览器引擎。在不同的浏览器中,浏览器的某个部分会根据它接收到的文件确定显示什么,这就是所谓的浏览器引擎。浏览器引擎是每一种主流浏览器的核心软件组件,不同的浏览器开发商用不同的名字来称呼他们的引擎。

1. html解析

  • 接收信息
    数据是以“数据包”的形式通过互联网发送,而数据包以字节为单位。当你编写一些 HTML、CSS 和 JS,并试图在浏览器中打开 HTML 文件时,浏览器会从你的硬盘(或网络)中读取 HTML 的原始字节。
  • 计算机接收到字节数据
    浏览器读取的是原始数据字节,而不是你编写的代码的实际字符。浏览器读取的是原始数据字节,而不是你编写的代码的实际字符。
  • 从 HTML 的原始字节到 DOM,浏览器对象需要处理的是文档对象模型(DOM)对象。那么,DOM 对象是从何而来的呢?首先,将原始数据字节转换为字符。(Bytes => haracters)
  • 从字节到字符
    这一点,你可以通过你所编写的代码的字符看到。这种转换是基于 HTML 文件的字符编码完成的。至此,浏览器已经从原始数据字节转换为文件中的实际字符。但这不是最终的结果。这些字符会被进一步解析为一些称为“标记(token)”的东西。(Bytes => haracters => Tokens···)
  • 从字符到标记
    那么,这些标记是什么?文本文件中的一堆字符对浏览器引擎而言没什么用处。如果没有这个标记化过程,那么这一堆堆字符只会生成一系列毫无意义的文本,即 HTML 代码——不会生成一个真正的网站。 当你保存一个扩展名为.html 的文件时,就向浏览器引擎发出了把文件解析为 HTML 文档的信号。浏览器“解释”这个文件的方式是首先解析它。在解析过程中,特别是在标记化过程中,浏览器会解析 HTML 文件中的每个开始和结束“标签(tag)”。解析器可以识别尖括号中的每个字符串,如“< html>”、“< p>”

但标记还不是最终的结果。标记化完成后,接下来,标记将被转换为节点。你可以将节点看作是具有特定属性的不同对象。实际上,更好的解释是,将节点看作是文档对象树中的独立实体。但节点仍然不是最终结果。 现在,让我们看一下最后一点。在创建好之后,这些节点将被链接到称为DOM 的树数据结构中。DOM 建立起了父子关系、相邻兄弟关系等。在这个 DOM 对象中,每个节点之间都建立了关系。现在,这是我们可以使用的东西了。

  • 标记
    但标记还不是最终的结果。标记化完成后,接下来,标记将被转换为节点。你可以将节点看作是具有特定属性的不同对象。实际上,更好的解释是,将节点看作是文档对象树中的独立实体。但节点仍然不是最终结果。
  • DOM (Bytes => haracters => Tokens => Node => DOM) 让我们看一下最后一点。在创建好之后,这些节点将被链接到称为 DOM 的树数据结构中。DOM 建立起了父子关系、相邻兄弟关系等。在这个 DOM 对象中,每个节点之间都建立了关系。这个时候,是浏览器需要的东西了。

2.css解析

这个是我们很常见的写法

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" type="text/css"  href="test.css" />
</head>
<body>

</body>
</html>
复制代码

当浏览器接收到原始数据字节并启动 DOM 构建过程时,它还会发出请求来获取链接的 test.css 样式表。当浏览器开始解析 HTML 时,在找到 css 文件的链接标签的同时,它会发出请求来获取它。可能你已经猜到,浏览器还是接收 CSS 数据的原始字节,从互联网或是本地磁盘。

浏览器如何处理这些 CSS 数据的原始字节?

当浏览器接收到 CSS 的原始字节时,会启动一个和处理 HTML 原始字节类似的过程。就是说,原始数据字节被转换成字符,然后标记,然后形成节点,最后形成树结构。 什么是树结构?大多数人都知道 DOM 这个词。同样,也有一种 CSS 树结构,,浏览器不能使用 HTML 或 CSS 的原始字节。必须将其转换成它能识别的形式,也就是这些树形结构。

DOM + CSSOM = 渲染树

渲染树包含页面上所有关于可见 DOM 内容的信息以及不同节点所需的所有 CSSOM 信息。注意,如果一个元素被 CSS 隐藏,例如使用 display; none,那么节点就不会包含在渲染树中。隐藏元素会出现在 DOM 中,但不会出现在渲染树中。这是因为渲染树结合了来自 DOM 和 CSSOM 的信息,所以它知道不能把隐藏元素包含在树中。

元素展示

我们已经得到了在屏幕上显示元素所需的所有信息。我们只要把它展示给用户。这就是这个阶段的全部工作。有了元素内容(DOM)、样式(CSSOM)和计算得出的元素的精确布局信息,浏览器现在就可以将节点逐个“绘制”到屏幕上了。元素可以呈现在屏幕上了!


渲染阻塞资源

通俗的解释为有东西阻止了屏幕上节点的实际绘制,在成功绘制之前,必须构造 DOM 和 CSSOM,因此,HTML 和 CSS 都是渲染阻塞资源。

  • JavaScript 如何执行?
    一个常用的 Web 应用程序肯定会使用一些 JavaScript。这是一定的。JavaScript 的“问题”在于你可以使用 JavaScript 修改页面的内容和样式。通过这种方式,你可以从 DOM 树中删除元素和添加元素,还可以通过 JavaScript 修改元素的 CSSOM 属性。这很方便,但是同时也带来了弊端
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>testRander</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <p>浏览器页面渲染机制</p>
  <img src="http://spage.haimati.cn/activityImage/newplan.jpg">
</body>
</html>
复制代码

这是一个非常常见的文档。样式表 style.css简单定义样式:

* {
  font-size: 20px;
}
body {
  background-color: antiquewhite;
}
复制代码

一段简单的文本和图像呈现在屏幕上。

根据前面的解释,浏览器从磁盘(或网络)读取 HTML 文件的原始字节并将其转换为字符。字符被进一步解析为标记。当解析器遇到< link rel="stylesheet" href="style.css">时,就会请求获取 CSS 文件 style.css。DOM 构造继续进行,当 CSS 文件返回一些内容后,CSSOM 构造就开始了。

  • 引入 JavaScript
    每当浏览器遇到脚本标签时,DOM 构造就会暂停!整个 DOM 构建过程都将停止,直到脚本执行完成。JavaScript 可以同时修改 DOM 和 CSSOM。由于浏览器不确定特定的 JavaScript 会做什么,所以它采取的预防措施是停止整个 DOM 构造。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>testRander</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <p id="title">浏览器页面渲染机制</p>
  <img src="http://spage.haimati.cn/activityImage/newplan.jpg">
  <script>
      let title = document.getElementById("title");
      console.log("title is: ", title);
  </script>
</body>
</html>
复制代码

当把js放到元素之前的话

<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>testRander</title>
 <link rel="stylesheet" href="style.css">
</head>
<body>
   <script>
       let title = document.getElementById("title");
       console.log("title is: ", title);
   </script>
 <p id="title">浏览器页面渲染机制</p>
 <img src="http://spage.haimati.cn/activityImage/newplan.jpg">
</body>
</html>
复制代码

当脚本试图访问一个 id 为 header 的 DOM 节点时,由于 DOM 还没有完成对文档的解析,所以它还不存在。这把我们带到了另一个重要的问题。脚本的位置很重要。

在默认情况下,每个脚本都是一个解析器阻断器!DOM 的构建总是会被打断。不过,有一种方法可以改变这种默认行为。如果将 async 关键字添加到脚本标签中,那么 DOM 构造就不会停止。DOM 构造将继续,脚本将在下载完成并准备就绪后执行。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>testRander</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <script src="test.js" async></script>
  <p id="title">浏览器页面渲染机制</p>
  <img src="http://spage.haimati.cn/activityImage/newplan.jpg">
</body>
</html>
复制代码

把js放入test.js中进行引入

let title = document.getElementById("title");
console.log("title is: ", title);
复制代码

这样DOM的构建就不会停止,脚本在构造完成后执行。

文章分类
前端