• JavaScript——浏览器的重绘与回流


    一、浏览器渲染过程?

    1. 浏览器把获取到的html代码解析成1个Dom树,html中的每个tag都是Dom树中的1个节点,根节点就是我们常用的document对象 。dom树里面包含了所有的html tag,包括display:none隐藏,还有用JS动态添加的元素等。

    2. 浏览器把所有样式(主要包括css和浏览器的样式设置)解析成样式结构体,在解析的过程中会去掉浏览器不能识别的样式,比如IE会去掉-moz开头的样式.

    3. dom tree和样式结构体(cssom)结合后构建呈现树(render tree),render tree有点类似于dom tree,但其实区别有很大,render tree能识别样式,render tree中每个node都有自己的style,而且render tree不包含隐藏的节点(比如display:none的节点,还有head节点),因为这些节点不会用于呈现,而且不会影响呈现的,所以就不会包含到 render tree中。

    注意: `visibility:hidden隐藏的元素还是会包含到render tree中的,因visibility:hidden 会影响布局(layout),会占有空间。

    1.1 dom树结构,cssom,renderTree

    CSSOM : 样式表解析完毕后,系统会根据选择器将CSS规则添加到对应的哈希表中。这些哈希表包括:ID哈希表、类名称哈希表、标记名哈希表等,还有一种通用哈希表,适合不属于上述类别的规则。如果选择器是ID,规则就会添加到ID表中;如果选择器是类,规则就会添加到类表中。

    注意:此处的cssom(样式结构体)尚有疑问。

    1.2 cssom和dom树怎么匹配结合成renderTree ?

    以dom节点匹配cssom:从本渲染结点开始,判断此结点是否与选择器链表的当前选择器相匹配。如果匹配,判断此选择器与下一个选择器的关系:如果为NONE,表示本选择器是选择器链的最后一个,返回成功;如果关系为AND (比如:#id.class),选择下一个选择器与本渲染结点继续比较;如果关系为CHILD,表示本选择器是下一个选择器的子结点,返回下一个选择器与下一个渲染结点的匹配结果;否则,关系为DESCENDANT,选择器和渲染结点各指向下一个结点,然后将渲染结点继续回溯,直到第一个满足回溯后的选择器的结点,此时将继续判断回溯后的选择器和回溯后的渲染结点是否匹配。

    二、什么是重绘?回流(重排)?

    1. 当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流。每个页面至少需要一次回流,就是在页面第一次加载的时候。
    2. 当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

    注:回流必将引起重绘,而重绘不一定会引起回流。

    三、哪些情况会发生重绘,回流?

    不同的条件下发生重排的范围及程度会不同 :

    1. 页面初始渲染

    2. 改变字体,改变元素尺寸(宽、高、内外边距、边框,改变元素位置):

      • 设置 style 属性的值

      • 激活 CSS 伪类,比如 :hover

      • 操作 class 属性

      css3的某些属性(https://csstriggers.com/ 结合此链接查看哪些属性会触发重排、哪些属性会触发重绘以及哪些属性会触发合成;)

      注意:如果修改属性不影响布局则不会发生重排

    3. 改变元素内容(文本或图片等或比如用户在input框中输入文字)

    4. 添加/删除可见DOM元素(注意:如果是删除本身就display:none的元素不会发生重排;visibility:hidden的元素显示或隐藏不影响重排)

    5. fixed定位的元素,在拖动滚动条的时候会一直回流

    6. 调整窗口大小(Resizing the window)

    7. 计算 offsetWidthoffsetHeight 属性(flush队列)

    浏览器是聪明的,当对以下属性进行操作的时候:

    包括:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle() (currentStyle in IE)

    浏览器不会马上操作它们,而是会先缓存在队列中,有一定时间顺序去执行这些操作,但是在这过程中我们需要去获取在该队列中的属性时,浏览器为取得正确的值就会触发重排。这样就使得浏览器的优化失效了。

    所以,在多次使用这些值时应进行缓存。

    四、如何减少回流,重绘?

    4.1 修改html元素中对应的class名,利用class替换样式

    【代码示例】

    <style type = "text/css" >
    .changeStyle{width: 200px;height: 200px;}
    </style >
    < script type = "text/javascript" >
        $(document).ready(function ()
        {
            var el = $('id');
            //1
            el.css('width', '200px');
            el.css('height', '200px');
            //2
            el.css({
                'width' : '200px;',
                'height' : '200px;'
            });
            //3 
            el.addClass('changeStyle');
        });
    </script >
    

    4.2. csstext(利用cssText属性合并所有改变,然后一次性写入)

    【代码示例】

    var s = document.body.style; 
    s.padding = "2px"; // 回流+重绘
    s.border = "1px solid red"; // 再一次 回流+重绘 
    s.color = "blue"; // 再一次重绘
    s.backgroundColor = "#ccc"; // 再一次 重绘 
    s.fontSize = "14px"; // 再一次 回流+重绘 
    // 添加node,再一次 回流+重绘
    document.body.appendChild(document.createTextNode('abc!));
    
    ----------------------------------------------------------------------------
    
    // 不好的写法
    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;";
    

    4.3 display:none(隐藏元素,应用修改,重新显示)

    【代码示例】

    var list = document.getElementById("list");
    
    list.style.display = 'none’;
    
    appendDataToElement(list,data);
    
    list.style.display = 'block';
    

    4.4 cloneNode (将原始元素拷贝到一个脱离文档的节点中,修改副本,完成后再替换回去; )

    【代码示例】

    var old = document.getElementById("list");
    
    var clone = old.cloneNode(true);
    
    appendDataToElement(clone,data);
    
    old.parentNode.replaceChild(clone,old);
    

    4.5. document.createDocumentFragment();(使用文档片段(document fragment)在当前DOM之外构建一个子树,再插回去)

    【代码示例】

    var fragment = document.createDocumentFragment();
    var list = document.getElementById("list");
    for (var i = 0; i < 10; i++)
    {
        var _li = document.createElement("li");
        _li.onmouseover = function ()
        {
            this.style.backgroundColor = "#22b909";
            this.style.width = "120px";
            this.style.height = "50px";
        }
        _li.onmouseout = function ()
        {
            this.style.backgroundColor = "";
            this.style.width = "100px";
            this.style.height = "40px";
        }
        fragment.appendChild(_li);
    }
    list.appendChild(fragment);
    

    4.6 使用trsansform

    CSS的最终表现分为以下四步:Recalculate Style -> Layout -> Paint Setup and Paint -> Composite Layers

    按照中文的意思大致是 查找并计算样式 -> 排布 -> 绘制 -> 组合层

    由于transform是位于Composite Layers层,而width、left、margin等则是位于Layout层,在Layout层发生的改变必定导致Paint Setup and Paint -> Composite Layers

    所以相对而言使用transform实现的动画效果肯定比使用改变位置(margin-left等)这些更加流畅。

    五、遗留问题

    1. cssom结构

    2. domtree+cssom匹配

    3. 脱离文档流的,body改变是否会受影响

    4. trsansform/translate/opacity等对重绘回流的影响(css3)

    5. 如果回流的操作并没有发生改变,那么是否会发生重绘

    6. flush队列问题

    1,2问题尚不确定喵~


    作者:奇奇要努力
    来源:CSDN
    原文:https://blog.csdn.net/qq_42269433/article/details/81133772

  • 相关阅读:
    ubuntu shell插件
    通过更改服务器解决双系统ubuntu时间+8
    ubuntu安装mysql遇到的问题
    05 面向对象:构造方法&static&继承&方法 &final
    electron 大体结构
    js时间Date对象介绍及解决getTime转换为8点的问题
    Fiddler命令行和HTTP断点调试
    使用HTTP头去绕过WAF(bypasswaf)
    Linux下php5.3.3安装mcrypt扩展
    Error: Cannot find a valid baseurl for repo: epel
  • 原文地址:https://www.cnblogs.com/YooHoeh/p/12098892.html
Copyright © 2020-2023  润新知