最直接有效的方式是点击时立即禁用按钮并设 isSubmitting 为 true,再触发 submit 事件;父组件在处理时第一时间置 loading 为 true,且必须在 finally 中重置;全链路统一用 submitting 状态控制按钮、加载态和表单区域。
最直接有效的方式是:点击按钮时立刻禁用它,并同步设置 loading 状态;请求结束后再恢复。关键不是“等响应回来再禁用”,而是“先锁住,再发请求”。
子组件内用一个响应式变量(如 isSubmitting)同时控制按钮的 :disabled 和文字提示,不要依赖父组件传来的 loading 去判断是否可点——因为状态传递有延迟,容易漏掉一次点击。
<el-button :disabled="isSubmitting">{{ isSubmitting ? '提交中...' : '提交' }}</el-button>
isSubmitting.value = true,然后马上 $emit('submit', data)
父组件监听到 submit 事件后,loading 必须在回调函数第一行就赋值为 true,且必须在异步操作完成(无论成功或失败)后才重置为 false。
.finally() 或 try/catch/finally 结构,避免遗漏重置async handleFormSubmit(data) { this.loading = true; try { await api.save(data); } finally { this.loading = false; } }
避免分散定义多个 loading 变量。比如表单页只有一个提交动作,就只用一个 submitting 控制按钮、loading 图标、甚至整个表单区域的 v-loading。
<el-form v-loading="submitting">
如果项目中多处用到类似逻辑,可以抽成 useSubmitting Hook 或 v-click-once 指令,自动处理禁用、loading、Promise 完成后恢复等细节。
const { loading, execute } = useSubmitting(api.submit),按钮直接绑 @click="execute(formData)"
<el-button v-click-once="handleSubmit">,内部已集成节流、禁用、Loading 图标和 Promise 自动恢复