Vue.js中提交表单前如何更新响应式变量me?
问题分析
你的代码核心问题在于逻辑顺序完全颠倒:在调用登录接口login之前就判断me.value === null,此时用户还未发起登录请求,me必然是初始的null值,所以错误弹窗会每次都触发;同时在handleSubmit内部创建watchEffect会导致每次提交都新增一个监听,容易引发内存泄漏和重复逻辑。
修正方案
关键调整思路
- 先执行登录请求,再根据登录结果处理后续操作
- 登录成功后主动刷新用户信息
me(如果useMe是基于查询库实现,通常会提供refetch方法) - 监听
me的变化,确保用户信息更新后再执行跳转或错误提示
修改后的完整代码
<script setup lang="ts"> import { NButton, NCard, NForm, NFormItem, NInput, NSpace, useDialog } from 'naive-ui'; import { useLogin } from '@/composables/API/useLogin'; import { computed, ref, watch } from 'vue'; import { useRouter, useRoute } from 'vue-router'; import { useMe } from '@/composables/API/useMe'; import { useI18n } from 'vue-i18n'; const { mutate: login } = useLogin(); const credentials = ref({ username: '', password: '' }); // 解构出refetch方法,用于登录后主动刷新用户信息 const { data: me, refetch } = useMe(); const { t } = useI18n({ useScope: 'global' }); const dialog = useDialog(); const router = useRouter(); const route = useRoute(); const redirectRoute = computed(() => { const redirect = route.query.returnUrl; if (redirect) { return redirect as string; } return '/'; }); // 监听me的变化,处理登录后的最终结果 const unwatchMe = watch(me, (newMe) => { if (newMe === null) { dialog.error({ title: t('login.errorPopup.title'), content: t('login.errorPopup.content'), positiveText: t('login.errorPopup.positiveText'), }); // 触发一次后取消监听,避免后续重复触发 unwatchMe(); } else { router.replace(redirectRoute.value); unwatchMe(); } }, { immediate: false }); const handleSubmit = () => { const { username, password } = credentials.value; // 简化表单非空验证 if (!username || !password) return; login(credentials.value, { onSuccess: () => { // 登录接口请求成功后,主动刷新用户信息 refetch(); }, onError: () => { // 登录接口本身请求失败时直接弹窗提示 dialog.error({ title: t('login.errorPopup.title'), content: t('login.errorPopup.content'), positiveText: t('login.errorPopup.positiveText'), }); } }); }; </script> <template> <n-space vertical justify="center" align="center" :size="20" style="width: 100vw; height: 100vh"> <img alt="GPI logo" src="@/assets/logo.svg" style="width: 200px" /> <n-card :title="$t('login.title')" size="huge" embedded style="width: 400px"> <n-form :model="credentials" @submit.prevent="handleSubmit" label-placement="left" label-width="auto" label-align="left" > <n-form-item :label="$t('login.username')" path="username"> <n-input v-model:value="credentials.username" :placeholder="$t('login.username')" /> </n-form-item> <n-form-item :label="$t('login.password')" path="password"> <n-input v-model:value="credentials.password" type="password" show-password-on="mousedown" :placeholder="$t('login.password')" /> </n-form-item> <n-form-item> <n-button attr-type="submit" type="primary" block>{{ $t('login.submit') }}</n-button> </n-form-item> </n-form> </n-card> </n-space> </template>
核心修改说明
- 移除
handleSubmit内部的watchEffect,改为组件初始化时创建一次性监听,避免重复监听导致的逻辑混乱 - 登录成功后调用
refetch()强制刷新me,确保用户信息及时更新 - 拆分错误处理逻辑:区分登录接口请求失败(
onError回调)和用户信息获取失败(watch监听me为null) - 简化表单非空验证逻辑,代码更简洁
如果你的useMe不是基于查询库实现的,无法使用refetch,可以在login的onSuccess回调中手动触发用户信息的获取接口,确保me被正确更新后再执行后续操作。
内容的提问来源于stack exchange,提问作者rocco
相关产品推荐
相关产品推荐

