使用 vant 实现移动端上拉加载和下拉刷新功能

2,118 阅读5分钟

上拉加载

文档指明List组件是瀑布流滚动加载,用于展示长列表。当列表即将滚动到底部时,会触发事件并加载更多列表项

实际使用时,List组件能做的就是触发加载事件,加载更多列表项对应的代码还需要开发者编写。

<van-list 
    v-model="loading" 
    :finished="finished" 
    finished-text="没有更多了" 
    :error.sync="error"
    error-text="请求失败,点击重新加载"
    @load="onLoad" > 
        <van-cell v-for="item in list" :key="item" :title="item" /> 
</van-list>

基础用法(引用自文档):

List 组件通过 loading 和 finished 两个变量控制加载状态,当组件滚动到底部时,会触发 load 事件并将 loading 设置成 true。此时可以发起异步操作并更新数据,数据更新完毕后,将 loading 设置成 false 即可。若数据已全部加载完毕,则直接将 finished 设置成 true 即可。

我的理解:

List 组件通过 loading 和 finished 两个变量控制加载状态。

loading变量代表刷新状态,值为true时表示加载中,值为false时表示加载结束。

finished变量代表另一个状态:数据是否加载完毕。数据库中的数据总有穷尽,finished值为true时表示已经加载到最后一条,没有更多;值为false表示依旧能够加载新的数据。

代码示例

import { getList } from "@/api/demo"
export default {
  data() {
    return {
      list: [],
      total: 0,
      loading: false,
      error: false,
      finished: false,
      pageNum: 0, // 初始请求第一页
      pageSize: 5,
    };
  },
  methods: {
    // 当组件滚动到底部时,会触发 `load` 事件
    // 并将 `loading` 设置成 `true`。此时可以发起异步操作并更新数据
    async onLoad() {
      // 加1,表示加载下一页
      this.pageNum++; // 初始请求第一页

      // 发送 ajax 请求获取后端数据
      getList({
        pageNum: this.pageNum,
        pageSize: this.pageSize,
      })
        .then((res) => {
          // 返回的数据格式为:该格式来源于`mybatis`的轻量级分页插件pageHelper
          // {
          //    rows: [], // 所请求的数据列表
          //    total: 10, // 数据总条数
          // }

          this.total = res.total;

          // 将新数据添加在已展示数据的后面
          this.list = [...this.list, ...res.rows];

          // 加载状态结束
          this.loading = false;
        })
        .catch((e) => {
          this.error = true;
          this.pageNum--;
          console.log(e);
        });

      // 判断是否达到终点
      if (this.list.length == this.total) {
        this.finished = true;
      }
    },
  },
};

下面是文档给出的示例代码,经过对比可以发现,主要需要编写的就是加载更多列表项对应的代码:

export default { 
    data() { 
        return { 
            list: [], 
            loading: false, 
            finished: false, 
        }; 
    }, 
    methods: { 
        onLoad() { 
            // 异步更新数据
            // setTimeout 仅做示例,真实场景中一般为 ajax 请求 
            setTimeout(() => { 
              for (let i = 0; i < 10; i++) { 
                this.list.push(this.list.length + 1); 
              } 
              
              // 加载状态结束 
              this.loading = false; 
                
              // 数据全部加载完成 
              if (this.list.length >= 40) { 
                  this.finished = true; 
              } 
            }, 1000); 
        }, 
     }, 
};

关于List组件的更多信息,可参考文档:Vant 2 - List组件

下拉刷新

PullRefresh组件用于提供下拉刷新的交互操作。

基础用法(引用自文档):

下拉刷新时会触发 refresh 事件,在事件的回调函数中可以进行同步或异步操作,操作完成后将 v-model 设置为 false,表示加载完成。

我的理解

通过下拉这个操作,告知系统需要重新加载数据,此时会触发refresh 事件,在此事件中需要将相关变量重置为初始状态然后发出Ajax请求。

List 组件可以与 PullRefresh 组件结合使用,实现下拉刷新的效果。

代码示例

<van-pull-refresh v-model="refreshing" @refresh="onRefresh"> 
    <van-list 
        v-model="loading" 
        :finished="finished" 
        finished-text="没有更多了" 
        :error.sync="error"
        error-text="请求失败,点击重新加载"
        @load="onLoad" > 
            <van-cell v-for="item in list" :key="item" :title="item" /> 
    </van-list>
</van-pull-refresh>

