大量数据的渲染优化-分页渲染方案

2023-12-28 13:35:58

相信有一道耳熟能详的题目,如果前端获取到了 10w 条数据,应该怎么渲染?本文就以此为例,来进行切入,解析大量数据渲染的方案

直接渲染

  1. 样式代码比较简单,我就不做阐述了,展示一下直接渲染的js代码,如下:

    const data = []
    
    for (let i = 0; i < 100000; i++) {
    	data.push({ id: i, name: `Item ${i}` })
    }
    
    const box = document.querySelector('.box')
    const btn = document.querySelector('.btn')
    
    btn.addEventListener('click', function () {
    	render(data)
    })
    
    function render(list) {
    	const htmlStr = list
    		.map(item => {
    			return `<div class="item">
            <span>${item.id}</span>
            <span>${item.name}</span>
          </div>`
    		})
    		.join('')
    	box.innerHTML = htmlStr
    }
    
  2. 看一下结果:

    在这里插入图片描述

  3. 从点击渲染开始,经过了一段较长的时间才渲染出来了dom,这还只是一些简单的dom结构,如果是一些复杂的dom的结构,那相信时间会更加的漫长,而实际上我们通常是不需要一次性直接看到全部的数据的,只需要满足最开始展示在容器范围内的数据即可,或者适当多出一些,所以我们不难想到,只要我分开渲染,每次渲染一部分,虽然总时间变长,但是从体验上来说,会好上很多

数据的拆分

  1. 现在我们拿到的数据是一个10w个数据的一维数组,但是如果我们需要每次渲染一部分的话,那这样的数据用起来可能就不是那么的舒服,所以我们可以转变一下思路,将其作为二维数组,[[1-10], [11-20]…]例如这样,就比较适合我们进行数据的操作了

  2. 处理结果如下:

    image-20231227173448308

  3. 可以看到,处理的时间还是非常短的,目前我所使用的机器配置还是比较低的,所以不用担心这些数据处理的损耗,如果是这一点都想省去一点的话,就可以每次获取一部分就渲染一部分,再次拿取下一部分在渲染,我这里为了方便,就直接处理了

使用定时器分页渲染

  1. 在完成这个之前,我们首先需要对 render 这个渲染函数进行改造,如下:

    function render(list) {
    	const fragment = document.createDocumentFragment()
    	list.forEach(item => {
    		const div = document.createElement('div')
    		div.textContent = `${item.id}-${item.name}`
    		div.classList.add('item')
    		fragment.appendChild(div)
    	})
    	box.appendChild(fragment)
    }
    
  2. 现在我们只需要一个函数,来帮助我们完成重复执行 render 函数即可,如下:

    function exec() {
        // 边界判断
    	if (index >= renderList.length) return
        // 使用定时器主要是进入一个异步任务,不阻碍主线程的渲染
    	setTimeout(() => {
    		render(renderList[index])
    		index++
            // 再次调用
    		exec()
    	}, 0)
    }
    
  3. 函数准备完毕之后,我们看一下整体的代码,如下:

    const data = []
    
    for (let i = 0; i < 100000; i++) {
    	data.push({ id: i, name: `Item ${i}` })
    }
    
    const renderList = []
    // 处理数据
    function cutChuck(list, size) {
    	for (let i = 0; i < list.length; i += size) {
    		renderList.push(list.slice(i, i + size))
    	}
    }
    cutChuck(data, 10)
    
    const box = document.querySelector('.box')
    const btn = document.querySelector('.btn')
    
    let index = 0
    
    function exec() {
    	if (index >= renderList.length) return
    	setTimeout(() => {
    		render(renderList[index])
    		index++
    		exec()
    	}, 0)
    }
    
    btn.addEventListener('click', function () {
    	exec()
    })
    
    function render(list) {
    	const fragment = document.createDocumentFragment()
    	list.forEach(item => {
    		const div = document.createElement('div')
    		div.textContent = `${item.id}-${item.name}`
    		div.classList.add('item')
    		fragment.appendChild(div)
    	})
    	box.appendChild(fragment)
    }
    
  4. 执行效果如图:

    在这里插入图片描述

  5. 此时就可以看到,渲染就不会出现一开始那样的长时间的卡顿或白屏,而且滚动条还在自动往上滑动,就可以表示还在不停的渲染

  6. 当然如果你的案例觉得卡顿的话,可以去使用 requestAnimationFrame 来减少页面reflow的次数,提升性能,使用也是非常简单的,只是把通过 setTimeout 调用更换一下,具体的分析后续有时间会放在另一篇文章中

文章来源:https://blog.csdn.net/qq_53109172/article/details/135266367
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。