切换主题
优化大数据列表渲染
更新时间: 9/8/2023字数: 0 字
- 分页
- 虚拟列表/虚拟滚动
- 分片思想 + requestAnimationFrame
- 将数据切分成小块,交给浏览器渲染。使用
requestAnimationFrame
来分批次加载图片 requestAnimationFrame(callback)
是window
下的一个方法,跟setTimeout
用法类似,接收一个回调函数,该回调函数会在浏览器下一次重绘之前执行。只不过requestAnimationFrame
的时间间隔是由浏览器自己决定的,浏览器为了保证流畅性,重绘频率会与系统刷新率保持一致,如果刷新率是 60hz,即表示每秒刷新 60 次,也就是每次重绘的间隔时间大概是1000ms/60=16.6ms
,requestAnimationFrame
会跟随浏览器的频率,==每 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>
)
}