使用场景
项目中碰到这样一种情况,后台接口数据由于太多没法一次传输,因此希望前端在调接口时设置pageIdx和pageSize字段实现懒加载。前端页面上边一部分为选项栏下面一部分为滚动区域,大致的html结构简单抽象如下:
<div className='wrapper'>
<div className='header'></div>
<div className='scroll-body'></div>
</div>
简单的上下布局,外层为wrapper封装类,里面分上下两层,上面header类固定,下面就是要实现滚动懒加载的scroll-body区域。
涉及的问题列表:
- 如何计算scroll-body区域的高度来让scroll区域填充完除了header外的剩余部分,采用的css为:
.scroll-body {
overflow: auto; //设置scroll overflow
-webkit-overflow-scrolling: touch; // 优化移动端滑动体验
}
- 如何判断页面滚动到底部?
解决方案
- 针对第一个问题,可行的办法有两个:
方案1
在componentDidMount后获取对应header的高度,然后用window.innerHeight减去渲染后的header ref高度,公式如下:
scrollHeight = window.innerHeight - this.header.clientHeight
在render方法中需要给header提前设置ref,并且需要新增一个state来控制,具体代码如下:
class Test extents Component{
constructor(props){
super(props);
this.state = {
scrollHeight: 0
}
}
componentDidMount(){
this.setState({
scrollHeight: window.innerHeight - this.header.clientHeight
})
}
render(){
const {scrollHeight} = this.state;
return (
<div className='wrapper'>
<div className='header' ref={ref=>this.header}></div>
<div className='scroll-body' style={{height: scrollHeight}}></div>
</div>
)
}
}
上述方案的优点是可以动态设置高度,减少响应的计算,缺点是必须在render渲染后才能设置。
方案2
提前计算高度,这里有个限制,比如现在移动端中设置header的高度为2rem,需要从rem转换到像素,而获取当前设备的html的fontSize的方法可用的还比较有限,比如document.documentElement.style.fontSize,返回的数值为字符串比如’165px’,还没法直接拿来用,如果没有设置的话返回的是空串”。需要做个处理,具体依据项目中而定。
- 如果判断滚动到底部,触发接口调取下一页的数据?
react有不少现成的实现滚动的组件,在google上搜索会出来很多。不过就实现来说也是比较简单的,这里给出一种可行的方案:
class Test extents Component{
constructor(props){
super(props);
this.state = {
scrollHeight: 0,
hasMore: true,// 判断接口是否还有数据,通过接口设置
dataList:[], // 数据列表
}
}
componentDidMount(){
this.setState({
scrollHeight: window.innerHeight - this.header.clientHeight
})
}
// 处理滚动监听
handleScroll(){
const {hasMore} = this.state;
if(!hasMore){
return;
}
//下面是判断页面滚动到底部的逻辑
if(this.scrollDom.scrollTop + this.scrollDom.clientHeight >= this.scrollDom.scrollHeight){
this.fetchData()
}
}
fetchData(){
// 接口调用数据字段
//传入的参数包括但不限于:pageIndex, pageSize。。。
// 获取后更新的数据包括但不限于:dataList,hasMore。。。
}
render(){
const {scrollHeight} = this.state;
return (
<div className='wrapper'>
<div className='header' ref={ref=>this.header}></div>
<div
ref={body=>this.scrollDom = body}
className='scroll-body'
style={{height: scrollHeight}}
onScroll={this.handleScroll.bind(this)}
>
</div>
</div>
)
}
}