问题探究
- 用户实际体验感觉页面加载慢
- 页面加载完毕后到最终页面的展示中间的间隔时间较长
- 有动画失效的情况
浏览器收到html文本到加载css过程介绍
在用户输入网址向服务器发出请求后,服务器会返回html文件,然后浏览器开始载入html代码。在浏览器载入html代码过程中,如果发现有标签引用外部的CSS文件,浏览器又会向服务器发出请求css文件的请求。这个加载是异步,不会影响DOM树的构建,只是说在CSS没处理好之前,构建好的DOM并不会显示出来。
<link rel="stylesheet" href="demo.css">
当浏览器载入html中部分的代码,就根据CSS文件开始渲染页面了。
流程是:
- 生成dom树
- 计算css样式
- 构建render tree
- 回流(reflow),定位元素位置大小
- 绘制页面
而常见的导致页面闪动的流程是这样:
scene1:
1.浏览器发现一个<img>标签引用了一张图片,向服务器请求这个图片,未等到图片下载完,继续渲染后面的代码
2.当服务器返回图片文件后,由于图片的大小影响了后面元素的排布,浏览器重新渲染了图片部分的代码
scene2:
1.浏览器发现了一个包含一行Javascript代码的<script>标签,里面有命令浏览器隐藏元素的代码(style.display=”none”),令到浏览器重新渲染这部分的代码
回流和重绘
什么是回流和重绘
当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。
当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
3.回流必将引起重绘,而重绘不一定会引起回流。
回流的发生
- 当页面布局和几何属性改变时就需要回流。下述情况会发生浏览器回流:
- 添加或者删除可见的DOM元素;display
- 元素位置改变;top left
- 元素尺寸改变——边距、填充、边框、宽度和高度 border margin padding
- 内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变; font-size
- 页面渲染初始化;onload
- 浏览器窗口尺寸改变——resize事件发生时;
- 激活CSS伪类(:hover)
代码示例:
var s = document.body.style;
s.padding = "2px";
s.border = "1px solid red";
s.color = "blue";
s.backgroundColor = "#ccc";
s.frontSize = "14px";
document.body.appendChild(document.createTextNode('test'));
如何减少回流、重绘
-
避免修改会触发回流的操作
背景状态改变可用$ (“.rule-logo”).addClass(‘..’)代替两个按钮的display切换。
-
避免逐个更改样式
可以一次过更改style属性,或者将样式列表定义为class并一次性更改class属性
//不好的写法
var left = 1;
var top = 1;
el.style.left = left + "px";
el.style.top = top + "px";
el.className += " className1";
//较好的写法
el.style.cssText += ";
left: " + left + "px;
top: " + top + "px;";
- 对操作的元素进行“离线处理”
DocumentFragment:缓存操作,引发一次回流和重绘;
<script>
var frag = document.createDocumentFragment();
for(var x = 0;x < 10; x++){
var li = documemnt.createElement("li");
li.innerHtml = "List " + x;
frag.appendChild(li);
}
listNode.appendChild(frag);
</script>
-
对父节点使用display:none,只引发两次回流和重绘
<style>
ul{
display: none;
}
</style>
<script>
var ul = document.getElementByTagName("ul");
for(var x = 0;x < 10; x++) {
var li = document.createElement("li");
li.innerHTML = "List " + x;
ul.appendChild(li);
}
</script>
-
对动画的元素使用position: absolute,减少对父元素的回流
-
使用图片预加载
-
尽量将所见元素宽高预先设定
-
在最末改变元素
-
避免使用表格布局
-
利用transform 去改变会触发重绘的属性
-
利用requestAnimationFrame代替settimeout
参考资料: