当前位置: 首页
前端开发
Vue3+UniApp经典Hook方式实现加载更多列表组件

Vue3+UniApp经典Hook方式实现加载更多列表组件

热心网友 时间:2026-06-13
转载

基于Vue3组合式API与uni-app,封装useMoreList函数处理分页参数、触底加载与状态流转,结合MoreList组件与slot插槽构建通用列表架构,支持缺省页、加载中、上拉加载三种状态,显著提升开发效率与复用性。

vue3组合式编程列表组件封装

最终效果示例:

列表类型不重要——文本框、图片流、卡片瀑布,任你选择。通过 slot 插槽注入自己的模板,组件只负责架构,内容交给你。

前言

UniApp 开发中,列表场景是绕不开的标配——初始加载、下拉刷新、分页、上拉加载更多,外加缺省页提示,每次都要写一遍,重复劳动不说,还容易漏掉边界。这次抽离出一个基于 Vue3 组合式 API 的通用列表封装,把那些琐碎的循环逻辑统一收敛到 hook 里,组件只做壳,业务代码只关心数据渲染。实测下来,开发效率提升不少。

组件实现

创建一个 MoreList 组件,核心骨架如下——主体通过 slot 容纳你的自定义列表,当数据为空时展示预设的缺省图,有数据则自动显示 uni-load-more 组件,支持“上拉加载更多”、“加载中”、“已经到底”三种状态。

<script setup>
  import { ref } from "vue";  const props = defineProps({
    moreStatus: {
      type: String,
      default: "more",
    },
    hasData: {
      type: Boolean,
      default: false,
    },
  });
  const emit = defineEmits(["loadMore"]);  const contentText = ref({
    contentdown: "上拉加载更多",
    contentrefresh: "数据加载中……",
    contentnomore: "已经到底啦~",
  });  const handleLoadMore = () => {
    emit("loadMore");
  };
script><template>
  <view class="more-list">
    <view class="list-content">
      <view v-if="hasData">
        <slot>slot>
      view>
      
      <view v-else class="empty-data">
        <image src="/static/images/empty-data.png" class="empty-icon">image>
        <view class="empty-text">数据为空view>
      view>
    view>
    <uni-load-more
      v-if="hasData"
      :status="moreStatus"
      :contentText="contentText"
      @click="handleLoadMore"
    >uni-load-more>
  view>
template><style lang="scss" scoped>
  .more-list {
    height: 100%;
  }
  .empty-data {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 24rpx;
  }
  .empty-icon {
    width: 320rpx;
    height: 320rpx;
  }
  .empty-text {
    font-size: 32rpx;
    color: #666;
  }
style>

hook实现

接下来是核心逻辑层——useMoreList 组合式函数。它接收一个配置对象,自动完成分页参数维护、触底加载、状态流转,并暴露 listDatamoreStatushasData 等响应式变量供组件消费。代码里还预留了 validateConfig 对配置项做校验(这里简化处理,实际可补充类型检查)。

import { ref, computed, isReactive, reactive } from "vue";
import { onReachBottom } from "@dcloudio/uni-app";/**
 * 对配置项进行校验处理
 * @param {*} config 配置项
 * @param {*} fields 需要校验的字段数组
 */
const validateConfig = (config, fields) => {
  // to do ...
};/**
 * 无限滚动列表
 * @param {*} config 配置项
 * @param {*} config.api 接口函数
 * @param {*} config.isAutoLoad 初始是否自动加载列表 默认true
 * @param {*} config.pageSize 每页加载数量
 * @param {*} config.params 其他参数
 * @param {*} config.isRefreshBottom 是否触底刷新默认true
 * @returns
 */