import { getList } from "@/api/demo"
export default {
  data() {
    return {
      list: [],
      total: 0,
      loading: false,
      error: false,
      finished: false,
      pageNum: 0, // 初始请求第一页
      pageSize: 5,
      refreshing: false,
    };
  },
  methods: {
    onRefresh() {
      // 清空列表数据
      this.finished = false;

      // 重新加载数据
      // 将 loading 设置为 true,表示处于加载状态
      this.loading = true;
      this.onLoad(); // 手动执行onLoad()
    },
    // 当组件滚动到底部时,会触发 `load` 事件
    // 并将 `loading` 设置成 `true`。此时可以发起异步操作并更新数据
    onLoad() {
      if (this.refreshing) {
        this.pageNum = 0;
        this.list = [];
        this.refreshing = false;
      }

      // 加1,表示加载下一页
      this.pageNum++; // 初始请求第一页

      // 发送 ajax 请求获取后端数据
      getList({
        pageNum: this.pageNum,
        pageSize: this.pageSize,
      })
        .then((res) => {
          // 返回的数据格式为:该格式来源于`mybatis`的轻量级分页插件pageHelper
          // {
          //    rows: [], // 所请求的数据列表
          //    total: 10, // 数据总条数
          // }
          this.total = res.total;
          // 将新数据添加在已展示数据的后面
          this.list = [...this.list, ...res.rows];

          // 加载状态结束
          this.loading = false;
        })
        .catch((e) => {
          this.error = true;
          this.pageNum--;
          console.log(e);
        });

      // 判断是否达到终点
      if (this.list.length == this.total) {
        this.finished = true;
      }
    },
  },
};

多tab场景

比如任务列表,分为已完成、未完成,此时点击tab需要进行列表的切换。

官方示例:



<van-tabs v-model="active"> 
    <van-tab title="标签 1">内容 1</van-tab>
    <van-tab title="标签 2">内容 2</van-tab> 
    <van-tab title="标签 3">内容 3</van-tab> 
    <van-tab title="标签 4">内容 4</van-tab> 
</van-tabs>


为了只维护一组以list数组核心的相关变量,列表不能放在 van-tab 标签内部。

代码示例


<van-tabs v-model="active" @change="onChange"> 
    <van-tab title="未完成" :name="1"></van-tab>
    <van-tab title="已完成" :name="2"></van-tab> 
</van-tabs>


<van-pull-refresh v-model="refreshing" @refresh="onRefresh"> 
    <van-list 
        v-model="loading" 
        :finished="finished" 
        finished-text="没有更多了" 
        :error.sync="error"
        error-text="请求失败,点击重新加载"
        @load="onLoad" > 
            <!-- 如果已完成、未完成的列表项有不同的UI(一般都不一样) , -->
            <!-- 可依据 this.active 使用 v-if 进行条件渲染  -->
            
            <!-- 未完成列表 -->
            <div v-if="active == 1 ">
                <van-cell  v-for="item in list" :key="item" :title="item" /> 
            </div>
            
            <!-- 已完成列表 -->
            <div v-if="active == 2 ">
                <van-cell  v-for="item in list" :key="item" :title="item" /> 
            </div>
    </van-list>
</van-pull-refresh>

import { getYwcList, getWwcList } from "@/api/demo"
export default {
  data() {
    return {
      active: 1,
      list: [],
      total: 0,
      loading: false,
      error: false,
      finished: false,
      pageNum: 0, // 初始请求第一页
      pageSize: 5,
      refreshing: false,
    };
  },
  methods: {
    onChange() {
      // 标签切换时,需要清空列表数据后重新加载数据
      this.finished = false;
      this.pageNum = 0;
      this.list = [];
      // 将 loading 设置为 true,表示处于加载状态
      this.loading = true;
      // 手动执行onLoad()
      this.onLoad();
    },
    onRefresh() {
      // 清空列表数据后
      this.finished = false;

      // 重新加载数据
      // 将 loading 设置为 true,表示处于加载状态
      this.loading = true;
      this.onLoad(); // 手动执行onLoad()
    },
    // 当组件滚动到底部时,会触发 `load` 事件
    // 并将 `loading` 设置成 `true`。此时可以发起异步操作并更新数据
    onLoad() {
      if (this.refreshing) {
        this.pageNum = 0;
        this.list = [];
        this.refreshing = false;
      }

      // 加1,表示加载下一页
      this.pageNum++; // 初始请求第一页

      switch (this.active) {
        case 1:
          // 发送 ajax 请求获取后端数据
          getWwcList({
            pageNum: this.pageNum,
            pageSize: this.pageSize,
          })
            .then((res) => {
              // 返回的数据格式为:该格式来源于`mybatis`的轻量级分页插件pageHelper
              // {
              //    rows: [], // 所请求的数据列表
              //    total: 10, // 数据总条数
              // }

              // 将新数据添加在已展示数据的后面
              this.list = [...this.list, ...res.rows];
              this.total = res.total;

              // 加载状态结束
              this.loading = false;
            })
            .catch((e) => {
              this.error = true;
              this.pageNum--;
              console.log(e);
            });
          break;
        case 2:
          // 发送 ajax 请求获取后端数据
          getYwcList({
            pageNum: this.pageNum,
            pageSize: this.pageSize,
          })
            .then((res) => {
              this.list = [...this.list, ...res.rows];
              this.total = res.total;

              // 加载状态结束
              this.loading = false;
            })
            .catch((e) => {
              this.error = true;
              this.pageNum--;
              console.log(e);
            });
          break;
      }
      // 判断是否达到终点
      if (this.list.length == this.total) {
        this.finished = true;
      }
    },
  },
};

留言

若有错误之处,请评论区批评指正,感谢。