史上最详细的页面渲染机制

页面的渲染过程

当咱们在浏览器里输入一个 URL 后,最终会呈现一个完整的网页。会经历如下几个步骤:javascript

一、HTML 的加载

页面上输入 URL 后,会先拿到 HTML 文件。HTML是一个页面的基础,因此会在最开始的时候下载它,下载完毕后就开始对它进行解析css

二、其余静态资源的下载

HTML 在解析的过程当中,若是发现 HTML 文本里面有一些外部的资源连接,好比 CSS、JS 和图片等,会当即启用别的线程下载这些静态资源。在 head 中遇到 JS 文件时,HTML 的解析会停 下来,等 JS 文件下载结束而且执行完,HTML 的解析工做再接着来,防止 JS 修改已经完成的解析结果html

知识拓展

由上得知,JS 文件放在 head 中属于同步加载,会阻塞 DOM 树的构建,进而影响页面的加载。当 JS 文件较多时,页面白屏的时间也会变长java

那如何避免这个问题呢?下面有三个解决方案浏览器

一、 script 放在 body 里(通常是 </body> 上面)

因为 DOM 是自上而下解析的,所以 JS 不会阻塞 DOM 的解析,并且这时候能够在 JS 中操做 DOM数据结构

二、设置 defer 属性

经过给 script 标签设置 defer 属性,将脚本文件设置为延迟加载,当浏览器遇到带有 defer 属性的 script 标签时,会再开启一个线程去下载 JS 文件,同时继续解析 HTML,等 HTML 所有解析完、DOM 加载完成以后,再去执行加载好的 JS 文件。只适用于引用外部 JS 文件,而且能够确保全部加了 defer 属性的脚本会按顺序执行async

三、设置 async 属性

async 属性和 defer 属性相似,也是会开启一个线程去下载js文件,但和 defer 不一样的是,aysnc 会在 JS 加载完成后马上执行,而不是会等到 DOM 加载完成以后再执行,因此仍是有可能会形成阻塞。一样的也是只适用于外部 JS 文件,若是有多个设置了 aysnc 的 JS 文件,不能像 defer 那样保证按照顺序执行布局

四、动态建立脚本

这样去动态建立文件也不会影响到页面的加载,如下是百度统计的demo性能

<script>
 var _hmt = _hmt || [];
(function () {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js";
  var s = document.getElementsByTagName("script")[0];
  s.parentNode.insertBefore(hm, s);
})();
</script>
复制代码

三、DOM 树的构建

DOM 的全称是:文档对象模型(Document Object Model),在 HTML 解析时,解析器会把解析完的 HTML转化成 DOM 对象,再进一步构建 DOM 树优化

四、CSSOM 树的构建

当 CSS 下载完,CSS 解析器就开始对 CSS 进行解析,把 CSS 解析成 CSS 对象,而后把这些 CSS 对象组装成一颗 CSSOM 树

五、渲染树的构建

DOM 树和 CSSOM 树都构建完成之后,浏览器会根据这两棵树构建出一棵渲染树

六、布局计算

渲染树构建完成之后,全部元素的位置关系和须要应用的样式就肯定了。这时候浏览器会计算出全部元素的大小 和绝对位置

七、渲染

布局计算完成之后,浏览器就能够在页面上渲染元素了,通过渲染引擎的处理后,整个页面就显示在了屏幕上

上面是一个浏览器渲染页面的大概过程,接下来就来把这个过程的几个重要部分再详细讲下


DOM 树的构建

页面中的每个 HTML 标签,都会被浏览器解析成一个对象,咱们称它为文档对象(Document Object)。HTML 本质是一个嵌套结构,在解析的时候会把每一个文档对象用一个树形结构组织起来,全部的文档对象都会挂在一个叫作 Document 的东西上,这种组织方式就是 HTML 最基础的结构–文档对象模型(DOM),这棵树里面的每一个文档对象就叫作 DOM 节点。 在 HTML 加载的过程当中,DOM 树就在开始构建了。构建的过程是先把 HTML 里每一个标签都解析成 DOM 节点(每 个标签的属性、值和上下文关系等都在这个文档对象里),而后使用深度遍历的方法把这些对象构形成一棵树。 咱们如下面的 HTML 文件为例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <header>
        <p>header</p>
    </header>
    <div class="content">
        <p class="title">title</p>
    </div>
</body>
</html>
复制代码

在构建 DOM 树的时候,就是从最外层 HTML 节点开始,按深度优先的方式构建。之因此用深度优先,是由于HTML在加载的时候是自上而下的,最早加载的是根节点 ,而后是根节点的第一个子节点 ,再而后是 head 的第一个子节点,head 构建完成后再去构建 body 部分的内容,以此类推。使用深度优先的方式构建 这棵树就和文档的加载顺序吻合了。最后,上面这个 HTML 结构会生成这样一颗 DOM 树:

image.png

CSSOM 树的构建

在浏览器构建 DOM 树的同时,若是样式也加载完成了,那么 CSSOM 树也在同步地构建。CSS 树和 DOM 类 似,它的树形结构会记录全部样式的信息

假设咱们给上面的 HTML 加上这些样式:

body {
  font-size: 16px;
}

p {
  margin: 0;
  padding: 0;
}

header {
  width: 100%;
  height: 50px;
  text-align: center;
  display: none;
}

header p {
  color: #333;
}

.content {
  padding: 0 16px;
}

.content .title {
  font-size: 24px;
}
复制代码

这组样式经过解析器的构造,会获得相似这样的一颗 CSSOM 树:

image.png

Tips:

一、这棵树的结构只是一个示意图,并非浏览器构造 CSSOM 树的真实数据结构,各个浏览器实现 CSSOM 树的方式也不彻底相同

二、CSSOM 树和 DOM 树是独立的两个数据结构,它们没有一一对应的关系。DOM 树描述的是 HTML 标签的层级关系,CSSOM 树描述的是选择器之间的层级关系

三、CSS 中存在样式继承机制,有些属性在父节点设置后,其后代节点都会具有这个样式。好比在 html 上设置一个 "color: red" ,那页面的全部标签都会继承这个属性。固然不是全部标签和属性都是能够继承的,好比 border 这种属性就是不可继承的。若是 border 可继承,那么在一个父元素里设置了之后,全部子元素都会有个边框,这显然是不合理的。因此在大部分状况下,经过这种推理,就能知道哪些样式能够继承,哪些样式不能够继承

小结

img
这一篇就讲了大概这么些东西,下一篇接着讲渲染树的构建、渲染过程、布局、绘制、重排重绘以及怎么优化页面性能(还没写完,只有个大纲)

以为写的很差或不正确的地方还请不吝赐教 [抱拳]

相关文章
相关标签/搜索