更新记录

0.0.4(2026-02-28) 下载此版本

  • 嵌套滚动模式适配微信小程序

0.0.3(2026-02-28) 下载此版本

  • 增加嵌套滚动模式,支付页面滚动模式

0.0.2(2026-02-28) 下载此版本

重构插件版本

查看更多

平台兼容性

uni-app(4.55)

Vue2 Vue2插件版本 Vue3 Vue3插件版本 Chrome Chrome插件版本 Safari Safari插件版本 app-vue app-vue插件版本 app-nvue Android Android插件版本 iOS iOS插件版本 鸿蒙
0.0.2 0.0.2 0.0.2 0.0.2 0.0.2 - 0.0.2 0.0.2 -
微信小程序 微信小程序插件版本 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
0.0.2 - - - - - - - - - - -

zhibai-virtualList

一个支持 uniapp 的虚拟列表组件,兼容微信小程序、H5 和 APP 环境,同时支持 Vue2 和 Vue3。

功能特性

  • ✅ 虚拟列表加载,优化性能
  • ✅ 支持动态高度和固定高度
  • ✅ 支持双列瀑布流布局
  • ✅ 兼容微信小程序、H5、APP 环境
  • ✅ 同时支持 Vue2 和 Vue3
  • ✅ 支持加载状态和完成状态显示
  • ✅ 智能数据追加,无缝衔接新数据

使用方法

全局注册

import zhibaiVirtualList from 'zhibai-virtual-list'
Vue.use(zhibaiVirtualList)

局部引入

<template>
  <zhibai-virtualList
    :list="dataList"
    :item-height="100"
    :mode="'single'"
    :loading="loading"
    :finished="finished"
    @loadMore="handleLoadMore"
  >
    <template #default="{ item, index }">
      <view class="item">{{ item.title }}</view>
    </template>
  </zhibai-virtualList>
</template>

<script>
import zhibaiVirtualList from 'zhibai-virtual-list'

export default {
  components: {
    zhibaiVirtualList
  },
  data() {
    return {
      dataList: [],
      loading: false,
      finished: false
    }
  },
  methods: {
    handleLoadMore() {
      if (this.loading || this.finished) return

      this.loading = true
      // 模拟加载数据
      setTimeout(() => {
        const newData = [] // 获取新数据
        this.dataList = [...this.dataList, ...newData]
        this.loading = false

        // 如果没有更多数据
        if (newData.length === 0) {
          this.finished = true
        }
      }, 1000)
    }
  }
}
</script>

自定义加载状态

<template>
  <zhibai-virtualList
    :list="dataList"
    :loading="loading"
    :finished="finished"
    @loadMore="handleLoadMore"
  >
    <template #default="{ item, index }">
      <view class="item">{{ item.title }}</view>
    </template>

    <!-- 自定义加载状态 -->
    <template #loading>
      <view class="custom-loading">加载中...</view>
    </template>

    <!-- 自定义完成状态 -->
    <template #finished>
      <view class="custom-finished">没有更多了</view>
    </template>
  </zhibai-virtualList>
</template>

Props

参数 说明 类型 默认值
list 数据列表 Array []
itemHeight 固定高度模式下的项目高度(为0时使用动态高度) Number 0
mode 布局模式:'single'(单列) / 'waterfall'(双列瀑布流) String 'single'
estimatedHeight 动态高度模式下的预估高度 Number 100
bufferSize 缓冲区大小(渲染可视区域外的项目数量) Number 5
height 容器高度(为0时自动获取屏幕高度) Number 0
loading 是否正在加载 Boolean false
finished 是否已加载完成 Boolean false
scrollYShow 是否允许纵向滚动 Boolean true
usePageScroll 是否使用页面滚动模式(不使用scroll-view) Boolean false
externalScroll 是否外部控制滚动(嵌套在父级scroll-view中) Boolean false
parentScrollSelector 父级scroll-view的选择器(externalScroll时使用) String ''
containerSelector 组件容器的选择器(用于嵌套滚动时查询偏移量,微信小程序推荐使用) String ''
disableBounce 是否禁用滚动弹性效果(不影响滚动穿透) Boolean false

Events

事件名 说明 回调参数
loadMore 滚动到底部时触发(用于加载更多) -
scrollToUpper 滚动到顶部时触发 -

Slots

插槽名 说明 参数
default 列表项内容 { item, index }
loading 自定义加载状态 -
finished 自定义加载完成状态 -

使用说明

固定高度模式

当所有列表项高度相同时,设置 itemHeight 可以获得最佳性能:

<zhibai-virtualList
  :list="dataList"
  :item-height="80"
  mode="single"
>
  <template #default="{ item }">
    <view style="height: 80px">{{ item.title }}</view>
  </template>
</zhibai-virtualList>

动态高度模式

当列表项高度不固定时,设置 estimatedHeight 预估高度:

<zhibai-virtualList
  :list="dataList"
  :estimated-height="100"
  mode="single"
>
  <template #default="{ item }">
    <view>{{ item.content }}</view>
  </template>
</zhibai-virtualList>

瀑布流模式

