乐趣区

关于javascript:分时函数

有时候,咱们可能会在短时间外向页面中增加大量的节点,这样会让浏览器吃不消,导致浏览器卡顿甚至假死。

事实上,网页动画的每一帧(frame)都是一次从新渲染。每秒低于 24 帧的动画,人眼就能感触到进展。个别的网页动画,须要达到每秒 30 帧到 60 帧的频率,能力比拟晦涩。如果能达到每秒 70 帧甚至 80 帧,就会极其晦涩。大多数显示器的刷新频率是 60Hz,为了与零碎统一,以及节俭电力,浏览器会主动依照这个频率,刷新动画(如果能够做到的话)。所以,如果网页动画可能做到每秒 60 帧,就会跟显示器同步刷新,达到最佳的视觉效果。这意味着,一秒之内进行 60 次从新渲染,每次从新渲染的工夫不能超过 16.67 毫秒。

以下代码演示一次性向页面中增加大量 dom 节点:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <style type="text/css">
        #wrap {
            display: flex;
            flex-wrap: wrap;
            width: 100%;
        }
        #wrap div {
            margin: 10px 20px;
            color: #f00;
        }
    </style>
</head>
<body>
    <div id="wrap"></div>
</body>
</html>
export function timeTest1() {const ary = []
    const wrap = document.getElementById('wrap')
    Array.from({length: 100000}).forEach((i, index) => ary.push(index))
    // 创立 dom 节点并插入到 id 为 wrap 的元素中
    const createEle = (text) => {const div = document.createElement('div')
        div.innerHTML = text
        wrap.appendChild(div)
    }
    // 渲染函数
    const renderList = function(ary, fn) {
        // 记录开始工夫
        const startTime = Date.now()
        // 遍历数组,执行回调
        ary.forEach(item =>  fn(item) )
        // 记录完结工夫
        const endTime = Date.now()
        console.log('耗时', endTime - startTime)
    }
    // 执行渲染函数
    renderList(ary, createEle)
}

这里一次性向网页中增加了 10 万个节点,耗时 729 毫秒,尽管不到 1 秒的工夫就渲染实现,然而在刚开始渲染的一瞬间页面会有显著卡顿景象。
应用分时函数:

export function timeTest2() {const ary = []
    const wrap = document.getElementById('wrap')
    Array.from({length: 100000}).forEach((i, index) => ary.push(index))
    // 创立 dom 节点并插入到 id 为 wrap 的元素中
    const createEle = (text) => {const div = document.createElement('div')
        div.innerHTML = text
        wrap.appendChild(div)
    }
    const renderList = timeChunk(ary, createEle, 15000, 16)
    renderList()}

/**
 * 分时函数
 * @param {Array} ary 遍历渲染的数组
 * @param {Function} fn 数组每一项的回调函数
 * @param {Number} count 一个工夫片段内遍历数组多少项
 * @param {Number} time 工夫片段
 * @returns {Function} 返回一个函数,造成闭包
 */
export const timeChunk = function(ary, fn, count, time = 100) {
    let t = null
    // 依据 count 将 ary 中的每一项顺次取出执行 fn,直至 ary 长度为 0
    const start = function() {for (var i = 0; i < Math.min(count || 1, ary.length); i++) {const aryItem = ary.shift()
            fn(aryItem)
        }
    }
    // 造成一个闭包
    return function() {const timeStart = Date.now()
        // time 毫秒执行一下 start 函数,直至 ary 长度为 0,革除定时器 t
        t = setInterval(function() {if (ary.length === 0) {const timeEnd = Date.now()
                console.log('分时函数耗时', timeEnd - timeStart)
                return clearInterval(t)
            }
            start()}, time)
    }
}

从后果来看,我设置每距离 16 毫秒向页面增加 15000 个节点,总共有 100000 个节点,共用时 7612 毫秒。渲染残缺个列表所需工夫的确比不应用分时函数长了很多,但页面相对而言也更晦涩一些。

分时函数的性能是能够将一个长列表的渲染分批执行,每批渲染多少个元素(count)和渲染每批所需的工夫(time)是能够通过参数传递的。这样做的益处是:咱们将一个大工作变成多个小工作,咱们能够管制多长时间外向页面增加多少个节点,从而达到不阻塞浏览器渲染的成果;这样做的毛病是:整个长列表渲染实现所需的工夫会更长。

内容参考:《JavaScript 设计模式与开发实际》
网页性能治理详解 作者:阮一峰

退出移动版