稀土掘金技术社区 09月25日
分页Hook简化列表页开发
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

后台管理系统开发中,分页列表查询非常常见,但处理当前页、页大小、总数等分页状态,以及加载中、错误处理等请求状态,往往需要重复编写大量代码。为了解决这个烦恼,我封装了分页数据管理 Hook,只需几行代码就能轻松实现分页查询,减少90%的重复劳动。

📚 现在只需几行代码就能轻松实现分页查询,减少90%的重复劳动。

🔄 封装了分页逻辑和状态管理,包括当前页、页大小、总数等分页状态,以及加载中、错误处理等请求状态。

🔧 提供「useFetch」和「usePageFetch」两个相互配合的 Hook,分别处理基础请求封装和分页逻辑封装。

🚀 支持智能缓存机制,避免重复请求,提高开发效率。

🔄 适用于各种后台管理系统开发场景,易于扩展和使用。

原创 不一样的少年_ 2025-09-19 08:30 重庆

点击关注公众号,技术干货及时达!

还在为每个列表页写重复的分页代码而烦恼吗? 还在复制粘贴 currentPage、pageSize、loading 等状态吗? 一个 Hook 帮你解决所有分页痛点,减少90%重复代码

背景与痛点在后台管理系统开发中,分页列表查询非常常见,我们通常需要处理:

当前页、页大小、总数等分页状态

加载中、错误处理等请求状态

搜索、刷新、翻页等分页操作

数据缓存和重复请求处理

这些重复逻辑分散在各个组件中,维护起来很麻烦。

为了解决这个烦恼,我专门封装了分页数据管理 Hook。现在只需要几行代码,就能轻松实现分页查询,省时又高效,减少了大量重复劳动

使用前提 - 接口格式约定查询接口返回的数据格式:

{  list: [        // 当前页数据数组    { id1name'user1' },    { id2name'user2' }  ],  total100     // 数据总条数}
先看效果:分页查询只需几行代码!
import usePageFetch from'@/hooks/usePageFetch'// 引入分页查询 Hook,封装了分页逻辑和状态管理import { getUserList } from'@/api/user'        // 引入请求用户列表的 API 方法// 使用 usePageFetch Hook 实现分页数据管理const {  currentPage,      // 当前页码  pageSize,         // 每页条数  total,            // 数据总数  data,             // 当前页数据列表  isFetching,       // 加载状态,用于控制 loading 效果  search,           // 搜索方法  onSizeChange,     // 页大小改变事件处理方法  onCurrentChange   // 页码改变事件处理方法} = usePageFetch(  getUserList,  // 查询API  { initFetchfalse }  // 是否自动请求一次(组件挂载时自动拉取第一页数据)     )
这样子每次分页查询只需要引入hook,然后传入查询接口就好了,减少了大量重复劳动

解决方案我设计了两个相互配合的 Hook:

「useFetch」:基础请求封装,处理请求状态和缓存

「usePageFetch」:分页逻辑封装,专门处理分页相关的状态和操作

usePageFetch (分页业务层)├── 管理 page / pageSize / total 状态├── 处理搜索、刷新、翻页逻辑  ├── 统一错误处理和用户提示└── 调用 useFetch (请求基础层)    ├── 管理 loading / data / error 状态    ├── 可选缓存机制(避免重复请求)    └── 成功回调适配不同接口格式
核心实现useFetch - 基础请求封装
// hooks/useFetch.jsimport { ref } from'vue'const Cache = newMap()/** * 基础请求 Hook * @param {Function} fn - 请求函数 * @param {Object} options - 配置选项 * @param {*} options.initValue - 初始值 * @param {string|Function} options.cache - 缓存配置 * @param {Function} options.onSuccess - 成功回调 */function useFetch(fn, options = {}{const isFetching = ref(false)const data = ref()const error = ref()// 设置初始值if (options.initValue !== undefined) {    data.value = options.initValue  }function fetch(...args{    isFetching.value = true    let promise    if (options.cache) {      const cacheKey = typeof options.cache === 'function'        ? options.cache(...args)        : options.cache || `${fn.name}_${args.join('_')}`      promise = Cache.get(cacheKey) || fn(...args)      Cache.set(cacheKey, promise)    } else {      promise = fn(...args)    }    // 成功回调处理    if (options.onSuccess) {      promise = promise.then(options.onSuccess)    }    return promise      .then(res => {        data.value = res        isFetching.value = false        error.value = undefined        return res      })      .catch(err => {        isFetching.value = false        error.value = err        returnPromise.reject(err)      })  }return {    fetch,    isFetching,    data,    error  }}exportdefault useFetch
usePageFetch - 分页逻辑封装
// hooks/usePageFetch.jsimport { ref, onMounted, toRaw, watch } from'vue'import useFetch from'./useFetch'// 即上面的hook ---> useFetch import { ElMessage } from'element-plus'/** * 分页数据管理 Hook * @param {Function} fn - 请求函数 * @param {Object} options - 配置选项 * @param {Object} options.params - 默认参数 * @param {boolean} options.initFetch - 是否自动初始化请求 * @param {Ref} options.formRef - 表单引用 */function usePageFetch(fn, options = {}{// 分页状态const page = ref(1)const pageSize = ref(10)const total = ref(0)const data = ref([])const params = ref()const pendingCount = ref(0)// 初始化参数  params.value = options.params//  使用基础请求 Hookconst { isFetching, fetch: fetchFn, error, data: originalData } = useFetch(fn)//  核心请求方法const fetch = async (searchParams, pageNo, size) => {    try {      // 更新分页状态      page.value = pageNo      pageSize.value = size      params.value = searchParams      // 发起请求      await fetchFn({        page: pageNo,        pageSize: size,        // 使用 toRaw 避免响应式对象问题        ...(searchParams ? toRaw(searchParams) : {})      })      // 处理响应数据      data.value = originalData.value?.list || []      total.value = originalData.value?.total || 0      pendingCount.value = originalData.value?.pendingCounts || 0    } catch (e) {      console.error('usePageFetch error:', e)      ElMessage.error(e?.msg || e?.message || '请求出错')      // 清空数据,提供更好的用户体验      data.value = []      total.value = 0    }  }//  搜索 - 重置到第一页const search = async (searchParams) => {    await fetch(searchParams, 1, pageSize.value)  }// 刷新当前页const refresh = async () => {    await fetch(params.value, page.value, pageSize.value)  }// 改变页大小const onSizeChange = async (size) => {    await fetch(params.value, 1, size) // 重置到第一页  }// 切换页码const onCurrentChange = async (pageNo) => {    await fetch(params.value, pageNo, pageSize.value)  }// 组件挂载时自动请求  onMounted(() => {    if (options.initFetch !== false) {      search(params.value)    }  })// 监听表单引用变化(可选功能)  watch(    () => options.formRef,    (formRef) => {      if (formRef) {        console.log('Form ref updated:', formRef)      }    }  )return {    // 分页状态    currentPage: page,    pageSize,    total,    pendingCount,        // 数据状态    data,    originalData,    isFetching,    error,        // 操作方法    search,    refresh,    onSizeChange,    onCurrentChange  }}exportdefault usePageFetch
完整使用示例用element ui举例
<template>    <el-form :model="searchForm" >      <el-form-item label="用户名">        <el-input v-model="searchForm.username" />      </el-form-item>      <el-form-item>        <el-button type="primary" @click="handleSearch">搜索</el-button>      </el-form-item>    </el-form>  <!-- 表格数据展示,绑定 data 和 loading 状态 -->  <el-table :data="data" v-loading="isFetching">    <!-- ...表格列定义... -->  </el-table>  <!-- 分页组件,绑定当前页、页大小、总数,并响应切换事件 -->  <el-pagination    v-model:current-page="currentPage"    v-model:page-size="pageSize"    :total="total"    @size-change="onSizeChange"    @current-change="onCurrentChange"  /></template><script setup>import { ref } from 'vue'import usePageFetch from '@/hooks/usePageFetch' // 引入分页查询 Hook,封装了分页逻辑和状态管理import { getUserList } from '@/api/user'        // 引入请求用户列表的 API 方法// 搜索表单数据,响应式声明const searchForm = ref({  username: ''})// 使用 usePageFetch Hook 实现分页数据管理const {  currentPage,      // 当前页码  pageSize,         // 每页条数  total,            // 数据总数  data,             // 当前页数据列表  isFetching,       // 加载状态,用于控制 loading 效果  search,           // 搜索方法  onSizeChange,     // 页大小改变事件处理方法  onCurrentChange   // 页码改变事件处理方法} = usePageFetch(  getUserList,   { initFetch: false }  // 是否自动请求一次(组件挂载时自动拉取第一页数据)     )/*** 处理搜索操作*/const handleSearch = () => {  search({ username: searchForm.value.username })}</script>
高级用法带缓存
const {  data,  isFetching,  search} = usePageFetch(getUserList, {  cache(params) => `user-list-${JSON.stringify(params)}` // 自定义缓存 key})
设计思路解析「职责分离」:useFetch 专注请求状态管理,usePageFetch 专注分页逻辑

「统一错误处理」:在 usePageFetch 层统一处理错误

「智能缓存机制」:支持多种缓存策略

「生命周期集成」:自动在组件挂载时请求数据

总结这套分页管理 Hook 的优势:

开发效率高,减少90%的重复代码,新增列表页从 30 分钟缩短到 5 分钟

状态管理完善,自动处理加载、错误、数据状态

缓存机制,避免重复请求

错误处理统一,用户体验一致

易于扩展,支持自定义配置和回调

AI编程资讯AI Coding专区指南:

https://aicoding.juejin.cn/aicoding

""~

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

分页Hook Vue 前端开发 状态管理 提高效率
相关文章