Skip to content

优化大数据列表渲染

更新时间: 9/8/2023字数: 0 字

  1. 分页
  2. 虚拟列表/虚拟滚动
  3. 分片思想 + requestAnimationFrame
  • 将数据切分成小块,交给浏览器渲染。使用requestAnimationFrame来分批次加载图片
  • requestAnimationFrame(callback)window下的一个方法,跟setTimeout用法类似,接收一个回调函数,该回调函数会在浏览器下一次重绘之前执行。只不过requestAnimationFrame的时间间隔是由浏览器自己决定的,浏览器为了保证流畅性,重绘频率会与系统刷新率保持一致,如果刷新率是 60hz,即表示每秒刷新 60 次,也就是每次重绘的间隔时间大概是1000ms/60=16.6msrequestAnimationFrame会跟随浏览器的频率,==每 16.6ms 回调函数就被执行一次,从而避免丢帧卡顿现象==。
  • 原理:每 16.6ms 执行一次源数据切片,并追加到状态中,然后状态再更新视图。页面内容渐进式加载,适用于各种数据结构和布局,不局限于列表形式。

代码示例:

ts
import { useState, useEffect } from 'react'

const mockData = () => {
  return Array(10000).fill({
    name: '张三',
    src: 'https://pic.imgdb.cn/item/667a8251d9c307b7e96626c0.png',
    age: 18
  })
}

export default () => {
  const [data, setData] = useState([])

  const onClick2 = () => {
    const _data = mockData()
    setData(_data)
  }
  const onClick1 = () => {
    const totalData = mockData()
    // 每次渲染的条数
    const pageNum = 100
    // 总页数
    const totalPage = Math.ceil(totalData.length / pageNum)
    let currentPage = 1

    const renderList = () => {
      // 每次分片的数据
      const _renderData = totalData.slice(
        (currentPage - 1) * pageNum,
        currentPage * pageNum
      )
      setData((pre) => [...pre, ..._renderData])
      currentPage++

      if (currentPage <= totalPage) {
        // 递归调用
        window.requestAnimationFrame(renderList)
      }
    }
    renderList()
  }

  useEffect(() => {}, [])

  return (
    <div>
      <h1>大数据列表渲染</h1>
      <button onClick={onClick1}>点击渲染优化</button>
      <button onClick={onClick2}>点击渲染原始</button>
      <ul>
        {data.map((item, index) => {
          return (
            <li key={index}>
              <span>
                {index}
                {item?.name}
              </span>
              :<span>{item?.age}</span>
              <p>
                <img src={item?.src} alt="" width={100} />
              </p>
            </li>
          )
        })}
      </ul>
    </div>
  )
}

Released under the MIT License.