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(...)| 属性名 | 组件内仅一个调用时 | 组件内多个调用时 |
|---|---|---|
| 保留原名 | 重命名为对应内容含义: |
| 保留原名 | 重命名为语义化动词: |
| 保留原名 | 重命名为对应场景: |
错误示例:
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">Submit</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("Upload 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()StrictUseAxiosReturndataerrortypescript
// ✅ 使用返回值 — 避免额外定义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 },
});
// 参数变化时自动重新请求
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("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 {
// 仅吞掉用户主动取消的异常
}
};