两个组件解决移动端列表
移动端列表页几乎都是「下拉刷新 + 上拉加载更多」的组合。Vant 用两个组件搞定:
- van-pull-refresh:下拉刷新容器
- van-list:上拉加载更多(无限滚动)
最简示例
<template>
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list v-model:loading="loading" :finished="finished" @load="onLoad">
<van-cell
v-for="item in list"
:key="item.id"
:title="item.title"
:label="item.summary"
/>
</van-list>
</van-pull-refresh>
</template>
<script setup>
import { ref } from "vue";
const list = ref([]);
const loading = ref(false);
const finished = ref(false);
const refreshing = ref(false);
let page = 0;
const onLoad = () => {
setTimeout(() => {
page++;
const items = Array.from({ length: 10 }, (_, i) => ({
id: page * 10 + i,
title: `第 ${page} 页 - 文章 ${i + 1}`,
summary: "这是文章摘要...",
}));
list.value.push(...items);
loading.value = false;
if (page >= 5) finished.value = true;
}, 1000);
};
const onRefresh = () => {
setTimeout(() => {
page = 0;
list.value = [];
finished.value = false;
refreshing.value = false;
onLoad();
}, 1000);
};
</script>
关键点解释
- van-list v-model:loading:双向绑定 loading 状态,加载完数据要手动设为 false
- finished:为 true 时触发"没有更多"状态,不再触发 load 事件
- @load:滚动到底部时触发,需要自己控制分页逻辑
- van-pull-refresh:包裹 van-list,v-model 绑定刷新状态
实战:对接真实接口
<template>
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list
v-model:loading="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<van-cell
v-for="item in list"
:key="item.id"
:title="item.title"
:label="item.description"
is-link
@click="goDetail(item.id)"
/>
</van-list>
</van-pull-refresh>
</template>
<script setup>
import { ref } from "vue";
import { showToast } from "vant";
import { useRouter } from "vue-router";
import { fetchArticleList } from "@/api/article";
const router = useRouter();
const list = ref([]);
const loading = ref(false);
const finished = ref(false);
const refreshing = ref(false);
const page = ref(1);
const pageSize = 10;
const total = ref(0);
const onLoad = async () => {
try {
const { data } = await fetchArticleList({
page: page.value,
pageSize,
});
list.value.push(...data.list);
total.value = data.total;
page.value++;
if (list.value.length >= total.value) finished.value = true;
} catch (e) {
showToast("加载失败");
} finally {
loading.value = false;
}
};
const onRefresh = async () => {
try {
const { data } = await fetchArticleList({ page: 1, pageSize });
list.value = data.list;
total.value = data.total;
page.value = 2;
finished.value = false;
} catch (e) {
showToast("刷新失败");
} finally {
refreshing.value = false;
}
};
const goDetail = (id) => router.push(`/article/${id}`);
</script>
常见问题
List 不触发 load 事件?
通常是因为容器没有固定高度。给 van-list 或其父容器设置一个有限高度(或放在页面容器里),让它可以滚动。
finished 什么时候设为 true?
当加载到的数据量 >= 后端返回的 total 时,或者返回空数组时。
下拉刷新后为什么不自动加载?
需要手动在 onRefresh 里调用 onLoad(),或者把 loading 设为 true 触发 van-list 再次加载。
List 属性速查
| 属性 | 类型 | 说明 |
|---|---|---|
| v-model:loading | boolean | 是否处于加载中 |
| finished | boolean | 是否加载完成 |
| finished-text | string | 加载完成提示文字 |
| error | boolean | 加载是否出错 |
| error-text | string | 错误提示文字 |
| offset | number | 距离底部多远触发加载 |