# 前言

前端需要频繁操作 DOM,为了提高网站性能,需要针对 DOM 操作进行相应优化。

# 使用DOM的引用

比如:我们需要对一组 li 标签进行遍历操作,我们可以用一个变量 length 把这组 li 标签的长度保存下来,然后基于length 进行遍历。

# 使用事件代理批量处理事件

比如:给一系列子元素 li 添加绑定事件,可以通过事件代理来进行优化。

// container:父元素
container.addEventListener('click', function (e) {
    target = e.target || e.srcElement;
    if (target.tagName.toLowerCase() == 'li') {
        // 触发click后要做的事情
    }
}, false)

# 使用文档碎片

DocumentFragments 是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。

因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。

这个方法真是处理批量标签的一大利器,使用它可以节省下很多时间,提高 DOM 性能。

let container = document.getElementById('container');
let wrapper = document.createDocumentFragment; // 使用文档碎片
for (let i = 0; i < data.length; i++) {
    let li = document.createElement('li');
    li.innerText = data[i];
}
wrapper.appendChild(li);
container.appendChild(wrapper);
container.addEventListener('click', function (e) {
    target = e.target || e.srcElement;
    if (target.tagName.toLowerCase() == 'li') {
        // 触发click后要做的事情
    }
}, false)

# 使用innerHTML一次性添加DOM节点

var container = document.getElementById('container');
var sHtml = '';
var len = data.length;
var i, li;
for (i = 0; i < len; i++) {
    sHtml += '<li>' + data[i] + '</li>';
}
container.innerHTMl = sHtml; // 最后一次性添加到页面中,只出发浏览器一次重排

# 通过className来批量修改元素样式

经常有这样的场景,我们需要在js中批量的修改元素的样式,比如

ele.style.width = 100 + 'px';
ele.style.height = 100 + 'px';
ele.style.backgrounfColor = 'red';
ele.style.border = 'solid 1px green';

以上代码会多次出发浏览器重绘和重排,一种好的方式是将需要修改的样式在样式文件中先写好,通过给元素赋值className的形式批量修改样式

.active{
  width: 100px;
  height: 100px;
  backgroung-color: red;
  border: solid 1px green;
}

给元素赋值className

ele.className += ' active'; // 注意前面的空格

# 如何渲染几万条数据并不卡住界面

可以通过 requestAnimationFrame 动画,动态渲染数据。

与setTimeout相比,requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>如何渲染几万条数据并不卡住界面</title>
</head>
<body>
    <ul>
        控件
    </ul>
    <script>
        // 异步任务 避免阻塞
        setTimeout(() => {
            // 插入十万条数据
            const total = 100000
            // 一次插入 20 条,如果觉得性能不好就减少
            const once = 20
            // 渲染数据总共需要几次
            const loopCount = total / once
            // 循环渲染次数
            let countOfRender = 0
            let ul = document.querySelector('ul')
            function add() {
                // 使用文档碎片 优化性能,插入不会造成回流
                const fragment = document.createDocumentFragment()
                for (let i = 0; i < once; i++) {
                    const li = document.createElement('li')
                    li.innerText = Math.floor(Math.random() * total)
                    fragment.appendChild(li)
                }
                ul.appendChild(fragment)
                countOfRender += 1
                loop()
            }
            function loop() {
                // 循环条件判断
                if (countOfRender < loopCount) {
                    window.requestAnimationFrame(add)
                }
            }
            loop()
        }, 0)
    </script>
</body>
</html>

# 参考

更新时间: 5/13/2020, 11:23:00 PM