export const useMoreList = (config) => {
  validateConfig(config);
  // 列表
  const listData = ref([]);
  // 加载状态 more:加载更多 / noMore:没有更多了 / loading:加载中
  const moreStatus = ref("more");
  // 是否触底刷新
  const isRefreshBottom = ref(true);
  // 初始是否自动加载列表
  const isAutoLoad = ref(true);
  // 分页参数
  const pagination = ref({
    currentPage: 1,
    pageSize: config?.pageSize || 10,
  });
  // api其他参数
  const params = ref(null);
  // 列表是否有数据
  const hasData = computed(() => listData.value.length > 0);
  // 加载更多
  const handleLoadMore = async () => {
    if (moreStatus.value !== "more") {
      return;
    }
    moreStatus.value = "loading";
    // 校验api函数是否合法
    if (!config.api || typeof config.api !== "function") {
      console.error("useMoreList 中 config.api 必须是一个函数");
      return;
    }
    const res = await config.api({
      ...pagination.value,
      ...params.value,
    });
    if (res.code === 0) {
      listData.value = [...listData.value, ...res.data];
      moreStatus.value = listData.value.length >= res.data.total ? "noMore" : "more";
    } else {
      moreStatus.value = "noMore";
    }
  };  // 初始化配置项
  const initConfig = () => {
    if (config.params) {
      params.value = reactive(config.params);
    }    if (config.isRefreshBottom !== undefined) {
      isRefreshBottom.value = config.isRefreshBottom;
    }    if (config.isAutoLoad !== undefined) {
      isAutoLoad.value = config.isAutoLoad;
    }    // 初始加载列表
    if (isAutoLoad.value) {
      handleLoadMore();
    }    // 是否绑定触底刷新事件
    if (isRefreshBottom.value) {
      onReachBottom(() => {
        pagination.value.currentPage++;
        handleLoadMore();
      });
    }
  };  // 重置列表
  const reset = () => {
    listData.value = [];
    moreStatus.value = "more";
    pagination.value.currentPage = 1;
    pagination.value.pageSize = config?.pageSize || 10;
    handleLoadMore();
  };  // 初始化配置项
  initConfig();  return {
    listData,
    moreStatus,
    hasData,
    pagination,
    params,
    handleLoadMore,
    reset,
  };
};

使用

实际业务中怎么用?很简单——引入 MoreList 组件和 useMoreList hook,把接口回调传入,然后在 slot 里渲染你的列表模板。下面是一份带删除功能的帖子列表示例,卡片用了九宫格图片布局,代码结构很清晰。

<script setup>
  import { ref } from "vue";
  import MoreList from "@/components/MoreList/index.vue";
  import { useMoreList } from "@/hooks/useMoreList.js";  const { params, listData, moreStatus, handleLoadMore, hasData, reset } = useMoreList({
    api: () => {},
  });  const postList = ref([
    {
      id: 1,
      title: "周末露营|风景超美",
      desc: "和朋友一起去山里露营,晚上看星星,空气特别清新~",
      images: ["https://picsum.photos/400/300?random=1"],
    },
    {
      id: 2,
      title: "日常穿搭分享",
      desc: "简约通勤风,舒适百搭,上班族必备~",
      images: [
        "https://picsum.photos/400/300?random=2",
        "https://picsum.photos/400/300?random=3",
        "https://picsum.photos/400/300?random=4",
      ],
    },
    {
      id: 3,
      title: "我的生活碎片|6图",
      desc: "记录生活中的美好瞬间,美食、风景、日常~",
      images: [
        "https://picsum.photos/400/300?random=5",
        "https://picsum.photos/400/300?random=6",
        "https://picsum.photos/400/300?random=7",
        "https://picsum.photos/400/300?random=8",
        "https://picsum.photos/400/300?random=9",
        "https://picsum.photos/400/300?random=10",
      ],
    },
    {
      id: 4,
      title: "九宫格生活照|9图",
      desc: "一次性分享9张生活照片,满满都是回忆~",
      images: [
        "https://picsum.photos/400/300?random=11",
        "https://picsum.photos/400/300?random=12",
        "https://picsum.photos/400/300?random=13",
        "https://picsum.photos/400/300?random=14",
        "https://picsum.photos/400/300?random=15",
        "https://picsum.photos/400/300?random=16",
        "https://picsum.photos/400/300?random=17",
        "https://picsum.photos/400/300?random=18",
        "https://picsum.photos/400/300?random=19",
      ],
    },
    {
      id: 5,
      title: "美食探店|打卡网红餐厅",
      desc: "终于打卡了这家网红餐厅,环境超棒,菜品也很好吃!",
      images: [
        "https://picsum.photos/400/300?random=20",
        "https://picsum.photos/400/300?random=21",
        "https://picsum.photos/400/300?random=22",
      ],
    },
    {
      id: 6,
      title: "读书分享|近期书单",
      desc: "最近读了几本书,分享给大家,每本都很值得一读~",
      images: ["https://picsum.photos/400/300?random=23"],
    },
  ]);  const handleDelete = (id) => {
    uni.showModal({
      title: "提示",
      content: "确定要删除这篇帖子吗?",
      success: (res) => {
        if (res.confirm) {
          const index = postList.value.findIndex((post) => post.id === id);
          if (index > -1) {
            postList.value.splice(index, 1);
            uni.showToast({
              title: "删除成功",
              icon: "success",
            });
          }
        }
      },
    });
  };
