早点下班:在 Vue3 中少写 40%+ 的异步代码

作为前端开发者,我们每天都在和异步操作打交道 ------ 发起 API 请求、处理表单提交、管理数据加载状态... 但你有没有发现,写这些代码时总在重复同样的逻辑?

"定义 loading 变量、定义 error 变量、调用函数时设 loading 为 true、成功后更新数据、失败后记录错误、结束后设 loading 为 false、还要处理竞态条件..." 这些样板代码占用了大量时间,却几乎没有技术含量。

于是我开发了「vue-asyncx」------ 一个专注于简化 Vue3 异步操作的工具库,让你少写 40%+ 的重复代码,早点下班陪女朋友 / 打游戏 / 休息😎。

为什么需要 vue-asyncx?

先看一个常见场景:用 Vue3 的 Composition API 获取用户信息。传统写法大概是这样的:

ts 复制代码
<script setup>
import { ref } from 'vue'
import { getUserApi } from './api'
// 1. 定义一堆状态变量
const user = ref(null)
const loading = ref(false)
const error = ref(null)
// 2. 编写异步函数
const queryUser = async (userId) => {
  // 3. 手动处理状态更新
  loading.value = true
  error.value = null
  
  try {
    // 4. 执行异步操作
    const res = await getUserApi(userId)
    user.value = res.data
    return res.data
  } catch (e) {
    // 5. 处理错误
    error.value = e
    throw e
  } finally {
    // 6. 清理状态
    loading.value = false
  }
}
</script>

这段代码里,真正有价值的逻辑只有getUserApi(userId)这一行,其余全是重复的状态管理代码。更麻烦的是:

  • 每个异步操作都要复制这套逻辑,代码量爆炸
  • 变量命名风格不统一,团队协作成本高
  • 手动处理竞态条件(多次请求时数据覆盖问题)容易出错

而用 vue-asyncx 实现同样的功能,只需要:

ts 复制代码
<script setup>
import { useAsyncData } from 'vue-asyncx'
import { getUserApi } from './api'
// 一行代码搞定所有状态管理
const { user, queryUser, queryUserLoading, queryUserError } = useAsyncData('user', getUserApi)
</script>

这就是 vue-asyncx 的核心价值:自动处理异步操作的所有周边逻辑,用命名约定提升协作效率

自定义变量也有类型提示

核心功能:让异步操作 "开箱即用"自动处理异步操作的所有周边逻辑,

vue-asyncx 提供了两个核心 API,覆盖 90%+ 的异步场景:

1. useAsyncData:专注异步数据

当你需要使用异步数据时,用useAsyncData。它会自动生成:

  • {name}:存储异步数据 Ref(如user)
  • query{Name}:触发异步数据获取的函数(如queryUser)
  • query{Name}Loading:加载状态 Ref(如queryUserLoading)
  • query{Name}Error:错误信息 Ref(如queryUserError)
  • query{Name}Arguments:最近一次调用过程中的传参
  • {name}Expired:当前异步数据是否过期(因后续请求失败导致)

基础用法

ts 复制代码
import { useAsyncData } from 'vue-asyncx'
import { getArticleApi } from './api'
// 管理文章数据
const { 
  article,         // 文章数据 (Ref)
  queryArticle,    // 获取文章的函数
  queryArticleLoading, // 加载状态
  queryArticleError    // 错误信息
} = useAsyncData('article', getArticleApi)
// 调用函数获取数据
queryArticle(123) // 获取id=123的文章

其它特性

  • 初始值设置:useAsyncData('user', getUserApi, { initialData: { name: '默认' } })
  • 自动监听:当依赖变化时自动执行(类似 watch)
ts 复制代码
const userId = ref(1)
useAsyncData('user', getUserApi, { 
  watch: userId, // userId变化时自动调用queryUser(userId.value)
  immediate: true // 初始时立即执行
})
  • 过程中更新数据:支持在异步函数执行过程中手动更新结果
ts 复制代码
const { progress, queryProgress } = useAsyncData('progress', async (init = 0) => {
  const { updateData } = getAsyncDataContext() // 获取上下文
  updateData(init) // 立即更新为初始值
  await wait(100)
  updateData(50) // 中途更新为50%
  await wait(100)
  return 100 // 最终结果
})

2. useAsync:专注异步函数

当你只需要使用异步函数(不需要长久保持结果),比如表单提交、数据删除等操作场景,用useAsync。它会生成:

  • {name}:包装后的异步函数(如submit)
  • {name}Loading:加载状态 Ref(如submitLoading)
  • {name}Error:错误信息 Ref(如submitError)
  • {name}Arguments:最近一次调用过程中的传参

表单提交示例

ts 复制代码
<script setup>
import { useAsync } from 'vue-asyncx'
import { submitFormApi } from './api'
// 管理提交操作
const { 
  submit,       // 提交函数
  submitLoading, // 提交状态
  submitError    // 提交错误
} = useAsync('submit', submitFormApi)
</script>
<template>
  <form @submit.prevent="submit(formData)">
    <button type="submit" :disabled="submitLoading">
      {{ submitLoading ? '提交中...' : '提交' }}
    </button>
    <div v-if="submitError" class="error">
      {{ submitError.message }}
    </div>
  </form>
</template>

3. 自动处理竞态条件

当一个异步函数被快速连续调用(比如用户快速点击按钮),可能出现 "后发请求先返回,先发请求后覆盖" 的竞态问题,导致数据混乱。

vue-asyncx 内置了竞态处理机制,通过调用追踪,确保只有最后一次调用的结果会更新状态,前面的请求结果会被自动忽略。

