35、js - 面试 - 如何一次渲染10万条数据?

如何一次渲染10万条数据?

面试官想了解:面试者对原生js底层的了解、对渲染机制的理解。

思路:

第一种方案:分页 + setTimeout()

第二种方案:分页 + window.requestAnimationFrame()减少重排

第三种方案:分页 + window.requestAnimationFrame() + document.createDocumentFragment()文档碎片

(方案一到方案三,渲染所需的时间逐渐较少,方案一大概要2秒,后面2个方案所需的时间就更少)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div>如何一次渲染10万条数据?</div>
    <div>面试官想了解:面试者对原生js底层的了解、对渲染机制的理解</div>
    <script>
        // 模拟请求接口数据
        const getData = () => {
            return new Primise((resolve, reject) => {

            });
        };

        // 第一种方案:分页 + setTimeout()
        const renderList1 = async () => {
            console.time('开始分页');

            const data = await getData();
            const total = data.length;
            const page = 0; // 首页默认是0页
            const limit = 10; // 每页限制显示10条
            const totalPage = Math.ceil(total / limit); // 页数需要往上取整
            const render = (page) => {
                // if (page >= totalPage) return; // 如果page的值大于等于totalPage,就不再往下走
                // setTimeout是宏任务,那么就会依次执行任务队列里的任务
                setTimeout(() => {
                    for (let i = page * limit; i < page * limit + limit; i++) {
                        const item = list[i];
                        const div = document.createElement('div');
                        div.innerHTML = `<span>${item.text}</span>`;
                        document.body.appendChild(div);
                    }
                    render(page + 1);
                }, 0);
            }
            render(page);

            console.timeEnd('结束分页');
        }

        // 第二种方案:分页 +  window.requestAnimationFrame()减少重排 
        // 用requestAnimationFrame 代替 setTimeout
        const renderList2 = async () => {
            console.time('开始分页');

            const data = await getData();
            const total = data.length;
            const page = 0; // 首页默认是0页
            const limit = 10; // 每页限制显示10条
            const totalPage = Math.ceil(total / limit); // 页数需要往上取整
            const render = (page) => {
                // requestAnimationFrame减少重排
                requestAnimationFrame(() => {
                    for (let i = page * limit; i < page * limit + limit; i++) {
                        const item = list[i];
                        const div = document.createElement('div');
                        div.innerHTML = `<span>${item.text}</span>`;
                        document.body.appendChild(div);
                    }
                    render(page + 1);
                }, 0);
            }
            render(page);

            console.timeEnd('结束分页');
        }

        // 第三种方案:分页 +  window.requestAnimationFrame() + document.createDocumentFragment()文档碎片
        // document.createDocumentFragment()文档碎片是dom节点,但是并不是主dom树的一部分,
        const renderList3 = async () => {
            console.time('开始分页');

            const data = await getData();
            const total = data.length;
            const page = 0; // 首页默认是0页
            const limit = 10; // 每页限制显示10条
            const totalPage = Math.ceil(total / limit); // 页数需要往上取整
            const render = (page) => {
                // 创建一个文档碎片
                const fragment = document.createDocumentFragment();
                requestAnimationFrame(() => {
                    for (let i = page * limit; i < page * limit + limit; i++) {
                        const item = list[i];
                        const div = document.createElement('div');
                        div.innerHTML = `<span>${item.text}</span>`;
                        // 先塞进文档碎片
                        fragment.appendChild(div);
                    }
                    // 再一次性appendChild
                    document.body.appendChild(fragment);
                    render(page + 1);
                }, 0);
            }
            render(page);

            console.timeEnd('结束分页');
        }

    </script>
</body>
</html>