大厂面试题:浏览器从输入url到页面渲染发生了什么?——今生

255 阅读3分钟

页面渲染过程和面试题解析

1.浏览器的渲染是怎么进行的呢?这里用简单的语言描述一下

从浏览器获取到页面内容(html,css,js)开始

1.浏览器会将html代码解析成DOM树

2.浏览器会将css代码解析成CSSOM树

3.结合DOM树和CSSOM树,生成一棵render树

4.将render树的所有节点进行平面合成(重排/回流)

5.绘制页面到屏幕上(重绘)

1.1将html代码解析成DOM树

浏览器获取到一段html代码,是解析成什么样的DOM树?

例如从浏览器读到html开始,当然真实的DOM树是并没有下面那么简单,便于理解

html
    <div class="wrapper">
        <p>hello</p>
    </div>

var dom = {
    target:{
        el:'div',
        class:'wrapper',
        children:[
            {
                el:'p',
                class:'',
                value:'hello'
            }
        ]
    }
}

1.2将css代码解析成CSSOM树

浏览器获取一段css代码,解析成CSSOM树,这个过程和解析DOM树是相似的。

1.3将DOM树和CSSOM树结合成render树

css
    <style>
        div{
            width: 100;
            height: 100;
        }
    </style>
    
html

    <div class="wrapper">
        <p>hello</p>
    </div>

var dom = {
    target:{
        el:'div',
        class:'wrapper',
        style:{
            width: 100;
            height: 100;
        }
        children:[
            {
                el:'p',
                class:'',
                value:'hello'
            }
        ]
    }
}

1.4将render树的所有节点进行平面合成——重排(回流)

浏览器进行这一步时,页面布局完成,但是页面还没有被渲染到页面

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

在事件循环中,微任务队列执行完成,不会立即执行宏任务,之间如果有页面渲染则执行页面渲染(render-UI)

2.重排(回流)和重绘

2.1重排(回流)

重新生成新的布局,重新排列元素

什么情况会发生重排?

所有导致元素几何信息发生变化的操作,都会引起重排,例如:

  1. window大小被修改
  2. 增加删除dom结构
  3. 元素尺寸发生改变
  4. offsetWidth 和 offsetHeight, offset ... ,clientWidth,client...,scrollTop,screll ...
  5. 等等

2.2重绘:重新绘制页面

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

重排一定会重绘,重绘不一定会重排

3.浏览器的优化策略

那么这个过程经历了几次重绘和重排?

let el = document.querySelector('div')
el.style.width =  (el.offsetWidth + 1) + 'px'
el.style.width = 1 + 'px'

我们知道 offsetWidth 会触发重排, 元素尺寸发生改变会触发重排, 而发生重排又会发生重绘,所以回答3次重排,3次重绘。

面试官:恭喜你! 0 分

当改变元素的几何信息发生变化,导致重排发生,浏览器提供一个渲染队列用于临时存储该次重排,浏览器继续执行代码, 如果还有几何信息发生变化,继续入队,直至代码结束,然后浏览器会按照渲染队列来批量优化重排过程。如果途中出现了offsetWidth ... 等计算属性,则会立即对渲染队列进行刷新,重排一次

所以这题,在offsetWidth计算时,渲染队列是空的。所以offsetWidth进入渲染队列。最后,后续对几何信息的修改依次进入渲染队列。最后发生了1次重排,1次重绘

4.减少重排的操作

. 尽量避免style的使用,对于需要操作DOM元素节点,重新命名className,更改className名称。\

· 如果增加元素或者clone元素,可以先把元素通过documentFragment放入内存中,等操作完毕后,再appendChild到DOM元素中。\

· 不要经常获取同一个元素,可以第一次获取元素后,用变量保存下来,减少遍历时间。\

· 尽量少使用dispaly:none,可以使用visibility:hidden代替,dispaly:none会造成重排,visibility:hidden会造成重绘。\

· 不要使用Table布局,因为一个小小的操作,可能就会造成整个表格的重排或重绘。\

.使用resize事件时,做防抖和节流处理。\

.对动画元素使用absolute / fixed属性。\

.批量修改元素时,可以先让元素脱离文档流,等修改完毕后,再放入文档流。