likes
comments
collection
share

Vue3 封装了下拉刷新列表的操作

作者站长头像
站长
· 阅读数 16

开头先BB两句

Vue2 中可以使用 mixin 混入对象。公共数据,方法,可以混入。

页面逻辑差不多的时候,引入mixin 可以少写很多代码,提高复用性。一些常用的请求接口的方法,和文件流下载的方法,点击事件,都可以放在mixin文件中。

但是 Vue3 里面,没有了 mixin 混入。如果,要实现类似的功能,应该如何封装呢?

举个栗子,移动端H5页面,最常见的滚动加载,下拉刷新。

移动端下拉刷新列表的常见操作

Vant UI组件库,来实现下拉刷新

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

官网例子

<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>

自己写的 demo点击下方的详情展开

<template>
  <div class="page-box">
    <div class="list-box">
      <van-pull-refresh v-model="refreshing" @refresh="onRefresh" class="list-refresh">
        <van-list
          v-model:loading="loading"
          :finished="finished"
          loading-text="加载中..."
          finished-text=""
          @load="onLoad"
          @offset="10"
        >
          <div v-for="(item, index) in list" :key="index" class="list-item">
            {{ item.name }}
          </div>
        </van-list>
      </van-pull-refresh>
    </div>
  </div>
</template>
<script>
import { reactive, toRefs, onBeforeMount, onMounted } from "vue";
// 引入防抖函数,如果用户多次频繁操作以最后一次为准
import { debounce } from "@/common/utils.js";
export default {
  name: "",
  setup() {
    console.log("1-开始创建组件-setup");
    const state = reactive({
      // 加载中
      loading: false,
      // 加载完成
      finished: false,
      // 是否刷新
      refreshing: false,
      // 列表数据
      list: [],
      //请求第几页
      pageNumber: 1,
      //每页请求的数量
      pageSize: 20,
      // 总共多少页
      totalPage: 0,
    });
    onBeforeMount(() => {
      console.log("2.组件挂载页面之前执行----onBeforeMount");
    });
    onMounted(() => {
      console.log("3.-组件挂载到页面之后执行-------onMounted");
    });

    const loadData = async () => {
      console.log("加载数据");
      let params = {
        // 搜索条件
        pageNo: state.pageNumber,
        pageSize: state.pageSize,
      };
      const result = {
        total: 10,
        pages: 1,
        records: [
          {
            id: 1,
            name: "张三",
            number: "123456789",
          },
          {
            id: 2,
            name: "李四",
            number: "123456789",
          },
          {
            id: 3,
            name: "王五",
            number: "123456789",
          },
          {
            id: 4,
            name: "赵六",
            number: "123456789",
          },
          {
            id: 5,
            name: "田七",
            number: "123456789",
          },
          {
            id: 6,
            name: "孙八",
            number: "123456789",
          },
          {
            id: 7,
            name: "周九",
            number: "123456789",
          },
          {
            id: 8,
            name: "吴十",
            number: "123456789",
          },
          {
            id: 9,
            name: "郑十一",
            number: "123456789",
          },
          {
            id: 10,
            name: "王十二",
            number: "123456789",
          },

        
        ],
      };
      const { total, pages, records } = result;
      // 总共多少页
      state.totalPage = pages;
      if (pages) state.totalPage = Math.ceil(total / state.pageSize);
      let list = records;
      if (state.pageNumber == 1) {
        state.list = list;
      } else {
        state.list.push(...list);
      }
      state.loading = false;
      if (state.pageNumber >= state.totalPage) state.finished = true;
    };

    // 当组件滚动到底部时,会触发 load 事件,调用 onLoad 方法
    const onLoad = () => {
      if (!state.refreshing && state.pageNumber < state.totalPage) {
        state.pageNumber++;
      }
      if (state.refreshing) {
        state.list = [];
        state.refreshing = false;
      }
      // 加载数据
      loadData();
    };
    // 下拉刷新
    const onRefresh = () => {
      state.refreshing = true;
      state.finished = false;
      state.loading = true;
      state.pageNumber = 1;
      onLoad();
    };

    const onSearch = () => {
      onRefresh();
    };

    // vue3 里面定义防抖方法,直接在setup里面使用,防抖函数包裹一下
    const debounceSearch = debounce(() => {
      onSearch();
    }, 1000);

    return {
      ...toRefs(state),
      debounceSearch,
      onLoad,
      onRefresh,
      onSearch,
    };
  },
};
</script>
<style scoped lang="scss">
.list-box {
  padding-bottom: 0;
}
.list-item {
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  height: 28px;
  padding: 10px;
  border-bottom: 1px solid #f2f2f2;
  box-shadow: 0 1px 7px #edeef1;
  border-radius: 8px;
  margin: 10px 16px;
  background: #fff;
  color: #969799;
}
</style>

