Open
Description
最近在研究react的性能相关的问题,就学习了一下react-virtualized, 也顺便看了一下react-tiny-virtual-list的源码。然后根据自己的理解,大致实现了一个简单版的。
他的大致流程如下:
- 只渲染可视区域的信息
- 有两个box,外面那个用来表示可视区,里面那个用来表示是所有的列表项
- 每当滚动条滚动的时候,去计算显示哪些
这里有两种实现方式:- 父元素是position: relative; 子元素absolute;来定位显示元素的位置,动态计算每个元素的位置
- 知道这个列表有多少条,也知道没一条的高度
确定一些元素的大小
- 每一条item的大小
- 可视区域的大小: 这样可以计算需要显示多少条
- 另一个父元素的大小,即列表的大小,需要计算滚动条滚动的距离,来计算应该从哪里开始展示
const wrapperStyle = {...STYLE_WRAPPER, height: height + "px"};
const innerHeight = itemSize * itemCount,
innerStyle = {...STYLE_INNER, height: innerHeight + "px"};
return (
<div ref={this.rootNode} style={wrapperStyle}>
<div style={innerStyle}>
{items}
</div>
</div>
)
这是展示区域的样式,这里已经确定了每一个item的高度和可视区域的高度来实现的。
滚动计算展示的
这里只做了一个简单的例子,用来计算当滚动的时候,该显示的数据。
这里需要注意的是,当endIndex > lastIndex的时候,说明滚动条已经到底了,那么当endIndex 就该等于lastIndex。
而且在滚动的过程中,加了overscanCount,这是为了有所缓存,不至于滚动的时候,页面有所卡顿。
componentDidMount(){
this.rootNode.current.addEventListener('scroll', this.handleScroll);
}
handleScroll = () => {
const rootNode = this.rootNode.current,
{showCount, overscanCount, lastIndex} = this,
{itemSize} = this.props,
scrollTop = rootNode.scrollTop,
startIndex = parseInt(scrollTop/itemSize) + 1;
let endIndex = startIndex + showCount + overscanCount;
if(startIndex + showCount > lastIndex){
endIndex = lastIndex;
}
this.setState({
startIndex: startIndex,
endIndex: endIndex
})
}
性能优化点
- 在滚动事件中增加了
handleScroll = () => {
if(!this.ticking){
requestAnimationFrame(this.caculateStartAndEndIndex);
this.ticking = true;
}
}
- 把每个节点的位置记录下来,这样就不需要每次都动态计算了
getStyle(index){
const style = this.styleCache[index],
{itemSize} = this.props;
if(style){
return style;
}
return this.styleCache[index] = {...STYLE_ITEM, top: index*itemSize + "px", left: 0, height: itemSize+"px"}
}
Metadata
Metadata
Assignees
Labels
No labels