作为前端开发者,我们每天都在和异步操作打交道 ------ 发起 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?
- 更少的代码:平均减少 40%+ 的异步相关代码,专注业务逻辑
- 更强的可读性:统一的命名约定(如queryXxx、xxxLoading)让代码自文档化
- 零成本维护:自动处理状态更新、竞态条件,减少 bug
- 完整的 TypeScript 支持:所有 API 都有精确的类型定义,IDE 自动提示
- 轻量无依赖:仅依赖 Vue3,体积极小(gzip 后 ~2KB)
- 100% 测试覆盖:200+ 测试用例确保稳定性
如何开始使用?
- 安装依赖:
sh
pnpm i vue-asyncx
# 或 npm i vue-asyncx
# 或 yarn add vue-asyncx
- 在组件中使用:
ts
import { useAsync, useAsyncData } from 'vue-asyncx'
详细文档和更多示例见:GitHub 仓库
社区贡献:一起让它更好
vue-asyncx 还在不断进化,如果你有任何想法或需求,欢迎参与贡献:
- 提 Issue:报告 bug 或建议新功能
- 发 PR:修复 bug 或实现新功能(欢迎新手参与)
- 分享体验:在博客或社交平台分享你的使用心得
项目地址:github.com/xuyimingwor...
最后
开发 vue-asyncx 的初衷,就是想让自己和更多开发者从重复的异步状态管理中解放出来 ------ 毕竟,好的工具应该让你感觉不到它的存在,却能悄悄帮你搞定琐事。
希望 vue-asyncx 能让你少加班、多陪家人、多打游戏,早点下班😊。
如果觉得有用,欢迎给个 Star 支持一下~
