vueuse-axios-conventions

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

VueUse Axios Conventions (EMID Project)

VueUse Axios 规范(EMID 项目)

This skill documents the standardized patterns for data fetching and state management using
useAxios
in the EMID frontend.
本文档记录了EMID前端中使用
useAxios
进行数据请求和状态管理的标准化模式。

1. Centralized Error Handling

1. 集中式错误处理

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:
typescript
try {
  await execute();
} catch (error) {
  ElMessage.error("Failed to fetch");
}
Correct:
typescript
// Error is handled automatically by http.ts
await execute().catch(() => undefined);
绝对不要在组件中使用手动
try-catch
块或
ElMessage.error
处理API错误。
src/utils/http.ts
中的
axiosInstance
包含一个全局拦截器,它会:
  • 自动格式化错误信息
  • 通过
    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
execute
prefix (e.g.,
executeDelete
,
executeFetch
).
使用**语义化的“动词+名词”**命名方式。绝对不要使用
execute
作为前缀(例如
executeDelete
executeFetch
)。

Rule: Only rename when there are multiple useAxios calls in the same component

规则:仅在同一组件中有多个useAxios调用时才重命名

PropertyOnly 1 in componentMultiple 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:
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
data
保留
data
重命名为有实际含义的名称:
detail
materials
drafts
execute
保留
execute
重命名为语义化动词:
fetchDetail
deleteDraft
uploadFile
isLoading
保留
isLoading
重命名为对应场景:
isSubmitting
isDownloading
draftsLoading
错误示例:
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. 简化签名与默认值

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: true
    explicitly
    when passing an options object and wanting auto-execution.
  • Omit
    immediate: false
    when the intent is obvious from context (e.g., mutations with empty URL).
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和实例时,
useAxios
默认
immediate: true
如果传入选项对象,会覆盖默认值,导致
immediate
变为
undefined
(假值)。
  • 当传入选项对象且需要自动执行时,必须显式声明
    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
isLoading
property returned by
useAxios
instead of defining a separate
loading
ref.
typescript
const { isLoading: isSubmitting } = useAxios("/url", axiosInstance);

// In template:
// <el-button :loading="isSubmitting">Submit</el-button>
始终使用
useAxios
返回的
isLoading
属性,而不是单独定义
loading
ref。
typescript
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
initialData
to pre-populate the
data
ref. This eliminates repeated optional chaining (
?.
) in Vue templates.
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">
当请求对象或列表时,提供
initialData
来预填充
data
ref。这可以消除Vue模板中重复的可选链操作符(
?.
)。
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
onSuccess
for post-request logic (closing modals, toasts, state updates). Use
onError
for navigation on failure (e.g., redirect to home on 404).
typescript
// 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("/"),
  },
);
使用
onSuccess
处理请求后的逻辑(关闭弹窗、提示信息、状态更新)。使用
onError
处理失败时的导航(例如404时重定向到首页)。
typescript
// 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()
returns
StrictUseAxiosReturn
, which gives you access to
data
,
error
, etc. Use this instead of a separate
data
ref
when you need an inline result.
typescript
// ✅ 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()
返回
StrictUseAxiosReturn
,可以访问
data
error
等属性。当需要内联结果时,使用该返回值而不是单独的
data
ref
typescript
// ✅ 使用返回值 — 避免需要单独的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:
    onSuccess
    ,
    onError
  • Behavior:
    immediate
    ,
    initialData
    ,
    abortPrevious
  • Base Request Config: Fixed
    headers
    ,
    method
    , or the base
    url
  • 钩子与回调
    onSuccess
    onError
  • 行为设置
    immediate
    initialData
    abortPrevious
  • 基础请求配置:固定的
    headers
    method
    或基础
    url

B. Pass only variables to execute (Dynamic)

B. 仅将变量传递给execute(动态)

  • Request Body:
    data
    (for POST/PUT)
  • 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);
  • 请求体
    data
    (用于POST/PUT)
  • 查询参数
    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
params
with
reactive
+
watch
to auto-refetch:
typescript
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));
对于带有分页表格的组件,使用
reactive
params
配合
watch
实现自动重新请求:
typescript
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 {
    // 仅忽略“取消”操作
  }
};