【JS提升】DOM/CSS/渲染树、解析与加载、回流与重绘

一、DOM/CSS/渲染树

(1)DOM树构建(深度优先解析原则)

浏览器引擎把html结构变成一个又一个dom节点,然后排列形成一个树形结构,这个树形结构就是dom树

dom树的构建就是对元素节点的解析

(2)CSS树构建(样式结构体)

浏览器引擎通过查看css样式对应元素与其它元素之间的层次关系,然后通过这个层次关系来构建css树

与dom树一样也是满足深度优先的原则

特点

会忽略浏览器不兼容、不能识别、无效的样式(比如用删除线划掉的样式)

(3)渲染树构建(renderTree)

渲染树是dom树结合css树形成的一个新的树。当renderTree构建完毕后,浏览器会根据它去绘制页面。

特点

  1. 渲染树每个节点都有自己的样式。
  2. 不包含隐藏节点(display: none)、不需要绘制的节点(html/head/style/title),这些节点在renderTree形成之前就会被剔除。
  3. visibility: hidden相对应的节点是包含在渲染树上的,因为它影响布局(layout)。
  4. 渲染树上的每一个节点都会被当成一个盒子,具有内容填充、边距、边框、位置、大小及其它样式。

二、解析与加载

(1)解析与加载

概念

页面渲染机制主要分为解析加载

解析:解析就是把整个html结构变成一个又一个的节点,然后挂在dom树上面形成dom树

加载:加载就是加载html里面的资源

先有解析,再有加载,而解析和加载的过程是一个异步的过程

示例1

<img src="baidu.png" alt="" />

先是解析img标签变成节点再挂到dom树上,图片的加载并不在解析的过程当中,加载在当前节点解析完成之后

示例2

在构建dom树的时候,span和div会挂在dom树上面吗?

<span style="display: none"></span>

var oDiv = document.createElement('div');
document.body.appendChild(oDiv);

会,因为节点树不会管样式,只管dom节点,动态创建也是dom节点

三、回流与重绘

(1)回流与重绘

概念

当JS对页面的节点进行操作时,就会产生回流或重绘

回流/重排(reflow): 因为节点的尺寸、布局、显示(display: none/block)改变这些属性改变的时候,渲染树中的一部分或者全部需要重新构建,这种重新构建的现象就是回流。一个页面至少有一次回流。

重绘(repaint):
回流时,浏览器会看重新构建受影响部分的渲染树,只要渲染树一被改变或重新构建,就一定会引起重绘;

回流完成后,浏览器根据新的渲染树重新绘制回流影响的部分节点,这个重新绘制的过程叫做重绘;

不改变节点原有的尺寸、布局、显示、增删,就只会引起重绘。

小结

回流一定会引起重绘,重绘不一定是回流产生的后续反应。因为只要不是改变物理的位置、尺寸、显示,就不会引起回流。

引起回流的因素:

  1. DOM节点增加、删除
  2. DOM节点位置变化
  3. 元素的尺寸、边距、填充(文字、图片)、边框、宽高
  4. DOM节点display显示与否,不包含visibility
  5. 页面渲染初始化(第一次加载页面)
  6. 浏览器窗口尺寸变化(resize)
  7. 向浏览器请求某些样式信息(offset、scroll、client、width/height、getComputedStyle、currentStyle)

除开以上几个因素以外只会引起重绘。

dom操作之所以消耗性能,就是因为容易引起回流,所以在做dom操作优化的时候就是以减少回流次数为依据来进行优化的,之所以需要缓存、文档碎片就是为了减少回流次数。

示例1

<div class="box">我是一个孤独的盒子</div>
<script>
    // 专给一个节点增加样式就可以这样缓存
    var oBoxStyle = document.getElementsByClassName('box')[0].style; //点语法是消耗性能的,这里缓存可以优化性能
    // 回流 + 重绘
    oBoxStyle.width = '200px';
    // 回流 + 重绘
    oBoxStyle.height = '200px';
    // 回流 + 重绘
    oBoxStyle.margin = '20px';
    // 重绘
    oBoxStyle.backgroundColor = 'green';
    // 回流 + 重绘
    oBoxStyle.border = '5px solid orange';
    // 重绘
    oBoxStyle.color = '#fff';
    // 回流 + 重绘
    oBoxStyle.fontSize = '30px';
</script>

之所以不建议一行一行写,就是因为这样写容易引起回流

示例2

<div class="box">我是一个孤独的盒子</div>
<script>
    var h1 = document.createElement('h1');
    h1.innerHTML = '我是一个孤独的标题';
    document.body.appendChild(h1)
</script>

h1追加在box后面只引起一次回流与重绘,但是如果追加在box前面的话,整个body里面的元素都会重新构建。所以要尽量避免插入在最前面。

在写dom的时候要考虑两个问题:

  1. 回流的问题;
  2. 回流所涉及到的节点数(一个父节点重新构建,里面的所有子节点都会重新构建)

(2)队列策略

概念

它是浏览器优化性能的一种机制。

原理

浏览器引擎里面有一个存放操作的队列,只要一加载页面就会看有多少会引起回流与重绘的操作,然后按照顺序放入队列,放到了一定的数量或时间间隔以后批量的处理,处理完毕以后清空队列。

TIP:批处理只会引起一次回流与重绘。

当向浏览器请求某些样式信息(offset、scroll、client、width/height)的时候,就会清空队列单独计算,防止影响样式的取值。

(3)减少回流次数和影响规模

1、静态添加样式属性-css类名

<head>
    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: #000;
        }

        div.active {
            width: 200px;
            height: 200px;
            background-color: green;
            border: 5px solid orange;
        }
    </style>