双列瀑布流布局,适合图片列表:

<zhibai-virtualList
  :list="dataList"
  :estimated-height="150"
  mode="waterfall"
>
  <template #default="{ item }">
    <view>
      <image :src="item.image" />
      <text>{{ item.title }}</text>
    </view>
  </template>
</zhibai-virtualList>

页面滚动模式

使用页面原生滚动,而不是 scroll-view 局部滚动:

<template>
  <view>
    <zhibai-virtualList
      ref="virtualList"
      :list="dataList"
      :use-page-scroll="true"
      :estimated-height="100"
      @loadMore="handleLoadMore"
    >
      <template #default="{ item }">
        <view>{{ item.content }}</view>
      </template>
    </zhibai-virtualList>
  </view>
</template>

<script>
export default {
  // 小程序和APP需要在页面中添加
  onPageScroll(e) {
    // 调用组件的handlePageScroll方法
    if (this.$refs.virtualList) {
      this.$refs.virtualList.handlePageScroll(e)
    }
  }
}
</script>

页面滚动模式说明:

  • ✅ 使用页面原生滚动,性能更好
  • ✅ 自动支持下拉刷新等页面级功能
  • ✅ H5环境自动监听window滚动
  • ⚠️ 小程序/APP需要在页面的 onPageScroll 中手动调用组件方法
  • ⚠️ 需要给组件添加 ref 属性

嵌套滚动模式

当虚拟列表需要嵌套在父级 scroll-view 中时使用:

1. 嵌套在原生 scroll-view 中

<template>
  <view class="page">
    <!-- 外层scroll-view -->
    <scroll-view
      id="parentScroll"
      class="parent-scroll"
      scroll-y
      @scroll="handleParentScroll"
    >
      <!-- 页面头部内容 -->
      <view class="header">
        <text>页面头部</text>
      </view>

      <!-- 嵌套的虚拟列表 -->
      <zhibai-virtualList
        ref="virtualList"
        :list="dataList"
        :external-scroll="true"
        parent-scroll-selector="#parentScroll"
        :estimated-height="120"
        :loading="loading"
        :finished="finished"
        @loadMore="handleLoadMore"
      >
        <template #default="{ item }">
          <view>{{ item.content }}</view>
        </template>
      </zhibai-virtualList>

      <!-- 页面底部内容 -->
      <view class="footer">
        <text>页面底部</text>
      </view>
    </scroll-view>
  </view>
</template>

<script>
export default {
  methods: {
    // 处理父级scroll-view滚动
    handleParentScroll(e) {
      // 将滚动事件传递给虚拟列表组件
      if (this.$refs.virtualList) {
        this.$refs.virtualList.handlePageScroll(e.detail)
      }
    },

    handleLoadMore() {
      // 加载更多数据
    }
  }
}
</script>

2. 嵌套在 z-paging 等第三方组件中

<template>
  <view class="page">
    <!-- z-paging 组件 -->
    <z-paging
      ref="paging"
      id="pagingPage"
      @scroll="handlePagingScroll"
      :use-page-scroll="false"
    >
      <!-- 嵌套的虚拟列表 -->
      <zhibai-virtualList
        ref="virtualList"
        :list="dataList"
        :external-scroll="true"
        parent-scroll-selector="#pagingPage"
        :estimated-height="120"
        :loading="loading"
        :finished="finished"
        @loadMore="handleLoadMore"
      >
        <template #default="{ item }">
          <view>{{ item.content }}</view>
        </template>
      </zhibai-virtualList>
    </z-paging>
  </view>
</template>

<script>
export default {
  methods: {
    // 处理z-paging滚动
    handlePagingScroll(e) {
      // 将滚动事件传递给虚拟列表组件
      if (this.$refs.virtualList) {
        this.$refs.virtualList.handlePageScroll(e.detail)
      }
    },

    handleLoadMore() {
      // 加载更多数据
    }
  }
}
</script>

