Introduction
Vue Rex is a highly flexible, type-safe, and plugin-based request Hook library designed for Vue 3.
Core Features
- 🏭 Factory pattern:
createRequest/createPagination— configure once, share globally - 🛡️ Type inference:
datatype auto-inferred from service return type and factory config - 🌐 Backend adaptation:
dataKey/listKey+totalKeyunify different response structures - ⚡ Debounce / throttle: built-in
debounceRun/throttleRun, no extra dependencies - 💾 Cache & SWR: memory cache + Stale-While-Revalidate for instant list restores
- 🔄 Error retry: configurable retry count and interval for network resilience
- 🔁 Polling: auto re-fetch on interval, ideal for real-time data
- 📄 Pagination:
page/pageSizechanges trigger requests — no manualwatch - 🔌 Plugins:
definePluginfor custom request lifecycle hooks - ⚙️ Global defaults: factory options serve as project-wide config
The Problem
When the backend returns { code: number, data: T, msg: string }, a typical request to fetch user details looks like this:
// api/user.ts —— type definitions + API declarations
interface Response<T> {
code: number
data: T
msg: string
}
export interface User {
id: number
name: string
email: string
}
export const getUser = () => server.get<Response<User>>('/api/user/1')<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { getUser } from '@/api/user'
import type { User } from '@/api/user'
const loading = ref(false)
const data = ref<User | null>(null)
const fetchUser = async () => {
loading.value = true
const res = await getUser().finally(() => {
loading.value = false
})
if (res.code !== 0) {
console.error(res.msg)
} else {
data.value = res.data
}
}
onMounted(() => fetchUser())
</script>This boilerplate — loading management, code checks, data extraction — repeats for every endpoint. Vue Rex's approach:
// hooks/api.ts
import { createRequest } from 'vue-rex'
export const useApi = createRequest({ dataKey: 'data' })<script setup lang="ts">
import { getUser } from '@/api/user'
import { useApi } from '@/hooks/api'
const { data, loading } = useApi(getUser)
</script>Error mechanism: vue-rex's
errorcomes from the service rejecting. Set up an axios interceptor to handle business errors:ts// response interceptor server.interceptors.response.use( (res) => { if (res.data.code !== 0) return Promise.reject(res.data.msg) return res.data }, (err) => Promise.reject(err), )Now successful responses return data directly, and failures reject → vue-rex's
errorcaptures it automatically. AdderrorRetryCountfor automatic retries.
Debounce, throttle, cache, error retry, polling — all enabled by passing the corresponding option.
Core Idea
Recommended layering: API layer defines types and endpoints → Hooks layer wraps vue-rex → components consume directly.
// api/user.ts — API layer
interface Response<T> { code: number; data: T; msg: string }
export interface User { id: number; name: string; email: string }
// Basic request: backend returns Response<User>
export const getUser = (id: number) => server.get<Response<User>>(`/api/user/${id}`)
// Paginated request: backend returns Response<{ list: User[]; total: number }>
export const getUserPage = (params: { page: number; pageSize: number }) =>
server.get<Response<{ list: User[]; total: number }>>('/api/users', { params })// hooks/api.ts — Hooks layer
import { createRequest, createPagination } from 'vue-rex'
// createRequest for basic requests
export const useApi = createRequest({
dataKey: 'data',
options: { errorRetryCount: 2 },
})
// createPagination for paginated requests
export const usePage = createPagination({
listKey: 'data.list',
totalKey: 'data.total',
})<!-- Basic: fetch user detail -->
<script setup lang="ts">
import { useApi } from '@/hooks/api'
import { getUser } from '@/api/user'
const { data, loading } = useApi(() => getUser(1))
</script><!-- Paginated: fetch user list -->
<script setup lang="ts">
import { usePage } from '@/hooks/api'
import { getUserPage } from '@/api/user'
const { list, total, page, pageSize } = usePage(getUserPage)
</script>