加载数据,下拉刷新,上拉刷新都有了。可惜,不能像Vue2 一样复用。

于是,我参考了唐诗这位大佬的用法

juejin.cn/post/716048…

加载数据,和下拉刷新,等一系列操作,封装在 usePage 中

import { reactive, ref } from 'vue'
import { debounce } from "@/common/utils.js";
/**
 * @description usePage 接收一个 opts 参数,返回列表所需数据
 * @param {Function} opts.getListApi  - 获取列表数据的接口
 * @param {Function} opts.getListFunc  - 执行完 getList 成功后执行的逻辑 有一个 list 参数
 */

export const usePage = (opts) => {
  // getListApi 获取列表数据的接口
  // getListFunc 执行完 loadData 成功后执行的逻辑 有一个 list 参数
  const {
    getListApi,
    getListFunc = (opts) => { },
  } = opts

  const page = reactive({
    // 加载中
    loading: false,
    // 加载完成
    finished: false,
    // 是否刷新
    refreshing: false,
    // 列表数据
    list: [],
    //请求第几页
    pageNumber: 1,
    //每页请求的数量
    pageSize: 10,
    // 总共多少页
    totalPage: 0,
  })

  // 加载数据
  const loadData = async () => {
    console.log("加载数据");
    getListApi().then((res) => {
      if (res == null) return;
      const { result } = res;
      const { total, pages, records } = result;
      // 总共多少页
      page.totalPage = pages;
      if (pages) page.totalPage = Math.ceil(total / page.pageSize);
      let list = records;

      if (page.pageNumber == 1) {
        page.list = list;
      } else {
        page.list.push(...list);
      }
      page.loading = false;
      if (page.pageNumber >= page.totalPage) page.finished = true;
      getListFunc(list)
    })
  };

  // 当组件滚动到底部时,会触发 load 事件,调用 onLoad 方法
  const onLoad = () => {
    if (!page.refreshing && page.pageNumber < page.totalPage) {
      page.pageNumber++;
    }
    if (page.refreshing) {
      page.list = [];
      page.refreshing = false;
    }
    // 加载数据
    loadData();
  };
  // 下拉刷新
  const onRefresh = () => {
    page.refreshing = true;
    page.finished = false;
    page.loading = true;
    page.pageNumber = 1;
    onLoad();
  };
  
  const onSearch = () => {
    onRefresh();
  };
  // vue3 里面定义防抖方法,直接在setup里面使用,防抖函数包裹一下
  const debounceSearch = debounce(() => {
    onSearch();
  }, 1000);

  return {
    page,
    debounceSearch,
    onSearch,
    onRefresh,
    onLoad,
  }
}    

组件中使用:

<script>
import { reactive, toRefs, onBeforeMount, onMounted } from "vue";
import { usePage } from "@/common/usePage.js";
export default {
  name: "",
  setup() {
    console.log("1-开始创建组件-setup");
    const state = reactive({});
    onBeforeMount(() => {
      console.log("2.组件挂载页面之前执行----onBeforeMount");
    });
    onMounted(() => {
      console.log("3.-组件挂载到页面之后执行-------onMounted");
    });

    const getListApi = () => {
      return new Promise((resolve, reject) => {
        console.log("加载数据");
        const result = {
          total: 10,
          pages: 1,
          records: [
            {
              id: 1,
              name: "张三",
              number: "123456789",
            },
            {
              id: 2,
              name: "李四",
              number: "123456789",
            },
          ],
        };
        resolve({ result });
      });
    };

    const getListFunc = (list) => {
      console.log(list);
      console.log(page.list);
    };

    // 加载数据,和下拉刷新,等一系列操作,封装在 usePage 中,
    const { page, debounceSearch, onSearch, onRefresh, onLoad, loadData } = usePage({
      getListApi,
      getListFunc,
    });

    return {
      ...toRefs(state),
      ...toRefs(page),
      debounceSearch,
      onLoad,
      onRefresh,
      onSearch,
    };
  },
};
</script>

封装之后,简洁优雅多了。

最后的话

以上,如果对你有用的话,不妨点赞收藏关注一下,谢谢 🙏

😊 微信公众号: OrzR3

💖 不定期更新一些技术类,生活类,读书类的文章。