7行代码处理axios数据一致性

发现问题

在前端开发中,有一个很常见的问题:如何在多次请求中,正确地处理响应数据和原始请求保持一致。 说起来有点拗口,我这里直接用一个简洁明了的例子来讲解。假如要写一个可分页的表格页面,它的原始代码如下:

<script lang="ts" setup>
const rows = ref<{id: number, name: string, age: number}[]>([])
const currentPage = ref(1)
const total = ref(0)

const getList = (page) => {
  axios.get(`/api/list?page=${page}`)
    .then(res => {
      rows.value = res.data.list
      total.value = res.data.total
  })
}

// 页码改变时,调用 getList
watch(currentPage, (page) => {
  // 这里多此一举把 page 传进去,后面自有用处
  getList(page)
}, { immediate: true })
</script>

<template>
  <table>
    <thead>
      <tr>
        <th>id</th>
        <th>name</th>
        <th>age</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="item in rows" :key="item.id">
        <td>{{ item.id }}</td>
        <td>{{ item.name }}</td>
        <td>{{ item.age }}</td>
      </tr>
    </tbody>
  </table>
  <!-- 假设这是一个分页组件 -->
  <Pagination v-model="currentPage" :total="total" />
</template>

以上代码很好理解,它潜在的问题是:如果当前在第1页,用户点击第2页,又在接口pending的时候点了第3页,好巧不巧,后端在响应第2页的时候卡了几秒才返回,但在响应第3页是秒返回的。在前端体现就是明明在第3页,显示的却是第2页的数据。

两个方案

  • 方案1:在请求列表的时候,把翻页按钮disabled掉,不让用户翻页了。
  • 方案2:在下一个请求发送时,取消上一个请求。

方案1不够优雅,我们这里考虑如何实现方案2。

实现

废话不多说,直接端上代码,主要用到的是闭包以及#axios取消请求:

export const useAbortController = <T extends Array<unknown>, R = any>(fn: (controller?: AbortController, ...args: T) => Promise<AxiosResponse<R>>) => {
  // 在闭包里存一个 AbortController
  let controller: AbortController | undefined
  return (...args: T) => {
    // 取消上一个请求(如果有的话)
    if (controller) {
      controller.abort();
    }
    // 重新创建一个 AbortController,并传给实际要调的函数
    controller = new AbortController()
    return fn(controller, ...args)
  }
}

有了上面的功能函数,之前的代码可做如下修改:

<script lang="ts" setup>
const rows = ref<{id: number, name: string, age: number}[]>([])
const currentPage = ref(1)
const total = ref(0)

// 用page参数成功演示了如何给闭包内的函数传参
const getList = useAbortController<[page: number]>((controller, page) => {
  // 把 controller.signal 传给 axios 后,调用 controller.abort() 就能取消请求了
  axios.get(`/api/list?page=${page}`, { signal: controller?.signal })
    .then(res => {
      rows.value = res.data.list
      total.value = res.data.total
  })
})

// 页码改变时,调用 getList
watch(currentPage, (page) => {
  getList(page)
}, { immediate: true })
</script>

<template>
  <!-- 不做改变 -->
</template>

以上可以完美实现响应数据和原始请求保持一致,简单实用。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值