浅谈前端性能优化(html+css篇)

589 阅读4分钟

问题探究

  1. 用户实际体验感觉页面加载慢
  2. 页面加载完毕后到最终页面的展示中间的间隔时间较长
  3. 有动画失效的情况

浏览器收到html文本到加载css过程介绍

在用户输入网址向服务器发出请求后,服务器会返回html文件,然后浏览器开始载入html代码。在浏览器载入html代码过程中,如果发现有标签引用外部的CSS文件,浏览器又会向服务器发出请求css文件的请求。这个加载是异步,不会影响DOM树的构建,只是说在CSS没处理好之前,构建好的DOM并不会显示出来。

<link rel="stylesheet" href="demo.css">

当浏览器载入html中部分的代码,就根据CSS文件开始渲染页面了。

流程是:

  1. 生成dom树
  2. 计算css样式
  3. 构建render tree
  4. 回流(reflow),定位元素位置大小
  5. 绘制页面

而常见的导致页面闪动的流程是这样:

scene1:

1.浏览器发现一个<img>标签引用了一张图片,向服务器请求这个图片,未等到图片下载完,继续渲染后面的代码

2.当服务器返回图片文件后,由于图片的大小影响了后面元素的排布,浏览器重新渲染了图片部分的代码

scene2:

1.浏览器发现了一个包含一行Javascript代码的<script>标签,里面有命令浏览器隐藏元素的代码(style.display=”none”),令到浏览器重新渲染这部分的代码

回流和重绘

什么是回流和重绘

当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。

当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

3.回流必将引起重绘,而重绘不一定会引起回流。

回流的发生

  1. 当页面布局和几何属性改变时就需要回流。下述情况会发生浏览器回流:
  2. 添加或者删除可见的DOM元素;display
  3. 元素位置改变;top left
  4. 元素尺寸改变——边距、填充、边框、宽度和高度 border margin padding
  5. 内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变; font-size
  6. 页面渲染初始化;onload
  7. 浏览器窗口尺寸改变——resize事件发生时;
  8. 激活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

参考资料:

从Chrome源码看浏览器如何计算css

你真的了解回流和重绘吗