浅析浏览器从输入url到页面渲染发生了什么(今生篇)
一 前言
“ 我报名参加金石计划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步)
可以发生重排的操作:
- window大小被修改
- 增加或者删除dom结构
- 元素的尺寸变化
- offsetWidth 和 offHeight,offset...;clientWidth,clientHeight;client...;scrollTop,scroll... (这里画重点!!!)
重排总结:所有导致元素几何信息发生变化的操作都会触发重排
3) 重绘!(渲染的第5步)
所有导致元素非几何信息发生变化的操作都会触发重绘
面试经典问:!!!重排一定会重绘,重绘不一定会重排 ; 因为重排会影响重绘,所以重排比重绘大
4) 浏览器的优化
当改变元素的几何信息导致重排发生,浏览器提供一个渲染队列用于临时存储该次重排,浏览器继续执行代码,如果还有几何信息修改,继续入队,直到没有样式修改。然后浏览器会按照浏览器渲染队列来批量优化重排过程
offsetLeft,offset... 会强制刷新渲染队列 (立即执行修改任务) (这里画重点!!!)
5) 减少重排的操作(重排的性能开销是很大的)
- 让元素脱离文档流 -- 改变样式 -- 回归文档流
// 减少重排的操作
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);
回看上面的目录 深入了解页面的渲染 中的 浏览器的优化,新版浏览器优化后,当改变元素的几何信息导致重排发生,浏览器提供一个渲染队列用于临时存储该次重排,浏览器继续执行代码,如果还有几何信息修改,继续入队,直到没有样式修改。然后浏览器会按照浏览器渲染队列来批量优化重排过程
所以解答的思路是:
- el.offsetWidth要干的两件事请:自己进行重排,还会使渲染队列里面的立马执行
- 本来在el.offsetWidth执行之前,应该进行一次重排的,但是因为重排队列里面是空的,所以直接自己进行重排,进入到重排队列里面,后面的两次重排跟着进入到重排队列
- 三次重排会被优化到只有一次重排 所以上面的三行代码触发了1次重排,1次重绘
不足之处,欢迎在评论区补充,萌新作者会不断更新
转载自:https://juejin.cn/post/7143588008598601764