列表虚拟化就是只渲染部分列表项,具体思路:两层容器,外层控制可见区域大小,内层计算一个高度(列表项数量 * 列表项高度
,或列表项高度累加
),内层超高会使外层出现滚动条,监听外层容器滚动事件,通过event.currentTarget.scrollTop / 列表项高度
得到可视区域的列表项的起始下标start,从start开始遍历列表数据,取若干条数据渲染,注意列表项的key设为列表数据下标,列表项为绝对定位,top通过下标 * 列表项高度
动态计算而来;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| import React from "react"; import "./index.css";
function ListItem({ top, children }) { return <div className="listItem" style={{ top }}>{children}</div> }
const ITEM_HEIGHT = 35;
function VirtualList({ sourceData, itemCount = 8 }) { const [start, setStart] = React.useState(0);
const scrollHandler = React.useCallback((e) => { const newStart = Math.ceil(e.target.scrollTop / ITEM_HEIGHT); if (newStart !== start) { setStart(newStart); } }, [start, setStart]);
const renderedItems = React.useMemo(() => { const end = start + itemCount; const items = []; for (let i = start; i < end && i < sourceData.length; i++) { items.push(<ListItem key={i} top={i * ITEM_HEIGHT}>{sourceData[i]}</ListItem>); } return items; }, [start, itemCount, sourceData]);
return <div className="container"> <div className="listContainer" style={{ height: itemCount * ITEM_HEIGHT }} onScroll={scrollHandler}> <div className="list" style={{ height: sourceData.length * ITEM_HEIGHT }}> {renderedItems} </div> </div> </div> }
export function MockVirtualList() { const LEN = 100000; const data = new Array(LEN); for (let i = 0; i < LEN; i++) { data[i] = `${i}-------${Math.random() * i}`; }
return <VirtualList sourceData={data} /> }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| .container { padding: 50px; }
.listContainer { position: relative; overflow: auto; }
.list { position: relative; width: 100%; }
.listItem { position: absolute; border: 1px solid blue; height: 35px; box-sizing: border-box; padding: 5px; width: 100%; }
|