在经典的面试题中:”如果后端返回了十万条数据要你插入到页面中,你会怎么处理?”
除了像 useVirtualList 这样的虚拟列表来处理外,我们还可以通过 时间分片 来处理
通过 setTimeout
直接上一个例子:
 
<!--
 * @Author: Jolyne
 * @Date: 2023-09-22 15:45:45
 * @LastEditTime: 2023-09-22 15:47:24
 * @LastEditors: Jolyne
 * @Description: 
-->
<!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>十万数据渲染</title>
</head>
<body>
  <ul id="list-container"></ul>
  <script>
    const oListContainer = document.getElementById('list-container')
    const fetchData = () => {
      return new Promise(resolve => {
        const response = {
          code: 0,
          msg: 'success',
          data: [],
        }
        for (let i = 0; i < 100000; i++) {
          response.data.push(`content-${i + 1}`)
        }
        setTimeout(() => {
          resolve(response)
        }, 100)
      })
    }
    // 模拟请求后端接口返回十万条数据
    // 渲染 total 条数据中的第 page 页,每页 pageCount 条数据
    const renderData = (data, total, page, pageCount) => {
      // base case -- total 为 0 时没有数据要渲染 不再递归调用
      if (total <= 0) return
      // total 比 pageCount 少时只渲染 total 条数据
      pageCount = Math.min(pageCount, total)
      setTimeout(() => {
        const startIdx = page * pageCount
        const endIdx = startIdx + pageCount
        const dataList = data.slice(startIdx, endIdx)
        // 将 pageCount 条数据插入到容器中
        for (let i = 0; i < pageCount; i++) {
          const oItem = document.createElement('li')
          oItem.innerText = dataList[i]
          oListContainer.appendChild(oItem)
        }
        renderData(data, total - pageCount, page + 1, pageCount)
      }, 0)
    }
    fetchData().then(res => {
      renderData(res.data, res.data.length, 0, 200)
    })
  </script>
</body>
</html>
 
上面的例子中,我们使用了 setTimeout,在每一次宏任务中插入一页数据,然后设置多个这样地宏任务,直到把所有数据都插入为止。
但是很明显能看到的问题是,快速拖动滚动条时,数据列表中会有闪烁的情况
这是因为:
所以,我们改善一下,通过 requestAnimationFrame 来处理
通过 requestAnimationFrame
<!--
 * @Author: Jolyne
 * @Date: 2023-09-22 15:45:45
 * @LastEditTime: 2023-09-22 15:47:24
 * @LastEditors: Jolyne
 * @Description: 
-->
<!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>直接插入十万条数据</title>
</head>
<body>
  <ul id="list-container"></ul>
  <script>
    const oListContainer = document.getElementById('list-container')
    const fetchData = () => {
      return new Promise(resolve => {
        const response = {
          code: 0,
          msg: 'success',
          data: [],
        }
        for (let i = 0; i < 100000; i++) {
          response.data.push(`content-${i + 1}`)
        }
        setTimeout(() => {
          resolve(response)
        }, 100)
      })
    }
    // 模拟请求后端接口返回十万条数据
    // 渲染 total 条数据中的第 page 页,每页 pageCount 条数据
    const renderData = (data, total, page, pageCount) => {
      // base case -- total 为 0 时没有数据要渲染 不再递归调用
      if (total <= 0) return
      // total 比 pageCount 少时只渲染 total 条数据
      pageCount = Math.min(pageCount, total)
      requestAnimationFrame(() => {
        const startIdx = page * pageCount
        const endIdx = startIdx + pageCount
        const dataList = data.slice(startIdx, endIdx)
        // 将 pageCount 条数据插入到容器中
        for (let i = 0; i < pageCount; i++) {
          const oItem = document.createElement('li')
          oItem.innerText = dataList[i]
          oListContainer.appendChild(oItem)
        }
        renderData(data, total - pageCount, page + 1, pageCount)
      })
    }
    fetchData().then(res => {
      renderData(res.data, res.data.length, 0, 200)
    })
  </script>
</body>
</html>
 
 
 
很明显,闪烁的问题被解决了
这是因为:










