“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情”
当有人问浏览器输入url到页面渲染发生了什么,你会怎么回答? 下面就来聊聊浏览器输入url到页面渲染发生了什么,浏览器将前端的代码请求过来了(这是前世),那么拿到代码浏览器如何绘制我们就叫今生。
那么浏览器是如何渲染页面的呢?一共分为下面五个步骤。
1.将HTML解析成DOM树(其实就是一个js对象)
当浏览器拿到这个html的时候,不会立即将这个html绘制到页面上,而是先生成一个DOM树
<!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>
</head>
<body>
<div class="wrap">
<p>hello</p>
</div>
<script>
// 生成的DOM树
var dom = {
target: {
el: 'div',
class: 'wrap',
children: [
{
el: 'p',
class: '',
value: ''
}
]
}
}
</script>
</body>
</html>
2. CSS代码解析生成CSSOM树
DOM树生成完成之后就会将CSS代码解析生成CSSOM树,也就是类似生成DOM树。
3. 结合DOM树和CSSOM树,生成一棵 render树
第三步就是将前面生成的DOM树和CSSOM树融合成一棵 render树
<!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: #000;
}
</style>
</head>
<body>
<!-- 当浏览器获取到这个html的时候,不会立即将这个html绘制到页面上,而是先生成一个DOM树 -->
<div class="wrap">
<p>hello</p>
</div>
<script>
var dom = {
target: {
el: 'div',
class: 'wrap',
// 将CSSOM树融合进来
style: {
width: '100%',
height: '100%',
.... // 其他的一些样式
},
children: [
{
el: 'p',
class: '',
value: ''
}
]
}
}
</script>
</body>
</html>
4. 布局,将渲染树的节点进行平面合成
这一步只在浏览器里面执行,并没有渲染到页面上,可以理解为浏览器将完整页面的蓝图合成了,浏览器的大脑已经知道了即将要渲染的页面的样子
5. 绘制页面到屏幕上 (render-UI)
这一步我们才能看到页面,这是一个异步操作。
渲染
当我们访问某个页面的时候,导致页面内容变化就会导致页面重新渲染;当重新渲染的时候就不再需要重新生成DOM树、CSSOM树 和render树,而只要重新执行第四第五步。
那么就引出来第四步的重排(重新排版)与第五步的重绘(重新绘制)
重排
发生重排一定会发生重绘, 会发生重排的操作:
- window大小被更改
- 增加或删除DOM解构结构
- 元素尺寸变化
- offsetWidth 和 offsetHeight , offset... , clientWidth,client...,scrollTop,scroll... 所有导致元素几何信息发生变化的操作
重绘 第五步
所有导致元素非几何信息发生变化的操作 而boder-radious影响的是重绘而不是重排,因为即使将某个长方形的四个角变成圆形的,但是本身长方形这个容器没有改变,只是让这四个角看不见了
下面有个问题执行下面的代码,浏览器发生了几次重排,几次重绘
<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>
那么肯定会有人说浏览器发生了三次重排,三次重绘。其实不然,我们要先知道浏览器的优化策略:当改变元素的几何信息导致重排发生,浏览器提供一个渲染队列用于临时存储该次重排,浏览器继续执行代码。如果还有几何信息修改,继续入队,直到没有样式修改。然后浏览器会按照渲染队列来批量优化重排过程
所以在执行上面的代码的时候,重排这件事情浏览器会一次性的执行,因为每次浏览器识别到offsetWidth时就会重排,然后浏览器就会将这次重排挂到渲染队列,最后再一次执行。所以浏览器发生了一次重排,一次重绘。
那么肯定有人会问下面的代码会发生几次?
div.style.left='10px'
console.log(div.offsetLeft)
div.style.top='10px'
console.log(div.offsetTop)
肯定有人说是两次,也肯定会有人说一次的,不纠结;当浏览器看到offsetLeft时,浏览器就会强制将渲染队列清空然后再重排,所以会执行两次重排,两次重绘