有时候,咱们可能会在短时间外向页面中增加大量的节点,这样会让浏览器吃不消,导致浏览器卡顿甚至假死。
事实上,网页动画的每一帧(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 设计模式与开发实际》
网页性能治理详解 作者:阮一峰