嵌套滚动模式说明:

  • ✅ 支持虚拟列表嵌套在父级 scroll-view 中
  • ✅ 支持嵌套在 z-paging 等第三方滚动组件中
  • ✅ 自动计算组件相对于父级的偏移量
  • ✅ 正确处理滚动位置和触底加载
  • ⚠️ 需要设置 external-scroll="true"
  • ⚠️ 需要提供 parent-scroll-selector 指定父级选择器(使用ID选择器,如 #pagingPage
  • ⚠️ 需要在父级的 @scroll 事件中调用组件的 handlePageScroll 方法
  • ⚠️ 需要给组件添加 ref 属性
  • ⚠️ 如果父级是 z-paging 等封装组件,确保给父级组件添加 id 属性

注意事项

  1. 动态高度模式下,组件会自动查询实际高度并更新,首次渲染可能会有轻微的位置调整
  2. 瀑布流模式下,数据会根据高度自动分配到左右两列
  3. 使用 loadingfinished 属性可以控制加载状态的显示
  4. 新数据通过数组追加的方式添加([...oldList, ...newList]),组件会自动处理衔接
  5. 微信小程序中,瀑布流模式的key使用了前缀('left-'/'right-')来避免slot重复警告
  6. 使用 disableBounce 可以禁用滚动弹性效果,同时保留滚动穿透功能
  7. 页面滚动模式下,小程序和APP需要在页面的 onPageScroll 中手动调用组件的 handlePageScroll 方法
  8. 嵌套滚动模式下,需要在父级 scroll-view 的 @scroll 事件中调用组件的 handlePageScroll 方法
  9. 三种滚动模式互斥:
    • 默认模式:使用组件内部的 scroll-view
    • 页面滚动模式:usePageScroll="true",使用页面原生滚动
    • 嵌套滚动模式:externalScroll="true",嵌套在父级 scroll-view 中

常见问题

嵌套在 z-paging 等第三方组件中

如果虚拟列表嵌套在 z-paging、uview 的 scroll-view 等第三方封装组件中:

  1. 确保父级组件有 ID:给父级组件添加唯一的 id 属性
  2. 传递正确的选择器:使用 ID 选择器,如 parent-scroll-selector="#pagingPage"
  3. 监听滚动事件:在父级组件的 @scroll 事件中调用虚拟列表的 handlePageScroll 方法
  4. 父级组件查询失败:由于 z-paging 等是封装组件,无法直接查询到其位置信息,组件会自动使用虚拟列表自身的位置作为偏移量。这种情况下:
    • 确保在父级的 @scroll 事件中正确传递 e.detail.scrollTop
    • 如果显示不正常,检查传递的 scrollTop 值是否正确
    • 可以在控制台查看 [嵌套滚动] 相关的日志信息

z-paging 示例代码(推荐使用 containerSelector):

<template>
  <z-paging
    ref="paging"
    id="pagingPage"
    @scroll="handlePagingScroll"
    :use-page-scroll="false"
  >
    <!-- 使用 view 包裹组件,并提供 containerSelector -->
    <view class="defaGoodList">
      <zhibai-virtualList
        ref="virtualList"
        :list="dataList"
        :external-scroll="true"
        parent-scroll-selector="#pagingPage"
        container-selector=".defaGoodList"
        :estimated-height="120"
        @loadMore="loadMore"
      >
        <template #default="{ item }">
          <view>{{ item.content }}</view>
        </template>
      </zhibai-virtualList>
    </view>
  </z-paging>
</template>

<script>
export default {
  methods: {
    handlePagingScroll(e) {
      // 关键:正确传递 scrollTop
      if (this.$refs.virtualList) {
        this.$refs.virtualList.handlePageScroll(e.detail)
      }
    },
    loadMore() {
      // 加载更多数据
    }
  }
}
</script>

注意事项:

  • 推荐使用 containerSelector:在组件外包裹一个 view,并通过 container-selector 传入选择器(如 .defaGoodList
  • 微信小程序无法直接查询自定义组件内部元素,使用容器选择器可以准确获取偏移量
  • z-paging 的 @scroll 事件需要设置 :use-page-scroll="false" 才会触发
  • 确保 e.detail 包含 scrollTop 属性
  • 如果不使用 containerSelector,组件会尝试使用类名查询,但可能不够准确

微信小程序slot警告

如果遇到 "More than one slot named" 警告,这是因为微信小程序无法循环slot,有以下几种解决方案:

  1. 使用Skyline 渲染引擎,它支持循环slot,只要在使用的页面options中添加 {"dynamicSlots": true} 中即可,因为Skyline 渲染引擎实在BUG太多所以没有实践,具体可以参考 动态 slot
  2. 不使用循环slot,而是使用条件渲染来实现,例如:
    <!-- 父组件 -->
    <zhibai-virtualList
    :list="dataList"
    :estimated-height="150"
    mode="waterfall"
    >
    <!-- #ifndef MP-WEIXIN -->
    <template #default="{ item, index }">
        <goodInfo goodsComType="falls" :item="item"></goodInfo>
    </template>
    <!-- #endif -->
    </zhibai-virtualList>
<!-- zhibai-virtualList 组件 -->
<!-- #ifdef MP-WEIXIN -->
<goodInfo goodsComType="falls" :item="item._data"></goodInfo>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<slot :item="item._data" :index="item._index"></slot>
<!-- #endif -->
  1. 使用抽象节点,具体可以参考 微信抽象节点

iOS滚动弹性效果

组件使用 scroll-view 的原生 bounces 属性来控制滚动弹性效果:

<!-- 禁用弹性效果(橡皮筋效果),同时保留滚动穿透 -->
<zhibai-virtualList :disable-bounce="true" />

<!-- 启用弹性效果(默认) -->
<zhibai-virtualList :disable-bounce="false" />

说明:

  • bounces 属性只控制橡皮筋效果,不影响滚动穿透
  • 当列表滚动到顶部或底部时,继续滚动会自动穿透到外层页面
  • 这是使用 scroll-view 原生属性实现的,兼容性最好

平台支持:

  • ✅ 微信小程序:完全支持
  • ✅ APP:完全支持
  • ⚠️ H5:部分支持(取决于浏览器)

License

MIT

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议