1、在页面上渲染50万个元素,优化前的效果
可以看到没优化前一次性渲染大量的元素需要很久,有很长一段白屏时间
2、在页面上渲染50万个元素,优化后的效果
可以看到,当点击按钮的时候页面内容瞬间就出来了,并且滚动条还在一直变化,说明还在不断的生成我们需要的元素
3、为什么一次性渲染大量元素页面会卡住
因为循环的内容数据很多,每次循环都要操作真实的dom,会造成主线程的阻塞
4、优化原理和方法
原理:总所周知,浏览器每隔16.6ms渲染一次,现在有一个任务渲染时间比较长,这个任务没有给浏览器留出任何的空间进行渲染
方法:将这个任务进行分片,不要一次性的执行完,执行一段就要等一会,等浏览器渲染,然后再等一下,再等浏览器渲染,这样用户那边就不会看到卡顿了。简单点说,就是在浏览器空闲时间没事做的时候进行这个渲染任务
5、问题
1、下一次分片什么时候开始?
调用浏览器的API,requestIdleCallback 就表示下一个分片的执行时机,这个回调函数的运行时机:在浏览器的渲染帧中,1帧的时间是16.6ms,看上去时间很短,但是实际上这个时间对于计算机来说是非常非常长的,计算机的执行时间是以纳秒为单位的,所以对于计算机来说就非常非常长了,浏览器在这16.6ms里面有很多事情要做,比方说渲染页面,比方说解析dom,比方说js的执行都在这个16.6ms里面。有时候把这些事情做了以后呢,在这16.6ms里面可能还剩了一些时间,那么在这段剩余时间里面呢,就会触发这个回调函数的执行,所以执行时机就确定了:一个渲染帧中空闲时开启分片执行,这样呢不会阻塞渲染器,用户看上去也会比较舒服,不会感觉到卡顿
requestIdleCallback(()=> {})
2、每一次分片渲染多少元素?
剩余时间多就多执行一点,剩余时间少就少执行一点,可以依托于回调函数里面的参数idle,里面有一个方法timeRemaining,可以得到剩余时间单位是毫秒,只要大于0,就表示还有剩余时间
requestIdleCallback(idle => { console.log(idle.timeRemaining()) })
6、全部代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
button {
padding: 5px 10px;
}
</style>
</head>
<body>
<button>向页面插入五十万个元素</button>
</body>
<script>
const btn = document.querySelector('button')
// 填充50万个数据
const datas = new Array(500000).fill(0).map((_, i) => i)
btn.addEventListener('click', function () {
performChunk(datas)
})
function performChunk(datas) {
if (datas.length == 0) return
let i = 0
// 这个函数(_run()) 表示开启下一个分片的执行
function _run() {
// 边界条件的判定 如果大于了数组的长度就表示执行完了
if (i >= datas.length) return
requestIdleCallback(idle => {
// 每一次的while循环结束要么就是没有剩余时间了,要么就是执行完了
while (idle.timeRemaining() > 0 && i < datas.length) {
const div = document.createElement('div')
div.innerText = i
document.body.appendChild(div)
i++
}
_run()
})
}
_run()
}
</script>
</html>