Nuxt.js中Snackbar状态不更新问题排查
Vue迁移Nuxt.js后Snackbar无法显示的问题
迁移项目到Nuxt.js后,遇到以下问题:在路由中间件中调用Pinia store的showSnackbar方法后,使用navigateTo跳转页面时Snackbar完全不显示;但改用router.push跳转时,Snackbar能正常展示。
相关代码
Snackbar组件
<template> <v-snackbar v-model="snackbar" :timeout="timeout" :color="color"> {{ message }} </v-snackbar> </template> <script setup> import { ref, watch } from 'vue'; import { useUserStore } from '~/store/user'; const store = useUserStore() const snackbar = ref(false); const message = ref(''); const timeout = ref(3000); const color = ref('error'); console.log("STORE SNACKBAR", store.snackbarVisible) console.log("STORE SNACKBAR mesage", store.snackbarMessage) // 监听Pinia store状态变化 watch( () => store.snackbarVisible, (newVal) => { console.log("newval", newVal) console.log(store.snackbarVisible) snackbar.value = newVal; console.log("IN SNACLBAR", snackbar) if (newVal) { message.value = store.snackbarMessage; console.log("IN SNACKMAR MEESATE", message.value) } } ); </script>
Pinia Store
import { defineStore } from 'pinia'; export const useUserStore = defineStore('user', { state: () => ({ auth: false, admin: false, snackbarMessage: '', snackbarVisible: false }), getters: { isAuthenticated: state => state.auth === true, isAdmin: state => state.admin === true, isSnackBarVisible: state => state.snackbarVisible === true }, actions: { initializeAuth() { this.auth = useCookie('userLoggedIn').value === 1; this.admin = useCookie('admin').value === 1; }, setUser(user) { this.auth = user.auth; this.admin = user.admin; }, showSnackbar(message) { this.snackbarMessage = message; this.snackbarVisible = true; console.log("AICI IN STORE") console.log("SNACKBAR VISSBILE", this.snackbarVisible) setTimeout(() => { this.snackbarMessage = ''; this.snackbarVisible = false; console.log("SNACKBAR TERMINATED") }, 4000); } } });
路由中间件(middleware/auth.js)
import { useUserStore } from "~/store/user"; export default defineNuxtRouteMiddleware(async (to, from) => { const userStore = useUserStore() const isUserloggedInCookie = useCookie('userLoggedIn') const isAdminloggedInCookie = useCookie('admin') const user = { auth: isUserloggedInCookie.value === 1, admin: isAdminloggedInCookie.value === 1 } userStore.setUser(user) const isAuth = userStore.isAuthenticated; if (!isAuth) { userStore.showSnackbar('You need to be logged in!'); return navigateTo('/user/logout'); } });
控制台日志
// showSnackBar被调用时 AICI IN STORE SNACKBAR VISSBILE true // SnackBar组件初始化时 STORE SNACKBAR false STORE SNACKBAR mesage // setTimeout执行后 SNACKBAR TERMINATED
问题原因
- 路由中间件执行时机早于页面组件挂载:中间件在页面组件渲染前执行,此时页面内的Snackbar组件还未创建,watch监听器尚未注册,无法捕获store的状态变化。
- navigateTo的即时跳转特性:
navigateTo会立即触发页面跳转,当前页面组件来不及完成渲染和状态监听就被销毁。 - router.push的异步特性:
router.push是客户端路由跳转,会等待当前事件循环任务完成后再执行跳转,此时页面组件已挂载,监听器生效,能正常显示Snackbar。
解决方案
方案1:全局挂载Snackbar组件
将Snackbar组件放在app.vue中,确保它在应用生命周期内始终存在,不受页面跳转影响:
<!-- app.vue --> <template> <NuxtPage /> <Snackbar /> </template> <script setup> import Snackbar from '~/components/Snackbar.vue' </script>
方案2:延迟导航跳转
在调用showSnackbar后延迟执行navigateTo,给Snackbar足够的渲染时间:
// middleware/auth.js import { useUserStore } from "~/store/user"; export default defineNuxtRouteMiddleware(async (to, from) => { // ... 原有代码 ... if (!isAuth) { userStore.showSnackbar('You need to be logged in!'); // 延迟100ms跳转,保证Snackbar完成渲染 setTimeout(() => { navigateTo('/user/logout'); }, 100); } });
方案3:客户端环境下使用router.push
如果中间件仅在客户端执行,直接使用router.push替代navigateTo:
// middleware/auth.js import { useUserStore } from "~/store/user"; import { useRouter } from 'vue-router'; export default defineNuxtRouteMiddleware(async (to, from) => { const router = useRouter(); // ... 原有代码 ... if (!isAuth) { userStore.showSnackbar('You need to be logged in!'); if (process.client) { router.push('/user/logout'); } else { // 服务端环境仍使用navigateTo return navigateTo('/user/logout'); } } });
内容的提问来源于stack exchange,提问作者Misu Stefan
相关产品推荐
相关产品推荐

