likes
comments
collection
share

浅析浏览器从输入url到页面渲染发生了什么(今生篇)

作者站长头像
站长
· 阅读数 19

一 前言

“ 我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

'浏览器从输入url到页面渲染发生了什么'这是在大厂面试中经常会被问到的问题,用来考察我们对一些细节内容的知识广度。浏览器从输入url到页面渲染,前半段是浏览器成功的拿到了前端代码给请求回来了,后半段是拿到了前端代码之后要把页面绘制出来。本文要解释的就是后半段啦,也就是浏览器从输入url到页面渲染的今生篇,希望能解答童鞋们疑惑。

二 走进浏览器从输入url到页面渲染之今生篇

浏览器从输入url到页面渲染后半段,也就是成功拿到了前端代码之后要把页面绘制出来,主要分为五个步骤

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .wrap {
            width: 100%;
            height: 100%;
            background-color: #fff;
        }
    </style>
</head>
<body>
    <div class="wrap">
        <p>hello</p>
    </div>

    <script>
    // 生成DOM树
        var dom = {
            target: {
                el:'div',
                class:'wrap'
                // CSSOM树
                style: {
                    width:'100%',
                },
                children: [
                    {
                        el:'p',
                        class:'',
                        value:'hello'
                    }
                ]
            }
        }
    </script>
</body>
</html>

1. HTML代码解析生成DOM树

代码示例如上

2. CSS代码解析成CSSOM树

代码示例如上,想要更加了解可以看 HTML文档解析和DOM树的构建

3. 结合DOM树 和 CSSOM树,生成一棵 render(渲染)树

网页生成的时候,至少渲染一次

4. 布局,将渲染树的所有节点进行平面合成(可以看成仅在浏览器的大脑里面操作)

5. 绘制页面到屏幕上 (render-UI)

注意:!!!在异步执行中,render-UI的执行时间在微任务执行之后,下一次的宏任务之前,因为每次微任务执行完毕之后,如果有页面要渲染,那就做页面的渲染操作

三 深入了解页面的渲染

1) 渲染

网页生成的时候,至少渲染一次

2) 回流 (也叫重排) (渲染的第4步)

可以发生重排的操作:

  1. window大小被修改
  2. 增加或者删除dom结构
  3. 元素的尺寸变化
  4. offsetWidth 和 offHeight,offset...;clientWidth,clientHeight;client...;scrollTop,scroll... (这里画重点!!!)

重排总结:所有导致元素几何信息发生变化的操作都会触发重排

3) 重绘!(渲染的第5步)

所有导致元素非几何信息发生变化的操作都会触发重绘

面试经典问:!!!重排一定会重绘,重绘不一定会重排 ; 因为重排会影响重绘,所以重排比重绘大

4) 浏览器的优化

当改变元素的几何信息导致重排发生,浏览器提供一个渲染队列用于临时存储该次重排,浏览器继续执行代码,如果还有几何信息修改,继续入队,直到没有样式修改。然后浏览器会按照浏览器渲染队列来批量优化重排过程

offsetLeft,offset... 会强制刷新渲染队列 (立即执行修改任务) (这里画重点!!!)

5) 减少重排的操作(重排的性能开销是很大的)

  1. 让元素脱离文档流 -- 改变样式 -- 回归文档流
  // 减少重排的操作
    div.style.display = 'none'
    div.style.left = '10px'
    div.style.top = '10px'
    div.style.top = '10px'
    // .....
    div.style.display = 'block'     // 只有两次重排

四 练一练 字节面试题

<body>
   <div id="app"></div>

   <script>
    // 字节面试题!!!  提问:下面的三行代码触发了几次重排,几次重绘
    let el = document.getElementById("app");
    el.style.width = (el.offsetWidth + 1 ) + "px";
    el.style.width = 1 + "px";
   </script>
</body>

我的第一次思路是

el.offsetWidth 计算它的长度后就会进行一次重排; (el.offsetWidth + 1 ) + "px" 第二次重排; el.style.width = 1 + "px" 第三次重排

但是字节面试题不可能就这么简单的,结果明显是错误的,解答之前我们先回看上面的目录 深入了解页面的渲染 看完之后我们先一起来看一下这一段代码

    div.style.left = '10px'
    console.log(div.offsetLeft);// 会强制刷新渲染队列,会把渲染队列里面的执行掉,清空掉   
        
    div.style.top = '10px'
    console.log(div.offseTop);
    

回看上面的目录 深入了解页面的渲染 中的 浏览器的优化,新版浏览器优化后,当改变元素的几何信息导致重排发生,浏览器提供一个渲染队列用于临时存储该次重排,浏览器继续执行代码,如果还有几何信息修改,继续入队,直到没有样式修改。然后浏览器会按照浏览器渲染队列来批量优化重排过程

所以解答的思路是:

  1. el.offsetWidth要干的两件事请:自己进行重排,还会使渲染队列里面的立马执行
  2. 本来在el.offsetWidth执行之前,应该进行一次重排的,但是因为重排队列里面是空的,所以直接自己进行重排,进入到重排队列里面,后面的两次重排跟着进入到重排队列
  3. 三次重排会被优化到只有一次重排 所以上面的三行代码触发了1次重排,1次重绘

不足之处,欢迎在评论区补充,萌新作者会不断更新