别再往 setup 里堆逻辑了:Vue3 组合式函数封装实战

之前折腾过一个中后台项目,列表页写到最后有点绷不住了。

一个页面,要加载数据、要分页、要筛选、要刷新,我图省事全堆在 setup 里。刚开始还行,需求一加,几百行下去,改一处动全身。盯着屏幕发了会儿呆,决定还是得抽。

这篇记一下我把它抽成组合式函数(composable)的过程。不是什么高深玩意,但踩的坑挺真实,写出来给自己提个醒,也希望能帮到同样乱过的人。

一、先看看乱成什么样

大概长这样(简化过):

<script setup>
import { ref, onMounted } from 'vue'
import { getUserList } from '@/api'

const list = ref([])
const loading = ref(false)
const page = ref(1)
const size = ref(10)
const total = ref(0)
const keyword = ref('')

const load = async () => {
  loading.value = true
  try {
    const res = await getUserList({ page: page.value, size: size.value, keyword: keyword.value })
    list.value = res.data.list
    total.value = res.data.total
  } finally {
    loading.value = false
  }
}

const onSearch = () => { page.value = 1; load() }
const onPageChange = (p) => { page.value = p; load() }
const onReset = () => { keyword.value = ''; page.value = 1; load() }

onMounted(load)
</script>

一个页面这样也就算了。问题是我这个项目里,这种列表页有十几个,每个都差不多,又都有点小差别。复制粘贴吧,改一个 bug 要改十几个地方;不复制吧,每个都得重写一遍。

二、思路:把"列表"这件事抽出去

组合式函数说白了,就是把一堆相关的响应式状态和操作,从组件里挪进一个 useXxx 函数。组件只管用,不管它内部怎么实现。

我抽了一个 useList

// useList.js
import { ref } from 'vue'

export function useList(fetchFn) {
  const list = ref([])
  const loading = ref(false)
  const page = ref(1)
  const size = ref(10)
  const total = ref(0)
  const keyword = ref('')

  const load = async () => {
    loading.value = true
    try {
      const res = await fetchFn({ page: page.value, size: size.value, keyword: keyword.value })
      list.value = res.data.list
      total.value = res.data.total
    } finally {
      loading.value = false
    }
  }

  const search = () => { page.value = 1; load() }
  const reset = () => { keyword.value = ''; page.value = 1; load() }
  const pageChange = (p) => { page.value = p; load() }

  return { list, loading, page, size, total, keyword, load, search, reset, pageChange }
}

组件里就清爽多了:

<script setup>
import { onMounted } from 'vue'
import { getUserList } from '@/api'
import { useList } from './useList'

const { list, loading, total, keyword, search, reset, pageChange } = useList(getUserList)

onMounted(search)
</script>

数据怎么拿、分页怎么算,组件根本不用管,调一句 useList(getUserList) 就行。十几个列表页,每个只要一行。

三、踩过的几个坑

看着简单,真抽的时候还是翻了几个车。

1. 解构把响应式弄丢了

这是最容易踩的。如果你 return 的是一个 reactive 对象,组件里一解构,响应式就没了,页面怎么都不更新,你还以为是接口没返回。

我的解法是:组合式函数里单个值全用 ref,return 出去让组件自己解构。ref 解构出来还是响应式的,基本不会出错。

这块我其实也不是 100% 摸透了所有边界,但「ref 优先」这个习惯养成了,至今没再翻过车。

2. 参数传 ref 还是传值

fetchFn 如果依赖外部条件(比如要带上路由里的 id),你直接传个字符串进去,useList 里是不会跟着变的。要么把 ref/computed 传进去,要么干脆把这个条件也交给 useList 管。

我图省事,一般把查询条件都塞进 useList 的状态里,组件只管调方法。

3. loading 别忘了放 finally

loading.value = false 要是放在 try 末尾,接口一报错,loading 就卡死在那转圈。这个坑我踩过——页面一直 loading,我还以为是接口挂了,查了半天才发现是状态没复位。放进 finally 里就稳了。

四、不是所有逻辑都得抽

抽爽了容易上头,见啥都想抽。后来我给自己立了条规矩:两个地方以上要用,或者一块逻辑明显能独立成"一件事",才抽。就用一次的、跟当前页面强绑定的,留在 setup 里也挺好,没必要为了抽而抽。

这套 useList 我现在也还在调——要不要内置防抖、要不要缓存上一页数据,都没完全定。先这么用着,踩到新坑再说。


就这些。如果你也在 setup 里堆过几百行,希望这篇能给你个下手的思路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妍思码匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值