下载vue-intersect
npm install vue-intersect --save
html代码
<van-pull-refresh v-model="refreshing" success-text="刷新成功" @refresh="onRefresh">
<van-list
v-model="loading"
:finished="!apiData.pageInfo.hasNextPage"
finished-text="加载完啦"
@load="getFiles"
:error="error"
ref="v_list"
>
<div class="file_item" v-for="(item, index) in fileList" :key="index" :ref="'file_item' + index">
<intersect @enter="intersectFileDay(item, false)" @leave="intersectFileDay(item, true)">
<div>
<div v-if="item.isHidden" :style="{height: item.hiddenViewH + 'px'}"></div>
<div v-else>
<van-sticky :offset-top="listBoxOffsetTop">
<h1>{{item.createTime}}</h1>
</van-sticky>
<van-grid :column-num="4" square :border="false" clickable>
<van-grid-item v-for="(fileItem, fileIndex) in item.list" :key="fileIndex">
<intersect @enter="intersectFileItem(fileItem, false)" @leave="intersectFileItem(fileItem, true)">
<div class="wh100" @click="onShowFileItem(fileItem, index)">
<file-item :fileItem="fileItem" :fileIndex="fileIndex" v-if="!fileItem.isHidden"></file-item>
</div>
</intersect>
</van-grid-item>
</van-grid>
</div>
</div>
</intersect>
</div>
<Loading v-if="!loading" :len="fileList.length" :error="error"></Loading>
</van-list>
</van-pull-refresh>
script代码
import {searchFileApi} from '../../../http/api'
import FileItem from '../../../components/file-library/FileItem'
import Intersect from 'vue-intersect'
export default {
name: 'FileLibrary',
components: {
FileLibraryStatisticsPie,
SearchPopup,
FileItem,
Intersect,
FileInfoPopup
},
data () {
return {
supervene: false,//解决下拉刷新和上拉加载的冲突
fileList: [],
loading: true,
error: false,
refreshing: false,
searchParams: {
isHide: 'visible', //枚举: visible 可见的, hidden 已隐藏
fileFromList: [],
fileTypeList: [],
storeIdList: [],
filename: '',
pageNum: 1,
pageSize: 20,
timePeriod: {
beginTime: null,
endTime: null
}
},
ov: false,//已执行mounted
ol: false,//完成渲染list
om: false//完成获取ref高度
}
},
methods: {
requestOv(error){
this.refreshing = false;
this.loading = false;
this.supervene = false;
this.error = error;
},
getFiles(){
searchFileApi(this.searchParams).then(res=>{
this.searchParams.pageNum ++;
this.fileList = this.fileList.concat(res.data.pageInfo.list);
this.setH();
this.requestOv(false);
this.ol = true;
}).catch(()=>{
this.requestOv(true)
})
},
/**
* 内层每一项
*/
intersectFileItem(fileItem, type){
this.$set(fileItem, 'isHidden', type)
},
/**
* 外层每一列
* @param item
* @param type
*/
intersectFileDay(item, type){
if (this.ov && this.ol && this.om){
this.$set(item, 'isHidden', type);
}
},
setH(){
if (this.ov && this.ol){
setTimeout(()=>{
this.fileList.map((item,index)=>{
this.$set(item, 'hiddenViewH', this.$refs['file_item' + index][0].scrollHeight);
});
this.om = true
},100)
}
}
},
created() {
this.getFiles();
},
mounted(){
this.ov = true;
this.setH()
}
}
思路
使用vue-intersect
请求获取数据list
v-for渲染列表 并把在每一个列表设置一个独立的ref
ref一定要是唯一的(不唯一其实也可以,因为获取的refs是一个数组,但避免出错还是唯一比较好)
保证列表渲染完成后(可以加变量分段判断),通过ref读取每一项列表的高度,存在对应列表每一项中
在触发界面交叉显示函数时设置item显示隐藏属性,
用v-if判断空盒子替代组件,利用ref计算的高度赋值给空盒子,达到高度撑开
intersect下面必须要有一个稳定的div,如果去掉直接是ifelse的组件,会导致一些交叉口方法不执行,或者报错等
这样就可以了,但是划定过快会白屏,然后展现,这个MDN提到过,是html的bug,不过这并不影响什么
intersect支持嵌套,可以尽量的让当前可视页面存在使用的dom,其它均用空div替代
再优化可以将intersect组分大一些
在特殊情况下如几十万条数据量,请考虑使用按钮分页,或者其它解决方法
vue-intersect
vue-intersect是基于 交叉口观察者API开发的,想详细了解请移步官方文档
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API