笔记: 移动端h5 vue3+vue-virtual-scroller加载不定高巨量数据,支持上拉加载,下拉刷新(配合vant PullRefresh)+ 分页加载以及避坑指南

废话(不是): 项目是一个移动端的社区,可以发帖,可发布文字+图片(最多9张),之前直接搭的页面,通过分页加载数据,一次请求10条。后来产品那边反映在ios端会出现发热严重和掉电的情况。(部分原因: 社区首页是一直有兜底数据的,一直滑虽然分页,但dom会越堆越多。还有可能是定时器和监听器没关闭)问了下领导知道有"虚拟滚动/虚拟列表"这么个东西,网上找了几个成熟的库。

vue2:

  1. tangbc/vue-virtual-scroll-list
  2. Taro virtualscroll (组件库里的组件)
  3. Akryum / vue-virtual-scroller

vue3:

  1. reactjser / vue3-virtual-scroll-list (Forked from vue2 vue-virtual-scroll-list )
  2. tnfe / vue3-infinite-list
  3. Akryum / vue-virtual-scroller

项目结构

v-for(item, index) 渲染的dom,循环在父组件,子组件很多逻辑过度依赖循环的item,index,并且子组件Item高度完全不可控。自己造轮子失败,无法确定准确高度,想不通rem怎么动态设置。不知道该如何抽离子组件, Item多页面复用
要虚拟滚动的结构
搜遍全网,这几个基本不适合我这个情况: vue3-virtual-scroll-list 子组件以函数形式传入,无法监听prop和emit事件,官方issue有人提过这个问题,作者建议通过eventBus等其他方式交互,Taro virtualscroll 支持index,并且支持插槽,可以正常写子组件,事件交互都不受影响,但是它的动态高度是写死的 比如官方的例子: getItemSize 是一个有如下语法的函数 : (i: number): number, 通过这个函数可以动态设置元素宽高.

 const getItemSize = (i: number): number => {
      switch (i % 4) {
        case 1:
          return 80;
        case 2:
          return 50;
        case 3:
          return 100;
        default:
          return 200;
      }
    };

官方也在git上给予我答复
在这里插入图片描述
只能放弃,最后用 Akryum / vue-virtual-scroller 的库基本实现需求。

Akryum / vue-virtual-scroller 的坑

如果是ios端H5,在这个库上滑/下滑滚动的时候,屏幕会闪动,并且严重的时候会中断滑动。Android/pc正常,很丝滑。

ios端h5解决方案/避雷:

  1. 浏览issue发现,降低版本可解决问题。我降到了2.0.0-beta.3
  2. 高度一定写死。不能用transform或者动画改变高度的值。不然闪动非常严重
  3. 开启硬件加速 -webkit-overflow-scrolling: touch; ios高版本自带

使用方法:

  1. 安装组件库 (上述问题降低版本至beta4以下)
npm install --save vue-virtual-scroller@next 
  1. 全局引入/按需引入
//Install all the components:

import VueVirtualScroller from 'vue-virtual-scroller'

app.use(VueVirtualScroller)

//Use specific components:

import { RecycleScroller } from 'vue-virtual-scroller'

app.component('RecycleScroller', RecycleScroller)
  1. 引入组件并设置固定高度
 <DynamicScroller
    :items="items"
    :min-item-size="54"
    class="scroller"
  >
    <template v-slot="{ item, index, active }">
      <DynamicScrollerItem
        :item="item"
        :active="active"
        :size-dependencies="[
          item.message,
        ]"
        :data-index="index"
      >
        <div class="avatar">
          <img
            :src="item.avatar"
            :key="item.avatar"
            alt="avatar"
            class="image"
          >
        </div>
        <div class="text">{{ item.message }}</div>
      </DynamicScrollerItem>
    </template>
  </DynamicScroller>
.scroller {
height: calc(100vh - 160px);
-webkit-overflow-scrolling: touch;
}
  1. 实现上拉加载 + loading图标(vant)
    通过给虚拟滚动设置固定id,添加scroll监听
<DynamicScroller
    :items="items"
    :min-item-size="54"
    class="scroller"
    id="virtualScroller"
  >
  ...
  </DynamicScroller>


 let virtualScroller = document.getElementById('virtualScroller')
 virtualScroller ? virtualScroller.addEventListener('scroll', handleScroller) : ''

监听事件中判断是否触底 因为是移动端 用 scrollTop + clientHeight + 1 >= scrollHeight判断

const handleScroller = (e) => {
  if (scrollTop + clientHeight + 1 >= scrollHeight) {
   // virtualScrollerAllow.value = true // 触底逻辑
  }
}

ios触底会回弹,导致疯狂触发触底的逻辑,建议用一个单独变量控制是否到底,再watch监听这个变量,true/false 来判断是否执行触底逻辑 (可能影响性能)

