订单、消息、资讯类列表都要做分页。UniApp 页面自带 onReachBottom 生命周期,不必手动监听滚动;只要把页码、列表、是否还有下一页等状态收敛成组合式函数,任意列表页都能复用同一套逻辑。

实现思路

维护 page 与 hasMore 两个状态:加载成功后若返回条数小于 pageSize 则认为没有下一页;onReachBottom 里判断加载中或没有更多直接 return,否则 page 加一再请求;下拉刷新时把 page 重置为 1 并清空列表。

封装组合式函数

// composables/usePagedList.js
import { ref } from "vue";

export function usePagedList(apiFn, pageSize = 10) {
    const list = ref([]);
    const page = ref(1);
    const loading = ref(false);
    const finished = ref(false);
    const refreshing = ref(false);

    async function load(append = true) {
        if (loading.value || finished.value) return;
        loading.value = true;
        try {
            const data = await apiFn(page.value, pageSize);
            const records = data.records || data.list || [];
            list.value = append ? [...list.value, ...records] : records;
            if (records.length < pageSize) finished.value = true;
        } finally {
            loading.value = false;
            refreshing.value = false;
        }
    }

    function refresh() {
        page.value = 1;
        finished.value = false;
        return load(false);
    }

    function next() {
        page.value += 1;
        return load(true);
    }

    return { list, loading, finished, refreshing, refresh, next };
}

页面接入

// pages/news/list.vue
import { http } from "/utils/request";
import { usePagedList } from "/composables/usePagedList";

const { list, loading, finished, refreshing, refresh, next } = usePagedList(
    (page, size) => http.get("/news/list", { page, size }),
    15
);

// 生命周期钩子
onLoad(() => refresh());
onReachBottom(() => next());
onPullDownRefresh(() => {
    refresh().finally(() => uni.stopPullDownRefresh());
});

模板状态渲染

底部用统一判断渲染三种状态:加载中、没有更多、空数据,页面无需各自重复写。

<template>
    <view v-for="item in list" :key="item.id" class="news-item">
        {{ item.title }}
    </view>
    <view v-if="loading" class="list-footer">加载中...</view>
    <view v-else-if="finished && list.length" class="list-footer">没有更多了</view>
    <view v-else-if="finished && !list.length" class="list-empty">暂无数据</view>
    <button v-if="error" @click="refresh">加载失败,点击重试</button>
</template>

pages.json 配置

下拉刷新需在 pages.json 对应页面开启 enablePullDownRefresh,并把列表页 style 中的开关置为 true。

需要注意

  • onReachBottom 只在整页滚动时触发,若列表包在 scroll-view 内需改用 scrolltolower 事件;
  • 防重复:触底事件可能连续触发多次,loading 判断必须放在函数最前;
  • 切换 Tab 或条件筛选变化时先调用 refresh,避免残留上一条件的数据;
  • 后端接口分页参数命名不一时,在 apiFn 里做一次适配即可,不影响复用。

小结

把分页逻辑收进组合式函数后,新列表页只需要传接口函数并渲染 list,上拉、下拉、空态全部现成,能大幅减少重复样板代码。