分片加载超长列表渲染

2,981 阅读2分钟

很早以前就有面试问超长列表如何优化,那时候觉得一般前端不会有这种功能,有也是分页,也就没有去关注。今天分享一个超长列表渲染的优化方法,分片加载,现在几乎也都不会用了,但是还是要知道这个东西。

先明确eventloop的机制,js是单线程的,当你第一个主线程执行过程中,遇到宏任务就放到宏任务队列,遇到微任务就放到微任务队列,这一次主线程执行完了之后,会把微任务队列清空,接着GUI渲染页面,然后再去宏任务队列里面把到时的宏任务提取出来从主线程执行,一直循环。

先明确,页面渲染是在清空微任务之后:

<ul id="wrap"></ul>

<script>

    let time;

Promise.resolve().then(res => {

    for (let i = 0; i < 10000000000; i++) {}

    console.log('微任务执行完毕', new Date() - time);//微任务执行完毕 11124

})

document.getElementById('wrap').innerHTML = '页面渲染了';

time = new Date();

console.log('页面渲染语句执行');

</script>

接着看看渲染十万条数据页面使用的时间和js执行的时间:

<ul id="wrap"></ul>

<script>

    let total = 100000;

    let time = new Date();

    for (let i = 0; i < total; i++) {

        let li = document.createElement('li');

        li.innerHTML = i;

        document.getElementById('wrap').appendChild(li);

    }

    console.log('js执行时间', new Date() - time);//js执行时间 710

    setTimeout(function () {

        console.log('页面渲染时间', new Date() - time);//页面渲染时间 4771

    })

</script>

Js执行很快,但是页面渲染要将近5秒,如果是一百万条数据测试了一下将近一分钟。所以我们要分片渲染。

分片渲染:

<ul id="wrap"></ul>

<script>

    let total = 100000;

    let time = new Date();

    let index = 0;

    let id = 0;

    function loadList() {

        index += 50;

        if(index < total){

           setTimeout(function () {

               for (let i = 0; i < 50; i++) {

                   let li = document.createElement('li');

                   li.innerHTML = id++;

                   document.getElementById('wrap').appendChild(li);

               }

               loadList();

           })

        }

    }

    loadList()

</script>

分片渲染用的时间会比没分片长,好处就是不需要等所有渲染出来,可以直接看到数据,你拖动滚动条,还能看见页面还在加载。

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

虽然分片渲染可以优化,但是这种方法,页面的dom还是过多了,所以这种方法现在也几乎不会用了,一般会使用虚拟列表方法去优化。