Fork me on Github

探索浏览器对于HTML的渲染原理(过程)

原文链接:https://github.com/FIGHTING-TOP/FE-knowlodge-base

探索目的

为了更好地优化我们前端页面的性能,特对基础原理进行考究

大致过程

从浏览器获取到HTML文件开始,浏览器会经历解析、渲染、交互三大阶段;

解析

浏览器会在收到HTML文件的第一次响应包后开始解析(即使该HTML大于14kb),解析过程包括DOM树和CSSOM树的构建、资源的预加载(通过预加载扫描器异步加载)、JavaScript 编译以及构建辅助功能树。DOM包含了页面的所有内容,CSSOM包含了页面的所有样式。

渲染

渲染过程包括Style、Layout、Paint以及还可能会有Compositing这些阶段,
渲染器会在DOM树和CSSOM树构建好之后,将两棵树组合成一个render树,这个过程会计算所有可以显示标签的样式,可以显示的标签包括从body开始(包括body)没有display: none的所有节点,包含带有visibility: hidden的节点。
render-tree-construction
布局是浏览器从根节点开始遍历整棵render树,计算每个节点的尺寸和位置;第一次确定节点的大小和位置称为布局。随后对节点大小和位置的重新计算称为回流。如果布局完成后有图片加载完成并且该图片没有指定大小,这样就会造成回流。
绘制是将布局阶段生成的render树的多有节点转换成屏幕上的实际像素,包括文本、颜色、边框、阴影和替换的元素(如按钮和图像)。

在这个过程中,浏览器会将布局树中的元素分解为多个层,将内容提升到GPU上的层(脱离CPU上的主线程),从而提高绘制和重新绘制的性能。每一个带有一些特定的CSS属性的元素和一些特定标签元素都可以实例化一个层,像和元素,以及任何带有opacity``,3D转换will-changeCSS属性的元素都会和它们的子节点单独绘制一个层,当然,如果子节点满足以上条件则会再单独实例一个层。
浏览器针对处理CSS动画和不会很好地触发重排(因此也导致重新绘制)的动画属性进行了优化。为了提高性能,可以将被动画化的节点从主线程移到GPU上。将导致合成的属性包括 3D transforms (transform: translateZ(), rotate3d(),etc.),animating transform 和 opacityposition: fixedwill-change,和 filter。一些元素,例如 <video><canvas> 和 <iframe>,也位于各自的图层上。 将元素提升为图层(也称为合成)时,动画转换属性将在GPU中完成,从而改善性能,尤其是在移动设备上。

交互

当我们看到页面显示出来后,整个页面的所有渲染工作可能并没有完成,因为这时页面可能还无法进行点击,滚动,触摸等操作,因为这个时候可能还有js没有执行完,也就是主线程仍在占用状态,特别是像绑定了window.onLoad这种的js逻辑。
在测试页面性能的时候,有一项重要的指标就是TTI(Time to Interactive)是从第一个请求导致DNS查找和SSL连接到页面可交互时所用的时间。

webkit 渲染流程图

image

gecko 渲染流程图

image

问题探究

CSS的加载会阻塞页面的渲染吗?

提出假设

我们来分析一下,从上面的页面渲染流程来看,HTML的渲染过程是将解析阶段生成的DOM树和CSSOM树组合成一个render树,那么CSS的加载肯定是会阻塞CSSOM树的建立,那没有CSSOM树也就没办法合成render树,因此也就没办法渲染,所以CSS的加载是会阻塞页面的渲染的。

验证假设

我们来写段代码测试一下

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        console.log('走js这里了')
    </script>
    <link rel="stylesheet" href="https://www.google.com/123123.css">
</head>
<body>
    <h1>Hello World!</h1>
</body>
</html>

执行结果
test1

从结果来看,当css文件请求没有结束之前页面是空白的,等css加载失败后页面才显示内容,这就说明我们的假设成立。
在这个结果中,也可以看出css没有加载结束之前,js被执行了,那我们将js代码写在link标签的后面,他还会被执行吗?

css加载会阻塞j后面的s运行吗?

直接测试

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="https://www.google.com/123123.css">
    <script>
        console.log('走js这里了')
    </script>
</head>
<body>
    <h1>Hello World!</h1>
</body>
</html>

执行结果
test2
结果来看js是在css加载后执行了,则说明css的加载阻塞了后面js的运行。

那么如果不是内嵌的js,使用src来引入一个js文件呢?

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./test000.js"></script>
    <script>
        console.log('走link标签前面的内嵌js了')
    </script>
    <link rel="stylesheet" href="https://www.google.com/123123.css">
    <script src="./test111.js"></script>
    <script>
        console.log('走link后面的内嵌js了')
    </script>
</head>
<body>
    <h1>Hello World!</h1>
</body>
</html>

在test000.js中这样写

console.log('This is test000.js')

在test111.js中这样写

console.log('This is test111.js')

执行结果
test3
结果是一样的

那如果使用了defer或者async属性呢?

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./test000.js" defer></script>
    <script>
        console.log('走link标签前面的内嵌js了')
    </script>
    <link rel="stylesheet" href="https://www.google.com/123123.css">
    <script src="./test111.js" defer></script>
    <script>
        console.log('走link后面的内嵌js了')
    </script>
</head>
<body>
    <h1>Hello World!</h1>
</body>
</html>

结果是什么样呢?
test4
那要是把defer全部换成async呢?
test5

为什么会这样?
因为defer和async都可以使浏览器异步加载并解析执行js文件,所以link标签不能阻塞js的文件的执行,你可能会说,那为什么defer被阻塞了呢?我们知道defer和async有点不一样的,在没有内嵌js时,defer修饰的js会按照DOM先后顺序依次执行,async则是先加载完成的先执行;在有内嵌js时,无论是defer还是async都会等待内嵌js执行完才会去执行它们,如果没有这两个属性就会按照DOM中的顺序依次执行各个js。

在实际中,我们会使用一些方法来应对css加载阻塞js执行的问题,比如把js放在link之前然后使用DOMContentLoaded方法,或者之前在jQuery中常用的ready方法。

Reference

posted @ 2020-11-25 10:27  王者归来!  阅读(487)  评论(0编辑  收藏  举报