前端处理后端传的10w条数据

1. 这道题在考什么?

对于性能优化的处理方案

对于前端渲染机制的了解

极端情况下的处理及知识领域的广度

  1. 常规处理

const renderList = async () => {
    console.time('列表时间')
    const list = await getList();

    list.forEach( item => {
        const div = document.createElement('div')
        div.className = 'flex'
        div.innerHTML = `${item.text}`
        container.appendChild(div)
    });
    console.timeEnd('列表时间')
}
renderList()
这种方案就是简单粗暴的循环渲染
此方案耗时大概是 13s
这种做法当然是不可取的,等到天都黑了,估计都出不来

2. 优化的第一种方式—— 前端分页

const renderList = async () => {
    console.time('列表时间')
    const list = await getList();

    const total = list.length;

    const page = 0;
    const limit = 200;
    // 总页数
    const totalPage = Math.ceil(total / limit);   // Math.ceil 1.1 => 2

    const render = (page) => {
        if(page >= totalPage) return
        //写一个定时器
        setTimeout(() => {
            for(let i = page * limit; i < page * limit + limit; i++){
                const item = list[i];
                const div = document.createElement('div')
                div.className = 'flex'
                div.innerHTML = `${item.text}`
                container.appendChild(div)
            }
            render(page + 1)
        }, 0)
    }
    render(page);
    console.timeEnd('列表时间')
}
renderList()
思路是把十万条数据分成 10w / 200页,再加上setTimeout,每次渲染一页,速度得到了大幅度提升。
方案耗时:不到 1s 搞定

再次进行优化使用 requestAnimationFrame

window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。
该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行;
若你想在浏览器下次重绘之前继续更新下一帧动画,
那么回调函数自身必须再次调用window.requestAnimationFrame()
const renderList = async () => {
    console.time('列表时间')
    const list = await getList();

    const total = list.length;

    const page = 0;
    const limit = 200;
    // 总页数
    const totalPage = Math.ceil(total / limit);   // Math.ceil 1.1 => 2

    const render = (page) => {
        if(page >= totalPage) return

        requestAnimationFrame(() => {
            for(let i = page * limit; i < page * limit + limit; i++){
                const item = list[i];
                const div = document.createElement('div')
                div.className = 'flex'
                div.innerHTML = `${item.text}`
                container.appendChild(div)
            }
            render(page + 1)
        })
    }
    render(page);
    console.timeEnd('列表时间')
}

renderList()
使用 requestAnimationFrame 代替 setTimeout,减少了重排的次数,极大提高了性能
  • 最佳方案使用文档碎片 document.createDocumentFragment()

DocumentFragments —— 文档碎片
DocumentFragments 是DOM节点。它们不是主DOM树的一部分。
通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。
在DOM树中,文档片段被其所有的子元素所代替。因为文档片段存在于内存中,并不在DOM树中。
所以将子元素插入到文档片段时不会引起【页面回流】(对元素位置和几何上的计算)。
因此,使用文档片段通常会带来更好的性能。
const renderList = async () => {
    console.time('列表时间')
    const list = await getList();

    const total = list.length;

    const page = 0;
    const limit = 200;
    // 总页数
    const totalPage = Math.ceil(total / limit);   // Math.ceil 1.1 => 2

    const render = (page) => {
        if(page >= totalPage) return

        requestAnimationFrame(() => {
            const fragment = document.createDocumentFragment()
            // 文档碎片 => dom节点 不是在dom树上一部分
            // N次追加 => 1次
            for(let i = page * limit; i < page * limit + limit; i++){
                const item = list[i];
                const div = document.createElement('div')
                div.className = 'flex'
                div.innerHTML = `${item.text}`
                fragment.appendChild(div)
                // container.appendChild(div)
            }
            container.appendChild(fragment)
            render(page + 1)
        })
    }
    render(page);
    console.timeEnd('列表时间')
}

renderList()
这里的优化点主要是:之前是创建一个div就追加一次:
div.innerHTML = ${item.text}
container.appendChild(div)
现在改成一次性追加,极大的提高了性能。
container.appendChild(fragment)

你可能感兴趣的:(前端)