加入 “加载中/加载失败/到底文案及图标”,vant中list组件 虚拟滚动完算是废了😂 页面没有滚动,根本达不到vant触发逻辑,并且ios橡皮筋回弹会导致list鬼畜式触发。 如果h5依赖原生客户端,可以客户端方关闭并和h5通信,单h5页面网上方法好像并不能完全关闭橡皮筋效果。

通过插槽手动写个触底状态。 插槽有两个 before和after 在顶部和底部。 触底定义变量,通过监听变量的值请求接口,再请求接口回来根据改变变量的值

<DynamicScroller
    :items="listData"
    :min-item-size="100"
    class="scroller"
    id="virtualScroller"
    v-else
  >
    <template v-slot="{ item, index, active }">
      <DynamicScrollerItem
        :item="item"
        :active="active"
        :size-dependencies="[
          item.imageUrlList, item.content
        ]"
        :data-index="index"
      >
        //<Item :infos="item" @follow="onFollowClick(item,index)" :routerStatus="routerStatus" :currentIndex="index" :pageIdentify="pageIdentify"  @like="onLikeClick(item, index)" @collection="onCollectionClick(item, index)" @commit="onCommitClick" @report="onReportClick"  @delete="onDeleteClick(item,index)" @onDetailClick="sessionStorageWatch"></Item>
      </DynamicScrollerItem>
    </template>

    <template #after>
      <div class="after-loading">
        <van-loading size="24px" class="status-loading" v-if="loadingStatus==='loading'">加载中...</van-loading>
        <span class="status" v-if="loadingStatus==='error'" @click="loadError">加载失败,点击重新加载</span>
        <span class="status" v-if="loadingStatus==='finished'">到底了~</span>
      </div>
    </template>
  </DynamicScroller>

5.借助vant PullRefresh 实现下拉刷新
vant PullRefresh 中有disabled的选项,可以禁止下拉刷新,如果直接用PullRefresh包裹虚拟滚动,会导致无法向下滑动,任何位置下拉都会触发刷新逻辑。先用disabled禁用下拉刷新,通过监听虚拟滚动scroll中scrollTop,如果小于10或者等于0 (ios回弹还是体验不好) 将 disabled变为false
一直监听scroll也在影响性能

  1. 实现分页加载
    只需在触底逻辑时,执行分页加载,并且新数据回来时,往原数组添加
/*const getIndexContent = async (isFirst) => {
  loadingStatus.value = 'loading'
  try {
    let topRes, baseRes
    if(isFirst) {
      topRes = await getIndexTop()
      indexTops.value.push(...topRes)
    }
    baseRes = await getIndexList()
    indexBases.value.push(...baseRes)
    if (indexTops.value.length === 0 && indexBases.value.length === 0) {
      loadingStatus.value = 'finished'
    } else {
    */
      indexList.value = [
        ...indexTops.value,
        ...indexBases.value
      ]
      /*
      virtualScrollerAllow.value = false
      // 活动上报
      reportAdd(indexList.value.reduce((pre,cur)=>{
        pre.push(cur.id)
        return pre;
      },[]))
    }
  } catch (e) {
    console.log('获取首页内容有误', e)
    loadingStatus.value = 'error'
  }
}
*/
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
vue-virtual-scroller是一个用于Vue.js的虚拟滚动组件,它可以实现性能的长列表渲染。下面是使用vue-virtual-scroller实现下拉加载的步骤: 1. 首先,安装vue-virtual-scroller依赖: ```shell npm install vue-virtual-scroller -d ``` 2. 在你的Vue组件中引入vue-virtual-scroller: ```javascript import { RecycleScroller } from 'vue-virtual-scroller'; import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'; export default { components: { RecycleScroller }, // ... } ``` 3. 在模板中使用vue-virtual-scroller组件,并设置相应的属性和事件: ```html <template> <div class="wrapper"> <recycle-scroller :items="items" :item-size="50" :min-item-size="50" :max-item-size="50" :buffer="10" :page-mode="true" @load="loadMore" > <template slot-scope="props"> <!-- 渲染每个列表项的内容 --> <div class="item">{{ props.item }}</div> </template> </recycle-scroller> </div> </template> ``` 4. 在Vue组件的data中定义items数组,并在created钩子函数中初始化items: ```javascript export default { data() { return { items: [] }; }, created() { this.initItems(); }, methods: { initItems() { // 初始化items数组,可以从后端获取数据并赋值给items // 示例:假设从后端获取了10条数据 this.items = Array.from({ length: 10 }, (_, index) => `Item ${index + 1}`); }, loadMore() { // 加载更多数据的逻辑 // 示例:假设每次加载5条数据 const startIndex = this.items.length; const endIndex = startIndex + 5; const newItems = Array.from({ length: 5 }, (_, index) => `Item ${index + startIndex + 1}`); this.items = [...this.items, ...newItems]; } } } ``` 5. 根据你的需求,可以自定义样式来美化滚动区域。 以上是使用vue-virtual-scroller实现下拉加载的基本步骤。你可以根据自己的具体需求进行进一步的定制和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值