script><style lang="scss" scoped>
  .container {
    min-height: 100vh;
    padding: 24rpx;
    box-sizing: border-box;
  }  /* 列表容器 */
  .post-list {
    display: flex;
    flex-direction: column;
    gap: 32rpx;
  }  /* 帖子卡片 */
  .post-card {
    background: #fff;
    border-radius: 32rpx;
    padding: 32rpx;
    box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.06);
  }  /* 标题 */
  .post-title {
    font-size: 34rpx;
    font-weight: 600;
    color: #1a1a1a;
    margin-bottom: 16rpx;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }  /* 描述 */
  .post-desc {
    font-size: 28rpx;
    color: #666;
    margin-bottom: 24rpx;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }  /* 图片布局:自动支持 1~9 张 */
  .post-images {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12rpx;
    margin-bottom: 28rpx;
    width: 100%;
  }  /* 1张图时占满整行 */
  .post-images.single {
    grid-template-columns: 1fr;
  }  .post-img {
    width: 100%;
    height: auto;
    aspect-ratio: 1/1;
    border-radius: 20rpx;
    background: #f0f2f5;
  }  /* 操作栏 */
  .post-actions {
    display: flex;
    justify-content: flex-end;
    padding-top: 24rpx;
    border-top: 2rpx solid #f0f2f5;
  }  /* 删除按钮 */
  .delete-btn {
    padding: 12rpx 28rpx;
    border-radius: 40rpx;
    background-color: #227cff;
    color: #fff;
    font-size: 26rpx;
  }
style>

结尾

整体来看,这套封装足够覆盖日常开发里 80% 的列表场景——初始加载、自动触底、缺省图、重置刷新,一应俱全。代码没有写得很复杂,核心逻辑就在 hook 里,跟 Vue2 的 mixin 思路有点像,但组合式 API 让数据流更清晰、类型推导更友好。如果以上逻辑还不能完全匹配你的业务,欢迎直接在评论区聊聊需求,后续会持续迭代优化。(实测效果稳定,没有发现什么副作用。)

来源:https://juejin.cn/post/7647437715006291977

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
DCloud平台选型指南 适用场景与核心差异详解

DCloud平台选型指南 适用场景与核心差异详解

DCloud是一个围绕跨平台开发与流应用构建的技术生态,旨在帮助前端开发者使用Web技术开发高性能移动应用。其核心产品包括集成开发环境HBuilderX和基于Vue js的uni-app框架,支持一套代码发布至iOS、Android、Web及多端小程序。相比ReactNative和Flutter,uni-app在多端覆盖和Vue技术栈学习成本上具有优势,适合

时间:2026-07-10 06:47
DCloud使用教程与常见问题解决方案详解

DCloud使用教程与常见问题解决方案详解

DCloud提供跨平台应用开发方案,核心为HBuilderX与uni-app框架。开发者使用Vue js语法编写代码,可编译发布至iOS、Android、Web及小程序等多端。流程涵盖项目创建、开发调试与真机预览。常见问题如设备识别、编译失败等可通过检查设置、查看日志与条件编译解决。应用完成后支持云打包生成安装包。

时间:2026-07-10 06:47
DCloud新手入门教程 从零开始快速掌握开发技巧

DCloud新手入门教程 从零开始快速掌握开发技巧

DCloud是一个基于Web技术的跨平台应用开发平台,允许开发者使用HTML5等语言编写代码,并编译为iOS、Android及小程序应用,实现“一次编写,多端发布”。开发需使用HBuilderX,基于Vue js进行页面开发,平台提供丰富组件与API,并支持插件市场扩展功能。

时间:2026-07-10 06:47
DCloud新手入门指南从零开始了解这个开发平台

DCloud新手入门指南从零开始了解这个开发平台

DCloud是一个移动应用开发平台,旨在帮助前端开发者使用Web技术高效开发性能接近原生的跨平台应用。其核心产品包括HBuilderX开发环境和基于Vue js的uni-app框架,可实现一次开发、多端发布。平台通过完整工具链和插件生态降低开发门槛,适合需要快速迭代、覆盖多端的应用场景。

时间:2026-07-10 06:47
Zepto实战项目优化技巧与应用场景详解

Zepto实战项目优化技巧与应用场景详解

Zepto js作为轻量级JavaScript库,专为移动端优化,体积仅约10KB。它支持模块化定制,可按需构建以减小体积。使用时需减少DOM操作、善用事件委托,并可混合原生API提升性能。Zepto能融入现代工程流,但需注意其与jQuery的API差异及兼容性限制,适合在移动端性能优先的场景中替代jQuery。

时间:2026-07-10 06:47
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 热门数据榜