1. 长列表虚拟滚动
页面渲染太多dom节点会影响性能,对于长列表只渲染可视区域的节点(visibleData),滚动时替换visibleData 数据,实现虚拟滚动。
主要步骤
js:
// 1. data变量保存总数据,visibleData保存可视区域数据;onscroll事件处理函数中;
constructor(props) {
super(props);
let listData = [...new Array(100)].map((item, index) => ({value: `item${index}`));
this.state = {
listData: []
};
this.itemHeight = 40;
this.listData = listData;
}// 2. 节点挂载后,计算可视区域显示的条数,visibleData = data.slice(0, visibleCount);
componentDidMount() {
const visibleCount = Math.ceil(this.el.clientHeight / this.itemHeight);
const visibleData = this.listData.slice(0, visibleCount);
this.setState({ listData: visibleData });
}// 3. onscroll 事件处理函数中,根据scrollTop 获取 startIndex,endIndex,visibleData = data.slice(startIndex, endIndex)
updateVisibleData(scrollTop) {
scrollTop = scrollTop || 0;
const visibleCount = Math.ceil(this.el.clientHeight / this.itemHeight);
const start = Math.floor(scrollTop / this.itemHeight);
const end = start + visibleCount;
let visibleData = this.listData.slice(start, end);
this.setState({ listData: visibleData });
this.content.style.transform = `translate(0, ${start * this.itemHeight}px)`; } onScroll = () => {
const scrollTop = this.el.scrollTop;
this.updateVisibleData(scrollTop);
};html:
<div className="list-view" onScroll={this.onScroll} ref={el => (this.el = el)}>
<div className="list-view-phantom"
style={{ height: `${this.listData.length * 40}px` }}></div>
<div className="list-view-content" ref={el => (this.content = el)}>
<ul>
{
this.state.listData.map(item => (<li className="list-view-item">{item.value}</li>))
}
</ul>
</div>
</div>css:
.list-view {
height: 400px;
overflow: auto;
border: 1px solid #aaa;
position: relative;
}
.list-view-phantom {
position: absolute;
left: 0;
right: 0;
top: 0;
z-index: -1;
}
.list-view-content {
position: absolute;
top: 0;
left: 0;
right: 0;
}
.list-view-item {
padding: 5px;
color: #666;
line-height: 30px;
box-sizing: border-box;
}2. 滚动加载
滚动分页,快到内容底部时异步加载下一页,将异步响应内容append到之前的数据中。注意页面初始化时内容应该足够多到能出现滚动条。
判断是否加载下页的函数:
isLowEnough = () => {
const clientHeight = this.el.clientHeight;
const scrollTop = this.el.scrollTop;
const scrollHeight = this.el.scrollHeight;
return scrollHeight - clientHeight - scrollTop <= 20;
}onscroll事件处理函数
onScroll = e => {
let {listData, total} = this.state;
if (listData.length === total) return;
if (this.isLowEnough()) {
// setTimeout 模拟异步请求
setTimeout(() => {
let newData = [...new Array(10)].map((item, index) => ({ value: `item${index}`}));
listData = listData.concat(newData);
this.setState({listData});
}, 2000)
}
}