</head>

<body>
    <div></div>
    <script>
        var oDiv = document.getElementsByTagName('div')[0];

        oDiv.onmouseover = function () {
            oDiv.className = 'active';
            // this.style.width = '200px';
            // this.style.height = '200px';
            // this.style.backgroundColor = 'green';
            // this.style.border = '5px solid orange';
        }
    </script>
</body>

加类名属于批量处理,只会引起一次回流与重绘

2、动态添加样式属性-cssText

<head>
    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: #000;
        }

        div.active {
            width: 200px;
            height: 200px;
            background-color: green;
            border: 5px solid orange;
        }
    </style>
</head>

<body>
    <div></div>
    <script>
        var oDiv = document.getElementsByTagName('div')[0],
            width = 200,
            height = 200,
            backgroundColor = 'green',
            border = '5px solid orange';

        oDiv.onmouseover = function () {
            oDiv.style.cssText = '\
            width:'+ width + 'px;\
            height:'+ height + 'px;\
            background-color:' + backgroundColor + ';\
            border: '+ border + ';\
            ';
        }
    </script>
</body>

3、文档碎片

创建10个盒子

<head>
    <style>
        .box {
            width: 100px;
            height: 100px;
            margin-bottom: 10px;
            background-color: #000;
        }
    </style>
</head>

<body>
    <script>
        for (var i = 0; i < 10; i++) {
            var oDiv = document.createElement('div');
            oDiv.className = 'box';
            document.body.appendChild(oDiv);
        }
    </script>
</body>

这样写回流了10次

<head>
    <style>
        .box {
            width: 100px;
            height: 100px;
            margin-bottom: 10px;
            background-color: #000;
        }
    </style>
</head>

<body>
    <script>
        var oFragment = document.createDocumentFragment();

        for (var i = 0; i < 10; i++) {
            var oDiv = document.createElement('div');
            oDiv.className = 'box';
            oFragment.appendChild(oDiv);
        }

        document.body.appendChild(oFragment);
    </script>
</body>

用文档碎片就只回流了1次

4、display技巧

<head>
    <style>
        .box {
            width: 100px;
            height: 100px;
            margin-bottom: 10px;
            background-color: #000;
        }
    </style>
</head>

<body>
    <div class="box"></div>
    <script>
        var oBox = document.getElementsByClassName('box')[0],
            oBoxStyle = oBox.style;

        oBox.onmouseover = function () {
            oBoxStyle.display = 'none';
            oBoxStyle.width = '200px';
            oBoxStyle.height = '200px';
            oBoxStyle.backgroundColor = 'green';
            oBoxStyle.border = '5px solid orange';
            oBoxStyle.display = 'block';
        }
    </script>
</body>

因为渲染树不包括display:none的节点,所以可以给节点隐藏,改变完了以后再显示,这个就回流2次,重绘2次

(4)注意事项

1、获取样式信息时一定要缓存

div.style.left = div.offsetLeft + 10 + 'px';

在使用offset、client、scroll、width、height、getComputedStyle、currentStyle的时候,一定不能像上面那样写,否则每做一次就会产生回流与重绘,很消耗性能。

正确写法:

var oLeft = div.offsetLeft;

div.style.left = oLeft + 10 + 'px';

这样写每次请求样式信息的时候,就直接用的变量,就不会产生回流。

TIP:如果是动态的改变,比如每10秒修改一次,就只能去获取,获取的方法最好用getStyles,因为它的封装做了优化处理,性能比offset好很多。

2、动画元素一定要绝对定位

在这里插入图片描述

因为绝对定位脱离了文档流,新建一个定位层,所有元素产生了回流都不会影响到父级。

如果用margin来操作动画元素,每做一次整个父级都会引起回流与重绘(塌陷)。

3、尽量不要使用table布局

table回流的代价很大,因为table具有弹性性质,而且table本身具备内边距,这个内边距和普通的内边距不一样,它是cellpadding,它对回流的影响相当的大。

做表格如果没有特定的需求尽量不要用table来做,可以用ul、li来做。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
重绘和重排是CSS渲染过程中的两个重要概念。 重绘(Repaint)指的是当元素的样式发生变化,但不影响其布局时,浏览器会将新样式应用到元素上,重新绘制元素的外观。重绘的开销相对较小,不会引起布局的变化。 而重排(Reflow)指的是当页面布局发生变化时,例如修改了元素的尺寸、位置、内容等,浏览器会重新计算并更新元素的几何属性(如大小、位置),然后重新布局页面。重排的开销相对较大,因为它涉及到整个页面或部分页面的重新渲染重绘和重排的区别在于是否引起布局的变化。重绘只会重新绘制元素的外观,而不会影响其周围元素的布局;而重排会导致整个渲染的重新构建和布局。 在性能优化方面,我们通常要尽量减少重排和重绘的次数,因为它们会消耗大量的计算资源。一些常见的优化方法包括: 1. 使用 CSS3 动画或过渡代替 JavaScript 实现的动画效果,因为后者可能会导致频繁的重排和重绘; 2. 使用类似 flexbox 和 grid 等布局技术,可以减少页面布局的复杂性,降低重排和重绘的次数; 3. 避免频繁访问会引起重排和重绘的属性,例如 offsetTop、offsetLeft、scrollTop、clientWidth 等; 4. 批量更新样式或布局,可以使用 CSS 类名的方式一次性修改多个元素的样式,而不是逐个修改; 5. 将需要执行多次重排的 DOM 操作尽量合并为一次,使用文档片段(DocumentFragment)进行缓存。 通过合理优化和减少重排和重绘的次数,可以提升页面的性能和响应速度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值