浏览器的渲染过程

作者因面试前端优化问题答得不好,深入研究浏览器渲染过程。介绍从拿到代码开始,解析html、css、js的过程,如外部css文件才调用css解析器,js会阻塞html解析等,还提及减少请求次数和文件大小、script放最后或添加async等性能调优方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

浏览器的渲染过程

前言

最近在面试的时候遇到了一个前端的优化问题,答得不好,回来之后,就想系统的学习一下,发现想掌握好性能优化,就必须了解好浏览器的整个渲染过程。所以需要先学习渲染过程,却发现网上的相关资料少的可怜,能找到的只有一张很常见的图和几句话:http解析器解析生成DOM树,css解析器解析css生成CSSOM,DOM树和CSSOM树结合生成Render Tree之类的话,完全get不到重点。在经过自己几天的努力之后,大致了解了其渲染过程。接下来,让我们一起来深入了解其渲染过程。

渲染过程

我们在浏览器输入url请求之后,会向DNS服务器请求IP地址、进行TCP三次握手等操作,这些不是本文的重点,我们不进行研究,我们从服务拿到字符串(代码)后,开始说起。首先,我们先来一段简单的代码(如下所示),然后我们在浏览器中打开,并运行这个文件,观看渲染过程的日志。

<!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>
    <h1>Hello World</h1>
</body>
</html>

从上面的日志中可以看出,如果html文件中没有css、js代码的时候,只会调用html解析器进行解析,生成DOM文档而已,那么如果文件有css文件呢?接下来,我们在demo1.html中添加style标签为h1添加样式。代码如下,然后运行代码,看看其日志是怎么样的。

<!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>
<style>
    /* 新增加的样式 */
    h1 {
        color : red;
    }
</style>
<body>
    <h1>Hello World</h1>
</body>
</html>

在这里插入图片描述

我们发现,虽然添加了css样式,样式也生效了,但是并没有启动css解析器,这不科学啊,说好得css解析器解析css生成CSSOM树呢?接下来我们试试将css样式表移出来,放在style.css文件里面。

h1 {
    color : red;
}

在demo1.html中使用link标签引入css样式表。然后执行一下,看看其打印的日志。

<!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>
<!-- 引入样式表 -->
<link href = "./style.css" rel = "stylesheet">
<body>
    <h1>Hello World</h1>
</body>
</html>

在这里插入图片描述

从日志中可以看出,终于调用了css解析器了,这是因为有了外部资源style.css文件,在html解析的过程中,遇到要请求外部资源的时候,发送了一个send请求,然后接收资源,但是我们发现,在请求和接收资源的过程中,耗时都是0,这说明了该渲染线程并没有在做这些事,而是新开了一个线程,专门在处理这些请求。完成会将其获得的资源放在一个任务队列里面,待html解析器完成工作之后,就会开始去任务队列里面找任务(这里指style.css),然后调用css解析器解析生成CSSOM。所以说不是所有的css代码都是由css解析器解析的,只有外部文件才是。

这里就涉及到了一个性能调优的点:减少请求的次数和文件大小,(为什么会涉及到呢,你或许有疑惑)
首先,我们发现,在加载外部资源的时候,如果有多个资源需要请求,而这个时候,html解析器已经完成了工作,css解析器完成了任务队列里面的任务,任务队列里却没有任务了,但是还有请求没有完成,这个时候就需要等待了,所以减少请求的次数和文件大小可以优化性能。

讲完了css,接下来我们讲讲js,由于js可以操作doucment文档,所以我们不能在DOM树完成之后再去调用javascript引擎执行js文件。所以在遇到js代码的时候,渲染的主线程只能停止html解析,调用js引擎来执行js代码,上代码来看看实际情况。

<!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>

<script>
    var tag = document.getElementById("title");
    console.log(tag);
</script>

<body>
    <h1 id = "title">Heelo World</h1>
</body>

<script>
    var tag2 = document.getElementById("title");
    console.log(tag2);
</script>

</html>

在这里插入图片描述

可以看出,先执行了第一个script代码,在获取id为title的h1标签的时候,由于还没有解析到,所以是找不到h1的,在控制台打印出 “ null”,在执行第二个script代码的时候,h1已经在DOM树里面了,这个时候就可以获取到h1。

将js代码移出html页面,使用外部加载资源看看。

// 外部的gettag.js文件
var tag = document.getElementById("title");
console.log(tag);
var tag2 = document.getElementById("title");
console.log(tag2);
<!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>
<!-- 外部加载js -->
<script src="./gettag.js"></script>
<body>
    <h1 id = "title">Hello World</h1>
</body>
</html>

在这里插入图片描述

我们发现,两个console.log打印都为null,可以得出一个结论,虽然js是外部引用资源,但是因为是同步任务,所以我们的主线程会阻塞,等到js的资源拿到并执行完成之后,才继续解析html。这里有两个解决方法,第一:在script放在最后,第二,为script 添加async。这里示范一下第二种方法:

<!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>
<!-- 外部加载js -->
<script src="./gettag.js" async></script>
<body>
    <h1 id = "title">Hello World</h1>
</body>
</html>

在这里插入图片描述

可以看到,js不阻塞html解析了。

总结

渲染过程:

  1. 使用url请求拿到字符串(代码)之后,使用http解析器解析代码生成DOM Tree
  2. http解析器解析过程中,遇到需要请求外部资源的时候,会重新开启一个线程去请求这些数据,并将结果放入任务队列。
  3. http解析器解析过程中,遇到 js 代码的时候,会调用javascript引擎执行代码,html停止解析,等到javascript引擎完成工作之后,继续调用html解析器工作。
  4. http解析器解析过程中,遇到 script 需要外部加载资源,而且是同步的时候, javascript引擎会等待资源加载完成并执行完之后,才会把主线程的执行权交还给html解析器。
  5. http解析器解析过程中,遇到 style 标签,不会调用css解析器,只有在解析外部样式表的时候才会调用css解析器。

后话

需要这篇文章对你有所帮助,如果其中有什么写错的地方,欢迎指点出来。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值