超长列表渲染性能优化

390 阅读2分钟
  • 分片渲染(通过浏览器事件环机制,分割渲染时间)
  • 虚拟列表(只渲染可视区域)

分片渲染

我们知道 Event Loop 的机制,js 主线程是单线程的,当第一个主线程执行过程中,遇到宏任务就放到宏任务队列,遇到微任务就放到微任务队列,该次主线程执行完了之后,会把微任务队列先清空,紧接着 GUI渲染进程渲染页面,(明确页面渲染都是在清空微任务之后)然后再去宏任务队列里面把已经到时的宏任务提取出来放到主线程中执行,这样不断的循环这个过程。

// 分片 + 文档碎片 + requestAnimationFrame 渲染

let total = 10000;
let index = 0; // 偏移量
let id = 0; // 递增内容

function render() {
    index += 20;
    // 分片渲染,因为setTimeout,requestAnimationFrame 定时器是一个宏任务,会等待UI渲染完成后在执行
    // 这里先渲染20个 li 后,等待完成在,继续渲染 20 个
    if (index <= total) {
        requestAnimationFrame(() => {
            let fragment = document.createDocumentFragment();
            for (let i = 0; i < 20; i++) {
                let li = document.createElement('li');
                li.innerHTML = id++;
                fragment.appendChild(li);
            }
            document.getElementById('container').appendChild(fragment);
            render();
        })
    }

}

console.time('timer')
render();
console.timeEnd('timer')

注意: 分片渲染所用的时间会比没分片长,

  • 优点:不需要等所有的元素都渲染出来,用户就可以直接看到页面数据,
  • 缺点:当在拖动滚动条时,还能看见页面还在不断加载。

针对新版本浏览器对页面渲染做了优化,会等js执行完毕一次性更新页面,不会在 for 循环的时候执行一条插入一条,如果为了兼容 ie 这个老顽固浏览器,要创建文档碎片(document.createDocumentFragment())进行渲染。也可以用requestAnimationFrame 这个宏任务替代定时器,性能会稍微好一些。

因此:分片渲染,当页面的 dom 过多了,也会影响用户体验,这种方法也不是很友好,现在一般使用虚拟列表方法优化。

虚拟列表

虚拟滚动,指根据容器可视区域的列表的容量,监听用于滑动或者滚动事件,动态截取长列表数据中的部分数据渲染到可视区域显示出来,动态使用空白占位填充容器的上下滚动区域的内容,模拟实际的原生滚动效果

虚拟滚动.jpg

针对于某些页面,如果展示很多的数据,那么页面就会产生很多的 HTML 节点,这样导致消耗很到的浏览器性能,给用户带来不好的体验。

  • 页面等待时间很长,用户不愿意等待太久
  • CUP 计算能力不够,滑动时,还会卡顿
  • GPU 渲染能力不够,页面会跳频
  • 内存容量可能不能,浏览器还会崩溃

思考: 如何优化呢?

  1. 不把很多的数据直接渲染到页面上,截取一部分数据用来填充显示到可视区域内;
  2. 在可视区域外小部分使用空白占位填充;
  3. 监听滚动事件根据滚动的位置动态改变该可视列表和动态改变空白位置的填充。

这样是 虚拟滚动列表 的优化行为.