ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

usePageList:把分页列表逻辑封装成组合式函数,业务代码少一半

usePageList:把分页列表逻辑封装成组合式函数,业务代码少一半 usePageList把分页列表逻辑封装成组合式函数业务代码少一半导读Vue3 项目里列表页是最多的——职位列表、简历列表、消息列表每个页面都要写 loading、页码、上拉加载、搜索重置代码重复到吐。我封装了一个 usePageList 组合式函数业务页面的列表代码从 80 行缩到 20 行。这篇讲实现和踩坑。先说背景。job-uniapp 的管理端Vue3 Vite Pinia里有十几个列表页最开始每页都是一坨重复代码page、pageSize、total、loading、list、loadMore()、refresh()、search()复制粘贴改字段名。后来加需求每个页面都要改一遍实在受不了就抽了个 composables。为什么需要封装列表页看起来简单写起来全是重复劳动。加载中状态、分页参数、到底了没有、搜索重置页码每个页面都要来一遍。我一开始图省事直接复制后来一个需求改动十几个页面跟着改改到怀疑人生。抽出 composables 之后这些状态和逻辑收敛到一处业务页面只关心请求什么数据和怎么展示。设计思路usePageList 的核心传入一个返回分页数据的请求函数返回页面需要的所有状态和方法。请求函数必须返回{ list, total }结构后端接口统一格式没有就自己适配。// usePageList.jsimport{ref}fromvueexportfunctionusePageList(requestFn,options{}){constlistref([])constpageref(1)constpageSizeoptions.pageSize||10consttotalref(0)constloadingref(false)constfinishedref(false)// 加载列表mode: refresh | loadMoreasyncfunctionload(moderefresh){if(loading.value)return// 上拉加载时没有更多数据了直接返回if(modeloadMorefinished.value)returnloading.valuetruetry{consttargetPagemoderefresh?1:page.value1constresawaitrequestFn({page:targetPage,pageSize})if(moderefresh){list.valueres.list||[]page.value1}else{list.value.push(...(res.list||[]))page.valuetargetPage}total.valueres.total||0finished.valuelist.value.lengthtotal.value}finally{loading.valuefalse}}asyncfunctionrefresh(){awaitload(refresh)}asyncfunctionloadMore(){awaitload(loadMore)}// 搜索/筛选重置页码重新加载asyncfunctionsearch(){list.value[]total.value0finished.valuefalseawaitload(refresh)}return{list,total,loading,finished,refresh,loadMore,search}}页面里怎么用import { onMounted } from vue import { usePageList } from /composables/usePageList import { getJobList } from /api/job // 请求函数后端返回 { list, total } const { list, total, loading, finished, refresh, loadMore, search } usePageList((params) { return getJobList({ ...params, keyword: keyword.value }) }) onMounted(refresh)搜索框变化时functionhandleSearch(){search()// 页码自动重置重新请求}这样每个列表页只写请求函数和模板状态管理全部复用。踩坑记录坑一组件卸载后异步回调报错现象列表页快速切换到其他页面控制台报[Vue warn]: Operation failed: component is no longer mounted。原因请求还没返回组件已经销毁list.value res.list还在执行。解决组件卸载时清掉未完成的请求。用 AbortController 或者在 composable 里记录卸载状态import{ref,onUnmounted,getCurrentInstance}fromvue// 简化方案请求函数支持 cancel 标记letcancelledfalse// 在 load 的 await 之后判断if(cancelled)return// 页面组件里onUnmounted((){cancelledtrue})更规范的做法是请求层支持 AbortControllerVue3 里 axios 直接传signal。坑二搜索和加载更多竞态现象用户快速输入关键词搜索第一次搜索的慢请求先发、后返回把第二次搜索的结果覆盖了。列表显示的是旧关键词的数据。定位经典竞态问题旧请求比新请求晚返回。解决请求序号自增只有最新一次请求的结果才生效letrequestSeq0asyncfunctionload(mode){constseqrequestSeq// ...constresawaitrequestFn(...)if(seq!requestSeq)return// 有更新的请求丢弃这次结果// ...}坑三搜索后页码没重置翻页数据错乱现象搜索后直接调 loadMorepage 还是搜索前的值请求参数带了旧页码。解决search() 里先把 page 重置为 1上面代码里load(refresh)已经处理但要确保业务代码别在 search 之后手动改 page。坑四请求函数里闭包捕获旧值现象keyword 是响应式 ref但请求函数里直接读keyword.value搜索后立刻调 loadMorekeyword 还是旧的。这个不算 composable 的锅是闭包特性。解决请求函数每次调用时从 ref 重新取值上面代码keyword.value是在请求函数内部读取的天然正确别在 composable 初始化时就把 keyword 的值传进去。可直接复用请求函数统一返回{ list, total }后端格式不一致就在请求函数里适配composable 里做 loading/finished/页码管理页面只写模板和请求竞态用请求序号解决seq ! requestSeq丢弃旧结果组件卸载防报错记录 cancelled 标志或 AbortController搜索/筛选必须重置页码通过统一的 search() 方法而不是手动改 page这套 usePageList 我现在所有列表页都在用加上骨架屏组件列表页开发速度提升明显。Vue3 的 composables 就是干这个的——把状态逻辑抽出来复用别每个页面复制粘贴。
返回列表