SvelteKit SPA中JWT认证后user对象无法使用问题排查
SvelteKit JWT认证中user对象为undefined的问题解决
问题场景
作为TypeScript和SvelteKit新手,在实现JWT认证时,根+layout.svelte中通过await auth.init()获取用户信息,但得到的user对象始终是undefined,无法在导航栏展示用户名等信息。
问题代码
+layout.svelte
<script lang="ts"> import { goto } from '$app/navigation'; import auth from '$lib/stores/auth'; </script> {#await auth.init() then user} <slot /> {:catch} {goto('/login')} {/await}
lib/stores/auth.ts
import { writable, get } from 'svelte/store'; import { jwtDecode, type JwtPayload } from 'jwt-decode'; type User = { email: string; name: string; }; interface Token extends JwtPayload { user: User; } function createStore() { const user = {}; const { subscribe, update, set } = writable(user as User); return { subscribe, async init() { const tokenStr = localStorage.getItem('token')!; const token = jwtDecode<Token>(tokenStr); set(token.user); }, setToken(tokenStr: string) { const token = jwtDecode<Token>(tokenStr); let storeValue = get(this); storeValue = token.user; update(() => storeValue); localStorage.setItem('token', tokenStr); } }; } export default createStore();
核心原因
init方法无返回值:auth.init()是异步函数,但内部没有返回任何值,导致{#await ... then user}中的user自然是undefined。- 未处理token不存在的情况:强行使用非空断言
!,如果localStorage中没有token,会直接抛出错误,且错误处理逻辑仅会跳转到登录页,无法定位根本问题。 - 初始值类型断言错误:初始
user是空对象{},却断言成User类型,不符合User需要email和name的结构要求,可能引发后续类型问题。 setToken方法逻辑冗余且存在this指向问题:使用get(this)获取store值完全没必要,且this在该函数中的指向并非store实例,容易出错;同时update的写法冗余,直接用set更简洁。
修复方案
修改lib/stores/auth.ts代码,解决上述问题:
import { writable } from 'svelte/store'; import { jwtDecode, type JwtPayload } from 'jwt-decode'; type User = { email: string; name: string; }; interface Token extends JwtPayload { user: User; } function createStore() { // 初始值设为null,符合未登录状态的语义,避免类型断言错误 const { subscribe, set } = writable<User | null>(null); return { subscribe, async init() { const tokenStr = localStorage.getItem('token'); // 处理token不存在的情况 if (!tokenStr) { throw new Error('No token found'); } try { const token = jwtDecode<Token>(tokenStr); // 可选:验证token是否过期 if (token.exp && Date.now() >= token.exp * 1000) { localStorage.removeItem('token'); throw new Error('Token expired'); } set(token.user); // 返回当前用户,供await接收 return token.user; } catch (err) { localStorage.removeItem('token'); throw err; } }, setToken(tokenStr: string) { const token = jwtDecode<Token>(tokenStr); // 直接用set更新store,无需冗余的get和update set(token.user); localStorage.setItem('token', tokenStr); }, // 新增登出方法,清空store和localStorage logout() { set(null); localStorage.removeItem('token'); } }; } export default createStore();
同时,+layout.svelte可以调整为通过订阅store获取用户信息,更符合Svelte的状态管理逻辑:
<script lang="ts"> import { goto } from '$app/navigation'; import auth from '$lib/stores/auth'; // 订阅用户信息 let user; const unsubscribe = auth.subscribe(u => user = u); </script> {#await auth.init()} <!-- 可选:添加加载状态 --> <div>Loading...</div> {:then} {#if user} <nav>欢迎,{user.name}</nav> {/if} <slot /> {:catch} {goto('/login')} {/await}
内容的提问来源于stack exchange,提问作者LuMa
相关产品推荐
相关产品推荐

