useList通用列表管理Hook

作者:技术拾光

时间:2026-05-28

浏览:299

useList 通用列表管理hook 在开发列表页面时,重复的分页、筛选、加载状态管理往往耗费大量精力。每次都需要手动处理 loading 状态、维护分页参数、决定数据追加或替换逻辑。为解决这些问题,useList 通用列表管理 hook 应运而生,专门统一管理列表相关逻辑。 核心功能 该 Vue

文章目录

useList 通用列表管理hook

在开发列表页面时,重复的分页、筛选、加载状态管理往往耗费大量精力。每次都需要手动处理 loading 状态、维护分页参数、决定数据追加或替换逻辑。为解决这些问题,useList 通用列表管理 hook 应运而生,专门统一管理列表相关逻辑。

useList通用列表管理Hook

核心功能

该 Vue 自定义 hook 能够显著减少重复代码,主要功能包括:

  1. 响应式状态管理:使用 reactive 封装筛选条件和分页配置,修改一处即可联动全局,无需手动同步。
  2. 数据请求封装:集成 loading 状态、错误捕获、数据赋值等流程,无需在组件中重复编写 try catch 逻辑。
  3. 追加/替换模式:通过 isPush 参数,既支持普通分页翻页替换,也适用于滚动加载持续追加场景。
  4. 筛选重置:保存初始筛选条件,支持一键恢复初始状态,优化用户体验。

详细代码

以下为完整代码实现,关键部分已添加注释:

import { reactive, ref, toRaw } from 'vue'export interface IPage {
  pageNum: number
  pageSize: number
  total: number
}export interface GetListFnArgsType {
  pagination: IPage
  filter: T
}
export default function useListextends Record<string, any>>(
  filterOption: U,
  getListFn: (args: GetListFnArgsType) => Promise<InResult<{ records: Array, total: number }>>, // InResult 类型定义请阅读uni.request 二次封装这篇文章
  pageOption: IPage,
) {
  const list = ref<Array>([])
  const loading = ref(false)
  const filter = reactive({ ...filterOption })
  const pagination = reactive<IPage>({
    ...pageOption,
    pageSize: pageOption.pageSize || 10,
    pageNum: pageOption.pageNum || 1,
  })  // 请求列表数据
  const loadData = async (curPage = pagination.pageNum, isPush = true) => {
    pagination.pageNum = curPage
    loading.value = true    const req = { pagination, filter: toRaw(filter) as U }
    try {
      const res = await getListFn(req as GetListFnArgsType)
      loading.value = false
      if (!isPush) {
        list.value = res.data.records as Array<any>
      }
      else {
        list.value = [...list.value, ...res.data.records as Array<any>]
      }
      pagination.total = res.data.total
    }
    catch (error) {
      loading.value = false
      console.log(error)
    }
  }  // 重置筛选
  const filterReset = () => {
    Object.assign(filter, filterOption)
    loadData(1, false)
  }  // 搜索
  const filterSearch = () => {
    console.log(121212)
    return loadData(1, false)
  }  // 分页请求
  const loadDataPage = (page: number, isPush = true) => {
    return loadData(page, isPush)
  }  // 分页大小改变
  const loadDataPageSize = (pageSize: number) => {
    pagination.pageSize = pageSize
    loadData(1, false)
  }  return {
    list,
    loading,
    filter,
    pagination,
    loadData,
    loadDataPage,
    loadDataPageSize,
    filterReset,
    filterSearch,
  }
}

类型定义

IPage

分页参数的基础接口,包含三个常规属性:

interface IPage {
  pageNum: number // 当前页码
  pageSize: number // 每页条数
  total: number // 总条数(响应数据填充)
}

GetListFnArgsType

请求函数接收的参数类型,将分页和筛选整合在一起:

interface GetListFnArgsType {
  pagination: IPage // 分页信息
  filter: T // 筛选条件
}

返回值

调用该 hook 后,可获取以下属性和方法:

属性类型说明
listRef>列表数据
loadingRef加载状态
filterReactive筛选条件(响应式)
paginationReactive分页信息(响应式)
loadData(curPage?: number, isPush?: boolean) => Promise加载数据
loadDataPage(page: number, isPush?: boolean) => Promise分页加载
loadDataPageSize(pageSize: number) => void改变每页条数
filterReset() => void重置筛选条件
filterSearch() => Promise搜索(重置页码为1)

方法详解

loadData(curPage, isPush)

核心加载方法,需传入目标页码及是否追加数据:

  • curPage:默认值为当前分页页码,通常无需传入。
  • isPush:true 表示将新数据追加到现有列表末尾(适用于滚动加载),false 则直接替换列表数据(适用于常规翻页)。

loadDataPage(page, isPush)

直接调用 loadData 方法的封装,方便在模板中绑定事件处理。

loadDataPageSize(pageSize)

当用户切换每页条数时,更新分页配置并立即请求第一页数据。

filterReset()

将筛选条件重置为初始值,并重新加载第一页数据。采用覆盖式重置而非合并。

filterSearch()

搜索按钮触发时,将页码重置为 1 后重新请求数据,确保筛选条件变更后结果从第一页开始展示。

使用示例

以下为实际项目案例,管理一个"事件列表",筛选条件包含关键词、状态、起止时间:

import type { IEvent } from '@/api/task/type'
import { getEventList } from '@/api/task'
import useList, { type GetListFnArgsType } from '@/hooks/uesList.hook'const filterOption = {
  keyword: '',
  status: '',
  startTime: '',
  endTime: '',
}async function getListData(params: GetListFnArgsType<typeof filterOption>) {
  const { pagination, filter } = params
  const req = {
    params: {
      pageNo: pagination.pageNum,
      pageSize: pagination.pageSize,
    },
    data: {
      startTime: filter.startTime,
      endTime: filter.endTime,
    },
  }  const res = await getEventList(req)
  return res  /**
   * 这里假设接口返回的数据格式不是 records 和 total
   * 你可以在 getListData 中根据实际情况调整 records 和 total 的赋值,如:
   * const res = await getEventList(req);
   * return {data: { records: res.data.list, total: res.data.totalSize }}
   */
}const { list, filter, pagination, filterSearch, loadDataPage } = useList<IEvent, typeof filterOption>(filterOption, getListData, { pageNum: 1, pageSize: 20 })

在模板中,可配合自定义列表组件 cus-list 使用,将筛选条件绑定到 filter,分页事件绑定到 loadDataPage:

<template>
  <cus-list v-model:keyword="filter.keyword" :refresh-func="filterSearch" :load-func="loadDataPage" :pagination="pagination" @search="filterSearch" @clear="filterSearch">
    <template #serach-left>
      <van-dropdown-menu style="--dropdown-menu-title-active-text-color: var(--uni-color-primary);--dropdown-menu-background-color: transparent;--dropdown-menu-title-text-color: var(--uni-text-color); --dropdown-menu-option-active-color: var(--uni-color-primary)" custom-class="min-w-[100rpx]">
        <van-dropdown-item v-model:value="filter.siteId" :options="siteIdOptions" @change="onSiteChange" />
      van-dropdown-menu>
    template>    <template #search-filter="{ data }">
      <view class="box-border h-full w-full p-2">
        <view class="flex items-center gap-2">
          <view>开始时间:view>
          <cus-date-picker v-model="filter.startTime" type="date" />
        view>
        <view class="mt-2 flex items-center gap-2">
          <view>结束时间:view>
          <cus-date-picker v-model="filter.endTime" type="date" />
        view>
        
        <view class="mt-5 flex gap-2">
          <van-button size="small" block class="flex-1" @click="onFilterReset(data)">
            重置
          van-button>
          <van-button type="primary" block size="small" class="flex-1" @click="onFilter(data)">
            确定
          van-button>
        view>
      view>
    template>    <template #default>
      <task-item v-for="item in list" :key="item.id" :record="item" />
    template>
  cus-list>
template>

整个链路中,数据绑定和事件处理均与 hook 对齐,代码量显著减少,且逻辑集中维护,便于后期修改。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章