表单异步校验时怎么处理 loading 状态和错误提示?

A. 雨萱 阅读 15

我在用 Vue 写一个注册表单,用户名需要异步校验是否重复。现在的问题是:校验过程中没法显示 loading,而且如果请求失败了,错误信息也覆盖不了之前的提示。

我试过在 asyncValidator 里手动控制 loading 变量,但好像时机不对,UI 没反应。比如这样:

const validateUsername = async (rule, value) => {
  if (!value) return Promise.reject('请输入用户名');
  loading.value = true;
  try {
    const res = await checkUsername(value);
    loading.value = false;
    return res.available ? Promise.resolve() : Promise.reject('用户名已存在');
  } catch (err) {
    loading.value = false;
    return Promise.reject('校验失败,请重试');
  }
};

但界面上的 loading 图标根本不显示,错误信息有时候也会残留。这到底该怎么搞?

我来解答 赞 3 收藏
二维码
手机扫码查看
1 条解答
姝贝 Dev
我的做法是把 loading 状态从 asyncValidator 里抽出来,别让它在校验函数里硬控,因为校验函数只负责返回结果,不负责副作用,UI 更新得由外面的调用方统一管。

具体来说,用 el-form 的 validatevalidateField 方法前手动设 loading,校验结束后再关掉,错误提示交给 form-item 的 error 属性或者表单实例的 validate 返回结果来处理。

比如这样写:

// 先定义一个 ref 控制 loading
const loading = ref(false);
const formRef = ref(null);
const form = ref({
username: ''
});

// 校验函数只负责逻辑,别动 loading
const validateUsername = async (rule, value) => {
if (!value) {
return Promise.reject('请输入用户名');
}
const res = await checkUsername(value);
if (!res.available) {
return Promise.reject('用户名已存在');
}
return Promise.resolve();
};

// 触发校验时统一管 loading
const handleValidateUsername = async () => {
loading.value = true;
try {
await formRef.value.validateField('username');
// 成功了可以继续后续操作
} catch (err) {
// 这里 catch 到的是校验失败的错误,比如用户名重复、格式不对等
// 注意:网络请求失败的错误建议在 checkUsername 里 catch 掉并返回 reject('校验失败,请重试')
} finally {
loading.value = false;
}
};


然后在模板里:

<el-form ref="formRef" :model="form">
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" />
<el-icon v-if="loading" class="is-loading"><Loading /></el-icon>
</el-form-item>
</el-form>


关键点是:
- loading 要在调用校验前后手动控制,别塞进 asyncValidator 里,因为那个函数是同步返回 Promise 的,Vue 的响应式更新可能来不及
- 网络错误要在 checkUsername 里先 catch 一下,转成校验失败的 reject,这样外面统一处理就行
- 如果你用的是 el-form-item,它自带 error 显示逻辑,只要 reject 的字符串能传到它那儿,就不会残留——除非你多次快速触发校验导致旧的 reject 覆盖了新的,这种情况可以用 validateField 的第二个参数传入 callback 来更精细控制,但一般场景不用这么复杂

我之前也踩过这个坑,以为在 asyncValidator 里设 loading 能联动,结果发现 Vue 的响应式更新是在 asyncValidator 返回之后才触发的,UI 自然就卡住了。后来才明白:校验逻辑和 UI 状态控制得分开写,别混在一起。
点赞
2026-02-25 17:05