Nuxt3结合Pinia在SSR模式下实现中间件获取localStorage Token
解决Nuxt3 SSR模式下中间件获取localStorage Token的问题
核心问题分析
- SSR模式下路由中间件会先在服务器端执行,服务器环境不存在
localStorage,直接访问会触发报错。 - Pinia的
userToken在服务器端初始化为空值,客户端hydrate阶段会继承该空值,直到onMounted才更新,导致中间件初期无法获取有效Token。
步骤1:修复Pinia Store的状态同步
修改store/user/token.js,新增客户端初始化逻辑,无需在每个组件的onMounted中重复处理Token同步:
import { defineStore } from "pinia"; export const useTokenStore = defineStore("token", { state: () => ({ userToken: '' // 服务器端初始为空,客户端后续自动同步 }), getters: { isAuthenticated: state => !!state.userToken // 直接通过Token存在性判断登录状态 }, actions: { storeToken(token){ this.userToken = token if (process.client) { // 仅在客户端操作localStorage localStorage.setItem('token', this.userToken) } }, initToken() { // 新增客户端初始化方法 if (process.client) { const savedToken = localStorage.getItem('token') if (savedToken) { this.userToken = savedToken } } } } })
创建客户端专属插件plugins/pinia-init.client.js(后缀.client.js确保仅在客户端运行),全局初始化Token:
import { useTokenStore } from "~/store/user/token"; export default defineNuxtPlugin(() => { const tokenStore = useTokenStore() tokenStore.initToken() })
步骤2:修改路由中间件,仅在客户端执行登录状态判断
修改middleware/auth.js,跳过服务器端的登录判断逻辑,仅在客户端完成路由守卫:
export default defineNuxtRouteMiddleware((to, from) => { // 服务器端直接返回,不执行判断 if (process.server) return const tokenStore = useTokenStore() // 已登录用户访问登录页,重定向到首页 if (tokenStore.isAuthenticated && to.path === '/login') { return navigateTo('/') } // 未登录用户访问授权页面,重定向到登录页(可选) if (!tokenStore.isAuthenticated && to.path !== '/login') { return navigateTo('/login') } })
步骤3:优化登录逻辑(可选)
简化原登录函数,避免不必要的循环操作:
import {useTokenStore} from "../store/user/token"; const tokenStore = useTokenStore() const errorMsg = ref(false) async function login() { errorMsg.value = false try { const { data: responseToken } = await useLazyFetch('/api/auth/login', { method: 'POST', body: { email: inpEmail.value, password: inpPassword.value } }) if (responseToken.value !== "fout") { tokenStore.storeToken(responseToken.value.token) // 假设返回值含token字段 await navigateTo('/') // 登录成功后重定向首页 } else { errorMsg.value = true } } catch (error) { console.error(error) errorMsg.value = true } }
关键说明
- 环境区分:始终用
process.client/process.server判断运行环境,避免在服务器端调用客户端专属API。 - 状态同步:通过客户端插件初始化Token,确保页面刷新后Pinia状态立即与localStorage同步,无需依赖组件生命周期。
- 中间件逻辑:服务器端跳过路由守卫,由客户端完成登录状态判断与重定向,既保留SSR的SEO优势,又实现完整的客户端路由控制。
内容的提问来源于stack exchange,提问作者Moemen Hussein
相关产品推荐
相关产品推荐

