1 浏览器首次渲染(六步)
- 解析HTML,构建HTML树(DOM文档对象模型)
- 解析CSS,构建CSS树(CSSOM)
- 将两棵树合并成一颗渲染树(render tree)
- 根据渲染树对每个节点进行layout布局(根据文档流定位、盒模型、计算大小和位置)
- 把节点Paint绘制颜色(把边框颜色、文字颜色、阴影等画出来)
- Composite合成拍成一层(根据层叠关系展示画面),实现最终渲染。
2 再次渲染
操作JS改变CSS属性会触发不同的渲染流程来更新样式:
- 点击按钮,删除DOM里的元素,元素从文档流里消失,则需要重新构建DOM树,和首次渲染一样。
- 改变布局、滚动页面:layout、repaint、composite。需要重新计算元素位置
- 改变背景颜色:repaint、composite,不用layout,没有改变布局。
- transform变换:只合成Composite,布局和背景颜色都没有改变。
Reflow 和 Repaint
Reflow 回流:重新计算元素的几何尺寸大小,位置,内容
Repaint 重绘:绘制界面发生变化的部分,颜色,背景色
案例(repaint一定会发生)
- 添加、删除、更新DOM节点(reflow、repaint)
- 修改元素的magin、padding,border(reflow、repaint)
- display:none(reflow、repaint)元素从文档流中消失
- visibility:hidden(repaint)元素仍占据空间
- 修改颜色、背景色(repaint)
回流必将引起重绘,重绘不一定会引起回流。
如何优化
JS
- 尽量一次性修改样式(JS每次单独执行一次,改变一个样式,就要重绘回流一次)
- DOM离线后修改。(对虚拟DOM不断修改,再一次性渲染到真实DOM上)
CSS
- 给动画使用绝对定位可以减小reflow(浏览器只会计算这个元素本身位置的变化,小范围计算)
- 使用
transform:translate,触发浏览器的GPU进行渲染,计算绘制速度更快,减少CPU的负荷。
3 CSS 和 JS 对页面的阻塞
浏览器下载解析 HTML,构建 DOM 树,下载解析CSS,构建CSSOM树,这两个步骤可以是并行的,之后 DOM 树和 CSSOM树结合成 Render Tree。我们一般在HTML里是这样引入CSS和JS的:
<head>
<link rel="stylesheet" href="http://a.com/css" />
</head>
<body>
...
<script>...</script>
</body>
当CSS和JS的解析时间很久,会引发一些问题:
- 阻塞解析:当前阻塞点后面的标签不会被解析。
- 阻塞渲染:阻塞点后面的标签会继续被解析,但不会触发页面渲染(白屏),阻塞这之后的JS代码执行。
当CSS加载时间很久,link标签放置位置不同,会引发以下两种情况之一:
- 白屏:页面一片空白,突然展示完整的有样式的内容。(当link放在head内)
- 无样式内容闪烁FOUC:页面一开始展示无样式的内容,突然样式正常。(link放在页面底部)
- CSS加载:不阻塞解析,会阻塞渲染
- 不阻塞 DOM 树的解析,但会阻塞CSSOM的解析,进而阻塞 Render Tree (因为渲染树需要等待CSSOM,减少不必要的回流重绘)
- 阻塞在 CSS 之后的 JS 的解析执行(因为某些 JS API 如 getComputedStyle 需要最新的样式数据)
因此,尽量把link放在head标签内,防止出现FOUC
- JS加载:阻塞解析
JS 会阻塞在其后的 DOM 树的构建,进而影响 Render Tree。在JS运行请求完成之后,DOM节点才会被解析。
因此,尽量把外置JS放置在body的最后,以便让DOM尽快展现。
例子:以下内容是不能成功显示的,因为浏览器解析时遇到script标签执行,此刻没有执行解析到body内的标签,当前页面上什么都没有,是无法获取到这个标签,因此应该将<script>标签放到<p>后面。
<head>
<link rel="stylesheet" href="http://a.com/css" />
<script>
document.querySelector('p').innerText="hhh"
</script>
</head>
<body>
<p>hello</p>
</body>
总结
CSS不会阻塞DOM解析,但是会阻塞DOM渲染,CSS会阻塞render tree的生成,进而阻塞DOM的渲染。CSS会阻塞JS的执行JS会阻塞DOM解析- 浏览器遇到
<script>标签且没有defer或async属性时会触发页面渲染 <link>最好放在<head>里,<script>最好放在<body>最底部
常见问题
1.让一个元素消失的办法
display:none;元素会从页面中彻底消失,它原本占据的空间会被其他元素占有,会造成浏览器的回流与重绘。visibility: hidden;元素会从页面中消失,它原本占据的空间会被保留,会造成浏览器的重绘,适用于希望元素隐藏又不影响页面布局的场景,不再响应绑定的事件。opacity:0;元素变成透明的,但原本占据的空间还在,仍响应绑定的事件。transform: scale(0,0)同上,视觉缩放。- 设置盒模型各属性为0,使用
overflow: hidden;来隐藏子元素。
.box1 {
width: 0;
height: 0;
padding: 0;
border: 0;
margin: 0;
overflow: hidden;
}
- 移出屏幕:设置元素绝对定位与负值的top、left等将元素移出屏幕。
- 设置绝对定位与z-index,将z-index:-999。也要同时设置其他元素的z-index值
.box1 {
position: absolute;
z-index: -999;
}
.box2 {
position: absolute;
z-index: 1;
}