【译】浏览器如何呈现页面(二)

这篇文章是第二篇,第一篇在这里。javascript

第一篇大体讲到了浏览器从获取原始数据开始,直到把内容画到屏幕上,可是尚未完。css

以后,还有第三篇(整理)在这里html

渲染阻塞

当你听到“渲染阻塞”的时候,你会想到啥?我猜是,‘某些行为阻止了浏览器把内容画到屏幕上’。java

的确是你猜的那样。浏览器

因此这里有了咱们的第一个优化点,把最重要的HTML内容和CSS样式提供给客户端,越快越好。服务器

DOM和CSSOM都必须在屏幕绘制以前构造完成,因此HTMLCSS都是渲染阻塞资源。网络

因此关键点就是你应该让客户端竟可能快地获取你的htmlcss,这样就可以优化首屏渲染的时间。app

JavaScript登场

如今,基本只要是个网站都有JavaScript。。。async

JavaScript是会修改页面内容以及样式的。做为实现,你能用JS从DOM树里添加删除元素,还可以修改元素的CSSOM的属性。post

这很好,可是这也须要付出代价。

考虑一下下面的HTML文档:

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Medium Article Demo</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <p id="header">How Browser Rendering Works</p>
    <div><img src="https://i.imgur.com/jDq3k3r.jpg"></div>
</body>

</html>
复制代码

这是个很简单的例子,style.css内容也很简单,以下:

body {
  background: #8cacea;
}
复制代码

最终显示结果以下:

结果

简单文本和图片渲染到了屏幕上。

从以前的步骤来看,通过 原始数据->字符->标记 转换后,浏览器一旦读到<link rel="stylesheet" href="style.css">这一行的时候,就回去请求这个css文件style.css,DOM的构建仍然继续,而且一旦css文件返回内容以后,这个CSSOM的构建也开始了。

当JavaScript来了以后,这个过程会发生什么样的变化呢?

记住一点,只要浏览器读到了script标签,那DOM的构建就会暂停!

整棵DOM树的建立过程会暂停,直到这个script运行完成。(这里不是指那些ready以后的脚本哈。)

重要1

由于js会去修改DOM和CSSOM,而浏览器又不可以肯定这个js会作些什么,因此须要经过暂停整个DOM的建立来预防。

这样会有多糟?

让咱们用上面那个例子,而后加一点基础的script进去:

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Medium Article Demo</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>

    <p id="header">How Browser Rendering Works</p>
    <div><img src="https://i.imgur.com/jDq3k3r.jpg"></div>

    <script> let header = document.getElementById("header"); console.log("header is: ", header); </script>
</body>

</html>
复制代码

在这个script里,我用id去Dom里获得一个节点header,而后打印到了console。

结果1

你有注意到这个script是在body的底部么,让咱们看下若是把它放在头部会怎样:

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Medium Article Demo</title>
    <link rel="stylesheet" href="style.css">
    <script> let header = document.getElementById("header"); console.log("header is: ", header); </script>
</head>

<body>

    <p id="header">How Browser Rendering Works</p>
    <div><img src="https://i.imgur.com/jDq3k3r.jpg"></div>
</body>

</html>
复制代码

运行后,获得的结果header是null

结果2

为何?

其实很简单。当HTML解析器正在建立DOM树的时候,发现有一个script标签,而此时,body标签以及它的内容尚未解析出来。DOM构建被暂停直到这个script执行完。

这里又带出了另外一个重要的点。

你脚本的位置很重要。

重要2

若是你以文件形式加载一个js,同样也会暂停DOM的建立,相似:<script src="app.js"></script> 若是这个app.js放在远程服务器上,而且网络很慢,须要加载3秒呢?那么,DOM的构建就要等到3秒以后再继续!!!

这是个很大的性能问题,但还不是所有。

记得JS还可以改变CSSOM,好比:

document.getElementsByTagName("body")[0].style.backgroundColor = "red";

那么,在CSSOM建立好以前,解析器读到script会怎样呢?

结果是js的执行将会暂停,直到CSSOM建立好。

重要3

因此当遇到script的时候,DOM的构建会中止,可是CSSOM并不会(【译注】不但不会,还会暂停js的执行)。

【译注】这里我想要补充一下,经过上面所说的,咱们能够推出一个观点:

JS 文件不仅是阻塞 DOM 的构建,它会致使 CSSOM 也阻塞 DOM 的构建。

本来 DOM 和 CSSOM 的构建是互不影响,井水不犯河水,可是一旦引入了 JavaScript,CSSOM 也开始阻塞 DOM 的构建。由于不完整的 CSSOM 是没法使用的,若是 JavaScript 想访问 CSSOM 并更改它,那么在执行 JavaScript 时,必需要能拿到完整的 CSSOM。因此就致使了一个现象,若是浏览器还没有完成 CSSOM 的下载和构建,而咱们却想在此时运行脚本,那么浏览器将延迟脚本执行和 DOM 构建,直到完成 CSSOM 的建立。也就是说,在这种状况下,浏览器会先去构建 CSSOM,而后再执行 JavaScript,最后在继续构建 DOM。

async属性

默认状况下,每一个script都会组织DOM树的建立。不够有一个方法可以改变这一个行为。

若是你在script标签里加一个async属性,那么DOM树的建立就不会暂停,这个脚本就会在下载完后去执行。例如:

<script src="https://some-link-to-app.js" async></script>

关键渲染路径

咱们以前所说的从获取html,css和js原始数据开始一直到画到屏幕上。这整个过程就被叫作关键渲染路径(critical rendering path)。

优化网页性能就是优化这条关键渲染路径。

通过良好优化的网站应该是渐进式加载的,而不会被整个阻止。

这就是一个网站快与慢的差别所在。 一个比较好的策略是让浏览器优先加载哪些资源,加载资源的顺序比较重要。(【译注】好比:大部分都会将 js 放在 底部,css 放在顶部等。)

原文

相关文章
相关标签/搜索