你的观察非常敏锐,完全正确!
在方案一的代码中,手动设置 loading.value = true 确实会导致底部的加载圈(Loading Spinner)显示出来。
如果你的列表内容还没清空,底部的圈会在列表最下方,用户可能看不见;但如果你先清空了列表,或者列表很短,你就会看到上面一个圈(下拉刷新),下面一个圈(列表加载),同时在转。这确实不够完美。
为了既防止重复触发 onLoad,又不显示底部的圈,最完美的解决方案是:“加锁”。
即:不要修改 loading 的值,而是在 onLoad 里判断当前是否正在“下拉刷新中”。
终极解决方案:在 onLoad 中拦截
我们不复用 onLoad 的逻辑,或者在 onLoad 头部做一个判断:如果正在下拉刷新,就禁止触发上拉加载。
<script setup>
import { ref } from 'vue';
const list = ref([]);
const loading = ref(false); // 仅控制底部加载状态
const finished = ref(false);
const refreshing = ref(false); // 仅控制顶部刷新状态
// 模拟数据请求
const getData = async (isRefresh = false) => {
// 模拟请求延迟
return new Promise(resolve => setTimeout(() => {
const data = Array.from({ length: 10 }, (_, i) => i + (isRefresh ? 0 : list.value.length));
resolve(data);
}, 1000));
};
// 上拉加载 (van-list 触发)
const onLoad = async () => {
// 【核心技巧】:如果正在下拉刷新中,直接阻止上拉加载的逻辑执行
if (refreshing.value) {
return;
}
loading.value = true; // 显示底部圈
try {
const newData = await getData(false);
list.value.push(...newData);
// 数据加载完,隐藏底部圈
loading.value = false;
if (list.value.length >= 40) {
finished.value = true;
}
} catch(e) {
loading.value = false;
}
};
// 下拉刷新 (van-pull-refresh 触发)
const onRefresh = async () => {
// 这里不需要设置 loading.value = true,所以底部圈不会出来!
finished.value = false; // 重置结束状态
// refreshing.value 已经在组件 v-model 中自动变为 true 了
try {
const newData = await getData(true);
// 刷新是“替换”数据,不是“追加”
list.value = newData;
// 关键:重置页码等逻辑
} finally {
// 请求结束,关闭顶部下拉圈
refreshing.value = false;
// 额外保险:
// 如果数据太少不满一屏,手动调用一下 onLoad 填满
// 或者依靠 list 的 immediate-check 属性
}
};
</script>
<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" :title="item" />
</van-list>
</van-pull-refresh>
</template>
为什么这样做是最好的?
- 视觉干净:
onRefresh时,我们没有碰loading变量(它保持为false),所以van-list底部的加载圈绝对不会出现。 - 逻辑安全:虽然
van-list可能会因为内容变动尝试触发@load,但我们在onLoad函数的第一行写了if (refreshing.value) return;。这意味着即使van-list喊了“加载!”,函数也会因为“正在刷新”而直接忽略,避免了重复请求。 - 职责分离:下拉刷新负责“重置+替换”,上拉加载负责“追加”。