列表虚拟化就是只渲染部分列表项,具体思路:两层容器,外层控制可见区域大小,内层计算一个高度(列表项数量 * 列表项高度
,或列表项高度累加
),内层超高会使外层出现滚动条,监听外层容器滚动事件,通过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% ; }