vueuse-axios-conventions
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVueUse Axios Conventions (EMID Project)
VueUse Axios 规范(EMID 项目)
This skill documents the standardized patterns for data fetching and state management using in the EMID frontend.
useAxios本文档记录了EMID前端中使用进行数据请求和状态管理的标准化模式。
useAxios1. Centralized Error Handling
1. 集中式错误处理
NEVER use manual blocks or for API errors in components. The in includes a global interceptor that:
try-catchElMessage.erroraxiosInstancesrc/utils/http.ts- Automatically formats error messages.
- Displays them via .
ElMessage.error - Logs details to the console for debugging.
Incorrect:
typescript
try {
await execute();
} catch (error) {
ElMessage.error("Failed to fetch");
}Correct:
typescript
// Error is handled automatically by http.ts
await execute().catch(() => undefined);绝对不要在组件中使用手动块或处理API错误。中的包含一个全局拦截器,它会:
try-catchElMessage.errorsrc/utils/http.tsaxiosInstance- 自动格式化错误信息
- 通过展示错误
ElMessage.error - 将详细信息记录到控制台用于调试
错误示例:
typescript
try {
await execute();
} catch (error) {
ElMessage.error("Failed to fetch");
}正确示例:
typescript
// 错误由http.ts自动处理
await execute().catch(() => undefined);2. Naming Conventions
2. 命名规范
Use semantic, verb+noun names. NEVER use the prefix (e.g., , ).
executeexecuteDeleteexecuteFetch使用**语义化的“动词+名词”**命名方式。绝对不要使用作为前缀(例如、)。
executeexecuteDeleteexecuteFetchRule: Only rename when there are multiple useAxios calls in the same component
规则:仅在同一组件中有多个useAxios调用时才重命名
| Property | Only 1 in component | Multiple in component |
|---|---|---|
| Keep | Rename to content meaning: |
| Keep | Rename to semantic verb: |
| Keep | Rename to scenario: |
Incorrect:
typescript
const { execute: executeFetch } = useAxios(...)
const { execute: executeDeleteDraft } = useAxios(...)
const { data: detailsData } = useAxios(...)
const { data: dataStatsRes } = useAxios(...)Correct:
typescript
// 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(...)| 属性 | 组件中仅1个useAxios | 组件中有多个useAxios |
|---|---|---|
| 保留 | 重命名为有实际含义的名称: |
| 保留 | 重命名为语义化动词: |
| 保留 | 重命名为对应场景: |
错误示例:
typescript
const { execute: executeFetch } = useAxios(...)
const { execute: executeDeleteDraft } = useAxios(...)
const { data: detailsData } = useAxios(...)
const { data: dataStatsRes } = useAxios(...)正确示例:
typescript
// 单个useAxios调用 — 无需重命名
const { data, execute, isLoading } = useAxios(...)
// 多个useAxios调用 — 使用语义化名称
const { data: detail, execute: fetchDetail } = useAxios(...)
const { execute: deleteDraft } = useAxios(...)
const { data: materials, execute: fetchMaterials, isLoading: materialsLoading } = useAxios(...)3. Simplified Signature & Defaults
3. 简化签名与默认值
useAxiosimmediate: trueimmediateundefined- Always declare explicitly when passing an options object and wanting auto-execution.
immediate: true - Omit when the intent is obvious from context (e.g., mutations with empty URL).
immediate: false
typescript
// 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,
);当仅传入URL和实例时,默认。但如果传入选项对象,会覆盖默认值,导致变为(假值)。
useAxiosimmediate: trueimmediateundefined- 当传入选项对象且需要自动执行时,必须显式声明
immediate: true - 当意图从上下文可明显判断时(例如空URL的修改操作),省略
immediate: false
typescript
// 挂载时自动请求 — 由于传入了选项对象,必须显式声明immediate
const { data, isLoading } = useAxios<T>("/url", axiosInstance, {
immediate: true,
initialData: { results: [], count: 0 },
});
// 自动请求 — 无选项对象,immediate默认为true
const { data } = useAxios<T>("/url", axiosInstance);
// 手动执行 — 当未提供选项时,immediate默认为false
// 因为空URL表示手动调用
const { execute: submitMaterial, isLoading: isSubmitting } = useAxios<T>(
"",
axiosInstance,
);
const { execute: deleteDraft } = useAxios(
"",
{ method: "DELETE" },
axiosInstance,
);4. Leverage Internal Loading States
4. 利用内部加载状态
Always use the property returned by instead of defining a separate ref.
isLoadinguseAxiosloadingtypescript
const { isLoading: isSubmitting } = useAxios("/url", axiosInstance);
// In template:
// <el-button :loading="isSubmitting">Submit</el-button>始终使用返回的属性,而不是单独定义 ref。
useAxiosisLoadingloadingtypescript
const { isLoading: isSubmitting } = useAxios("/url", axiosInstance);
// 在模板中:
// <el-button :loading="isSubmitting">提交</el-button>5. Use initialData to Simplify Templates
5. 使用initialData简化模板
When fetching objects or lists, provide to pre-populate the ref. This eliminates repeated optional chaining () in Vue templates.
initialDatadata?.typescript
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">当请求对象或列表时,提供来预填充 ref。这可以消除Vue模板中重复的可选链操作符()。
initialDatadata?.typescript
const { data } = useAxios<PaginatedResponse>("/url", axiosInstance, {
immediate: true,
initialData: { results: [], count: 0 },
});
// 在模板中 — 无需使用data?.results
// <el-table :data="data.results">
// <el-pagination :total="data.count">6. Callbacks: onSuccess and onError
6. 回调函数:onSuccess和onError
Use for post-request logic (closing modals, toasts, state updates). Use for navigation on failure (e.g., redirect to home on 404).
onSuccessonErrortypescript
// 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("/"),
},
);使用处理请求后的逻辑(关闭弹窗、提示信息、状态更新)。使用处理失败时的导航(例如404时重定向到首页)。
onSuccessonErrortypescript
// onSuccess — 请求成功后处理副作用
const { execute: updateFiles } = useAxios<FileUpload[]>(
"/upload/file/update/",
{ method: "POST" },
axiosInstance,
{
onSuccess: (files) => {
ElMessage.success("上传成功!");
categoryRef.value = [...categoryRef.value, ...files];
isDialogVisible.value = false;
},
},
);
// onError — 失败时重定向
const { data: detail, execute: fetchDetail } = useAxios<MaterialDetail>(
"",
axiosInstance,
{
onSuccess: ({ expSerial }) => {
/* 处理数据 */
},
onError: () => router.push("/"),
},
);7. Using execute() Return Value
7. 使用execute()返回值
execute()StrictUseAxiosReturndataerrordatatypescript
// ✅ 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}`);execute()StrictUseAxiosReturndataerrordatatypescript
// ✅ 使用返回值 — 避免需要单独的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!;
};
// ✅ 在提交流程中使用返回值
const response = await submitMaterial(url, { method, data }).catch(
() => undefined,
);
if (!response) return;
router.push(`/details/${response.data.value.id}`);8. Static vs. Dynamic Configuration
8. 静态与动态配置
A. Define behavior at initialization (Static)
A. 初始化时定义行为(静态)
- Hooks & Callbacks: ,
onSuccessonError - Behavior: ,
immediate,initialDataabortPrevious - Base Request Config: Fixed ,
headers, or the basemethodurl
- 钩子与回调:、
onSuccessonError - 行为设置:、
immediate、initialDataabortPrevious - 基础请求配置:固定的、
headers或基础methodurl
B. Pass only variables to execute (Dynamic)
B. 仅将变量传递给execute(动态)
- Request Body: (for POST/PUT)
data - Query Params:
params - URL Overrides: Dynamic URL or one-off configs
typescript
// 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);- 请求体:(用于POST/PUT)
data - 查询参数:
params - URL覆盖:动态URL或一次性配置
typescript
// 1. 初始化时配置静态参数
const { execute: searchImages } = useAxios<Response>(
"/services/",
{ method: "POST" },
axiosInstance,
{
onSuccess: ({ images }) => (serialImages.value = images),
},
);
// 2. 执行时传入动态数据
const onSearch = () =>
searchImages({
data: { filters: JSON.stringify(filterItems.value) },
}).catch(() => undefined);9. Paginated List Pattern
9. 分页列表模式
For components with paginated tables, use with + to auto-refetch:
paramsreactivewatchtypescript
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));对于带有分页表格的组件,使用的配合实现自动重新请求:
reactiveparamswatchtypescript
const params = reactive({ page: 1, pageSize: 10 });
const {
data,
execute: fetchData,
isLoading,
} = useAxios<PaginatedResponse>("/items/", { params }, axiosInstance, {
immediate: true,
initialData: { results: [], count: 0 },
});
// 当params变化时自动重新请求
watch(params, () => fetchData().catch(() => undefined));10. Mutation with Refresh Pattern
10. 带刷新的修改操作模式
For delete/update operations that need to refresh a list afterward:
typescript
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'
}
};对于删除/更新操作后需要刷新列表的场景:
typescript
const { execute: deleteItem } = useAxios(
"",
{ method: "DELETE" },
axiosInstance,
{
onSuccess: () => {
ElMessage.success("删除成功");
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 {
// 仅忽略“取消”操作
}
};