ts 复制代码
// 模拟一个延迟返回的API
const fetchData = (id) => new Promise(resolve => 
  setTimeout(() => resolve(id), 1000)
)
const { data, queryData } = useAsyncData('data', fetchData)
// 快速连续调用
queryData(1)
queryData(2) // 最后一次调用,结果会是2
// 1秒后,data.value 会是2(而不是1),自动忽略了第一次调用的结果

实战场景:代码量对比

我们用 "用户列表 + 详情" 的经典场景,看看 vue-asyncx 能省多少代码。

传统实现(约 50 行)

ts 复制代码
<script setup>
import { ref, watch } from 'vue'
import { getUsersApi, getUserDetailApi } from './api'
// 列表相关状态
const users = ref([])
const getUsersLoading = ref(false)
const getUsersError = ref(null)
// 详情相关状态
const userDetail = ref(null)
const getUserDetailLoading = ref(false)
const getUserDetailError = ref(null)
const currentUserId = ref(null)
// 获取列表
const getUsers = async () => {
  getUsersLoading.value = true
  getUsersError.value = null
  try {
    const res = await getUsersApi()
    users.value = res.data
    return res.data
  } catch (e) {
    getUsersError.value = e
    throw e
  } finally {
    getUsersLoading.value = false
  }
}
// 获取详情
const getUserDetail = async (userId) => {
  getUserDetailLoading.value = true
  getUserDetailError.value = null
  try {
    const res = await getUserDetailApi(userId)
    userDetail.value = res.data
    return res.data
  } catch (e) {
    getUserDetailError.value = e
    throw e
  } finally {
    getUserDetailLoading.value = false
  }
}
// 监听用户ID变化,自动加载详情
watch(currentUserId, (id) => {
  if (id) getUserDetail(id)
})
// 初始加载列表
getUsers()
</script>

vue-asyncx 实现(约 20 行)

ts 复制代码
<script setup>
import { ref } from 'vue'
import { useAsyncData } from 'vue-asyncx'
import { getUsersApi, getUserDetailApi } from './api'
// 列表管理(自动生成getUsers、users等)
const { 
  users, 
  getUsers, 
  getUsersLoading, 
  getUsersError 
} = useAsyncData('users', getUsersApi, { immediate: true })
// 详情管理(自动监听currentUserId变化)
const currentUserId = ref(null)
const { 
  userDetail, 
  getUserDetail, 
  getUserDetailLoading, 
  getUserDetailError 
} = useAsyncData('userDetail', getUserDetailApi, { 
  watch: currentUserId, // 自动监听
})
</script>

代码量减少60% ,而且逻辑更清晰 ------ 所有状态都和对应的异步操作强关联,不用在多个 ref 之间跳来跳去。

为什么选择 vue-asyncx?

  1. 更少的代码:平均减少 40%+ 的异步相关代码,专注业务逻辑
  2. 更强的可读性:统一的命名约定(如queryXxx、xxxLoading)让代码自文档化
  3. 零成本维护:自动处理状态更新、竞态条件,减少 bug
  4. 完整的 TypeScript 支持:所有 API 都有精确的类型定义,IDE 自动提示
  5. 轻量无依赖:仅依赖 Vue3,体积极小(gzip 后 ~2KB)
  6. 100% 测试覆盖:200+ 测试用例确保稳定性

如何开始使用?

  1. 安装依赖:
sh 复制代码
pnpm i vue-asyncx
# 或 npm i vue-asyncx
# 或 yarn add vue-asyncx
  1. 在组件中使用:
ts 复制代码
import { useAsync, useAsyncData } from 'vue-asyncx'

详细文档和更多示例见:GitHub 仓库

社区贡献:一起让它更好

vue-asyncx 还在不断进化,如果你有任何想法或需求,欢迎参与贡献:

  • 提 Issue:报告 bug 或建议新功能
  • 发 PR:修复 bug 或实现新功能(欢迎新手参与)
  • 分享体验:在博客或社交平台分享你的使用心得

项目地址:github.com/xuyimingwor...

最后

开发 vue-asyncx 的初衷,就是想让自己和更多开发者从重复的异步状态管理中解放出来 ------ 毕竟,好的工具应该让你感觉不到它的存在,却能悄悄帮你搞定琐事。

希望 vue-asyncx 能让你少加班、多陪家人、多打游戏,早点下班😊。

如果觉得有用,欢迎给个 Star 支持一下~

相关推荐
老华带你飞3 小时前
健身房预约|基于springboot 健身房预约小程序系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·小程序
paopaokaka_luck3 小时前
基于SpringBoot+Uniapp的自习室预约小程序(腾讯地图API、Echarts图形化分析、二维码识别)
vue.js·spring boot·后端·spring·echarts
博客zhu虎康3 小时前
Vue全局挂载Element消息组件技巧
前端·javascript·vue.js
玉宇夕落3 小时前
现代前端开发工程化:从 Vite 到 Vue 3 路由实战
vue.js
神秘的猪头3 小时前
# Vue项目初识:从零开始搭建你的第一个现代前端工程化Vue3项目
前端·vue.js·面试
叫我詹躲躲3 小时前
vue3插槽的本质
vue.js
黄老五3 小时前
createContext
前端·javascript·vue.js
q_19132846954 小时前
基于SpringBoot2+Vue2的装修报价网站
java·vue.js·spring boot·mysql·计算机毕业设计·演示文稿
YaeZed4 小时前
Vue3-动态组件
前端·vue.js