You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
    }
}

关键说明

  1. 环境区分:始终用process.client/process.server判断运行环境,避免在服务器端调用客户端专属API。
  2. 状态同步:通过客户端插件初始化Token,确保页面刷新后Pinia状态立即与localStorage同步,无需依赖组件生命周期。
  3. 中间件逻辑:服务器端跳过路由守卫,由客户端完成登录状态判断与重定向,既保留SSR的SEO优势,又实现完整的客户端路由控制。

内容的提问来源于stack exchange,提问作者Moemen Hussein

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 20:25:13