vueuse-axios-conventions
npx skills add https://github.com/ztn9709/skills --skill vueuse-axios-conventions
Agent 安装分布
Skill 文档
VueUse Axios Conventions (EMID Project)
This skill documents the standardized patterns for data fetching and state management using useAxios in the EMID frontend.
1. Centralized Error Handling
NEVER use manual try-catch blocks or ElMessage.error for API errors in components. The axiosInstance in src/utils/http.ts includes a global interceptor that:
- Automatically formats error messages.
- Displays them via
ElMessage.error. - Logs details to the console for debugging.
Incorrect:
try {
await execute();
} catch (error) {
ElMessage.error("Failed to fetch");
}
Correct:
// Error is handled automatically by http.ts
await execute().catch(() => undefined);
2. Naming Conventions
Use semantic, verb+noun names. NEVER use the execute prefix (e.g., executeDelete, executeFetch).
Rule: Only rename when there are multiple useAxios calls in the same component
| Property | Only 1 in component | Multiple in component |
|---|---|---|
data |
Keep data |
Rename to content meaning: detail, materials, drafts |
execute |
Keep execute |
Rename to semantic verb: fetchDetail, deleteDraft, uploadFile |
isLoading |
Keep isLoading |
Rename to scenario: isSubmitting, isDownloading, draftsLoading |
Incorrect:
const { execute: executeFetch } = useAxios(...)
const { execute: executeDeleteDraft } = useAxios(...)
const { data: detailsData } = useAxios(...)
const { data: dataStatsRes } = useAxios(...)
Correct:
// Single useAxios â no rename needed
const { data, execute, isLoading } = useAxios(...)
// Multiple useAxios â use semantic names
const { data: detail, execute: fetchDetail } = useAxios(...)
const { execute: deleteDraft } = useAxios(...)
const { data: materials, execute: fetchMaterials, isLoading: materialsLoading } = useAxios(...)
3. Simplified Signature & Defaults
useAxios defaults to immediate: true when only a URL and instance are passed. But providing an options object replaces the defaults, making immediate become undefined (falsy).
- Always declare
immediate: trueexplicitly when passing an options object and wanting auto-execution. - Omit
immediate: falsewhen the intent is obvious from context (e.g., mutations with empty URL).
// Auto-fetch on mount â must declare immediate explicitly since options object is provided
const { data, isLoading } = useAxios<T>("/url", axiosInstance, {
immediate: true,
initialData: { results: [], count: 0 },
});
// Auto-fetch â no options object, immediate defaults to true
const { data } = useAxios<T>("/url", axiosInstance);
// Manual execution â immediate defaults to false when options aren't provided
// because the empty URL signals manual use
const { execute: submitMaterial, isLoading: isSubmitting } = useAxios<T>(
"",
axiosInstance,
);
const { execute: deleteDraft } = useAxios(
"",
{ method: "DELETE" },
axiosInstance,
);
4. Leverage Internal Loading States
Always use the isLoading property returned by useAxios instead of defining a separate loading ref.
const { isLoading: isSubmitting } = useAxios("/url", axiosInstance);
// In template:
// <el-button :loading="isSubmitting">Submit</el-button>
5. Use initialData to Simplify Templates
When fetching objects or lists, provide initialData to pre-populate the data ref. This eliminates repeated optional chaining (?.) in Vue templates.
const { data } = useAxios<PaginatedResponse>("/url", axiosInstance, {
immediate: true,
initialData: { results: [], count: 0 },
});
// In template â no need for data?.results
// <el-table :data="data.results">
// <el-pagination :total="data.count">
6. Callbacks: onSuccess and onError
Use onSuccess for post-request logic (closing modals, toasts, state updates). Use onError for navigation on failure (e.g., redirect to home on 404).
// onSuccess â handle side effects after successful request
const { execute: updateFiles } = useAxios<FileUpload[]>(
"/upload/file/update/",
{ method: "POST" },
axiosInstance,
{
onSuccess: (files) => {
ElMessage.success("Upload success!");
categoryRef.value = [...categoryRef.value, ...files];
isDialogVisible.value = false;
},
},
);
// onError â redirect on failure
const { data: detail, execute: fetchDetail } = useAxios<MaterialDetail>(
"",
axiosInstance,
{
onSuccess: ({ expSerial }) => {
/* process data */
},
onError: () => router.push("/"),
},
);
7. Using execute() Return Value
execute() returns StrictUseAxiosReturn, which gives you access to data, error, etc. Use this instead of a separate data ref when you need an inline result.
// â
Use return value â avoids needing a separate data ref
const { execute: uploadFile } = useAxios<FileUpload>(
"/upload/file/",
{ method: "POST" },
axiosInstance,
{
abortPrevious: false,
},
);
const handleFileUpload = async ({ file }: UploadRequestOptions) => {
const formData = new FormData();
formData.append("file", file);
const { data } = await uploadFile({ data: formData });
return data.value!;
};
// â
Use return value in submit flow
const response = await submitMaterial(url, { method, data }).catch(
() => undefined,
);
if (!response) return;
router.push(`/details/${response.data.value.id}`);
8. Static vs. Dynamic Configuration
A. Define behavior at initialization (Static)
- Hooks & Callbacks:
onSuccess,onError - Behavior:
immediate,initialData,abortPrevious - Base Request Config: Fixed
headers,method, or the baseurl
B. Pass only variables to execute (Dynamic)
- Request Body:
data(for POST/PUT) - Query Params:
params - URL Overrides: Dynamic URL or one-off configs
// 1. Static config at initialization
const { execute: searchImages } = useAxios<Response>(
"/services/",
{ method: "POST" },
axiosInstance,
{
onSuccess: ({ images }) => (serialImages.value = images),
},
);
// 2. Dynamic data at execution time
const onSearch = () =>
searchImages({
data: { filters: JSON.stringify(filterItems.value) },
}).catch(() => undefined);
9. Paginated List Pattern
For components with paginated tables, use params with reactive + watch to auto-refetch:
const params = reactive({ page: 1, pageSize: 10 });
const {
data,
execute: fetchData,
isLoading,
} = useAxios<PaginatedResponse>("/items/", { params }, axiosInstance, {
immediate: true,
initialData: { results: [], count: 0 },
});
// Auto-refetch when params change
watch(params, () => fetchData().catch(() => undefined));
10. Mutation with Refresh Pattern
For delete/update operations that need to refresh a list afterward:
const { execute: deleteItem } = useAxios(
"",
{ method: "DELETE" },
axiosInstance,
{
onSuccess: () => {
ElMessage.success("Deleted");
if (data.value.results.length === 1 && params.page > 1) params.page -= 1;
else fetchData().catch(() => undefined);
},
},
);
const handleDelete = async (id: number) => {
try {
await ElMessageBox.confirm("确认å é¤?", "æç¤º", { type: "warning" });
await deleteItem(`/items/${id}/`);
} catch {
// Only swallow 'cancel'
}
};