虚拟列表 xVirtualList
预览
介绍
万级列表用窗口切片,不要把一万个 list-item 全挂上。盒子定高,轨道撑总高,可见行 absolute + top;#item 只开一个出口;:scroll-top 只在跳转时写。默认 full 吃页面剩余高(页面级窗口高);抽屉、弹层写 :full="false" 跟父级局部高,不要量成窗口高。#top / #sticky 和轨道同一层 scroll-view,窗口用 outerScrollTop - topH - stickyH。不要原生子协商。App / Web #sticky 到达后 fixed。小程序 using-sticky + position:sticky。
平台兼容
| Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
|---|---|---|---|---|---|---|---|
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
文件路径
ts
@/uni_modules/tmx-ui/components/x-virtual-list/x-virtual-list.uvue使用
ts
<x-virtual-list></x-virtual-list>Props 属性
| 名称 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| list | `` | Array | () : any[] => [] as any[] |
| rowKey | `` | string | "key" |
| itemHeight | `` | string | "50" |
| height | `` | string | "100%" |
| full | `` | boolean | true |
| bufferSize | `` | number | 6 |
| scrollIndex | `` | number | -1 |
| refresh | `` | xVirtualRefreshFun | () : xVirtualRefreshFun => { return null } |
| pulldown | `` | xVirtualRefreshFun | () : xVirtualRefreshFun => { return null } |
| showScrollbar | `` | boolean | false |
Events 事件
| 名称 | 参数 | 说明 |
|---|---|---|
| scroll | - | - |
| itemClick | - | - |
| refresh | - | - |
| pulldown | - | - |
| rangeChange | - | - |
Slots 插槽
| 名称 | 说明 | 数据 |
|---|---|---|
| top | - | - |
| sticky | - | - |
| empty | - | - |
| item | - | - |
| refresh | - | - |
Ref 方法
| 名称 | 参数 | 返回值 | 说明 |
|---|---|---|---|
| scrollToIndex | - | - | - |
| refreshSize | - | - | - |
| resetScroll | - | - | - |
| getRenderRange | - | - | - |
示例文件路径
json
/pages/zhanshi/virtual-list示例源码
uvue
vue
<template>
<!-- #ifdef MP-WEIXIN -->
<page-meta :page-style="`background-color:${xThemeConfigBgColor}`">
<navigation-bar :background-color="xThemeConfigNavBgColor"
:front-color="xThemeConfigNavFontColor"></navigation-bar>
</page-meta>
<!-- #endif -->
<view class="page">
<x-virtual-list
class="pageList"
row-key="key"
item-height="80"
height="100%"
:list="list"
:scroll-index="jumpIndex"
:pulldown="onPulldown"
:refresh="onLoadMore"
>
<template #top>
<x-sheet :margin="[0, 0, 0, 8]">
<x-text font-size="18" class=" text-weight-b mb-8">虚拟列表 xVirtualList</x-text>
<x-text color="#999999" class="mb-12">万级只渲染窗口行。向下先推顶部,到顶后滚下面的行并触底加载。#item 一次给出 rows,页面自己 v-for。本页 {{ list.length }} 条。</x-text>
<view class="flex flex-row flex-row-center-between">
<x-button size="small" class="flex-1" @click="jumpTo">指定</x-button>
<x-button size="small" class="ml-5 flex-1" @click="appendRows">追加</x-button>
<x-button size="small" class="mx-5 flex-1" @click="keepTwenty">20条</x-button>
<x-button size="small" class="flex-1" @click="clearAll">清空</x-button>
</view>
</x-sheet>
</template>
<template #sticky="{ pinned }">
<view :class="pinned ? 'tabFull' : 'tabCard'">
<x-tabs
v-model="menuId"
:list="tabList"
item-width="33.333%"
height="44"
:round="pinned ? '0' : '10'"
></x-tabs>
</view>
</template>
<template #item="{ rows }">
<view
v-for="row in rows"
:key="keyOf(row)"
class="rowHit"
:style="styleOf(row)"
>
<view class="itemBox" :style="{ backgroundColor: itemBg }">
<image class="round-6" style="width: 50px;height:50px;" :src="coverOf(dataOf(row))"></image>
<x-text class="ml-10">{{ titleOf(dataOf(row)) }}</x-text>
</view>
</view>
</template>
<template #empty>
<x-empty v-if="list.length == 0" :empty="true" :loading="false" btn-label="加载1万条" @click="reloadAll"></x-empty>
</template>
</x-virtual-list>
</view>
</template>
<script lang="ts" setup>
import { computed, nextTick, ref } from "vue"
import { xConfig, TABS_ITEM_INFO } from "@/uni_modules/tmx-ui"
type DemoRow = {
key : string
title : string
}
type RowBoxStyle = {
position : string
top : string
height : string
left : string
width : string
}
function makeRows(from : number, count : number) : DemoRow[] {
const out : DemoRow[] = []
for (let i = 0; i < count; i++) {
const n = from + i
out.push({
key: n.toString(),
title: "数据项目-" + (n + 1).toString()
})
}
return out
}
const jumpIndex = ref<number>(-1)
const menuId = ref<string>("all")
const tabList = [
{ id: "all", title: "综合" },
{ id: "new", title: "新品" },
{ id: "hot", title: "热门" }
] as TABS_ITEM_INFO[]
const list = ref<DemoRow[]>(makeRows(0, 10000))
const itemBg = computed(() : string => {
if (xConfig.dark == "dark") return xConfig.sheetDarkColor
return "#ffffff"
})
function keyOf(row : any) : string {
return "" + row.key
}
function dataOf(row : any) : any {
return row.item
}
function styleOf(row : any) : RowBoxStyle {
const s = row.style
if (s != null) {
return {
position: "" + s.position,
top: "" + s.top,
height: "" + s.height,
left: "" + s.left,
width: "" + s.width
}
}
const top = row.top
let y = 0
if (typeof top == "number") y = Math.round(top)
return {
position: "absolute",
top: y.toString() + "px",
height: "80px",
left: "0px",
width: "100%"
}
}
function coverOf(item : any) : string {
return "https://picsum.photos/100/100?random=" + item.key
}
function titleOf(item : any) : string {
const t = item.title
if (t == null) return ""
return "" + t
}
function wait(ms : number) : Promise<void> {
return new Promise<void>((resolve) => {
setTimeout(() => {
resolve()
}, ms)
})
}
function reloadAll() : void {
list.value = makeRows(0, 10000)
jumpIndex.value = 0
}
function appendRows() : void {
list.value = list.value.concat(makeRows(list.value.length, 20))
}
function keepTwenty() : void {
list.value = list.value.slice(0, 20)
}
function clearAll() : void {
list.value = [] as DemoRow[]
}
function jumpTo() : void {
if (jumpIndex.value == 2000) {
jumpIndex.value = -1
nextTick(() : void => {
jumpIndex.value = 2000
})
return
}
jumpIndex.value = 2000
}
async function onPulldown() : Promise<void> {
await wait(800)
list.value = makeRows(0, 10000)
}
async function onLoadMore() : Promise<void> {
await wait(600)
appendRows()
}
</script>
<style scoped>
.page {
flex: 1;
display: flex;
flex-direction: column;
}
.pageList {
flex: 1;
min-height: 0;
}
.tabCard {
margin: 0 8px 8px 8px;
border-radius: 10px;
overflow: hidden;
}
.tabFull {
margin: 0;
}
.rowHit {
box-sizing: border-box;
padding-bottom: 8px;
overflow: hidden;
}
.itemBox {
height: 72px;
display: flex;
flex-direction: row;
align-items: center;
padding: 0 12px;
border-radius: 10px;
}
</style>