下拉加载显示 2个加载圈

26 阅读2分钟

你的观察非常敏锐,完全正确!

在方案一的代码中,手动设置 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>

为什么这样做是最好的?

  1. 视觉干净onRefresh 时,我们没有碰 loading 变量(它保持为 false),所以 van-list 底部的加载圈绝对不会出现
  2. 逻辑安全:虽然 van-list 可能会因为内容变动尝试触发 @load,但我们在 onLoad 函数的第一行写了 if (refreshing.value) return;。这意味着即使 van-list 喊了“加载!”,函数也会因为“正在刷新”而直接忽略,避免了重复请求。
  3. 职责分离:下拉刷新负责“重置+替换”,上拉加载负责“追加”。