目录
在你的代码中,userInfo 的浏览器本地存储是在 defineStore("user", () => { ... }) 函数内部的第一行实现的:
auth.ts
// 访问 token 缓存的 key const ACCESS_TOKEN_KEY = "access_token"; // 刷新 token 缓存的 key const REFRESH_TOKEN_KEY = "refresh_token"; function getAccessToken(): string { return localStorage.getItem(ACCESS_TOKEN_KEY) || ""; } function setAccessToken(token: string) { localStorage.setItem(ACCESS_TOKEN_KEY, token); } function getRefreshToken(): string { return localStorage.getItem(REFRESH_TOKEN_KEY) || ""; } function setRefreshToken(token: string) { localStorage.setItem(REFRESH_TOKEN_KEY, token); } function clearToken() { localStorage.removeItem(ACCESS_TOKEN_KEY); localStorage.removeItem(REFRESH_TOKEN_KEY); } export { getAccessToken, setAccessToken, clearToken, getRefreshToken, setRefreshToken };
import { setAccessToken, setRefreshToken, getRefreshToken, clearToken } from "@/utils/auth"; export const useUserStore = defineStore("user", () => { const userInfo = useStorage<UserInfo>("userInfo", {} as UserInfo); const roleList = useStorage<RoleInfo[]>("roleList", []); /** * 登录 * * @param {LoginFormData} * @returns */ function login(LoginFormData: LoginFormData) { return new Promise<void>((resolve, reject) => { console.log("modules.user.login"); AuthAPI.login(LoginFormData) .then((data) => { const { accessToken, user } = data; setAccessToken(accessToken); if (User.RoleList && Array.isArray(user.RoleList)) { roleList.value = user.RoleList; } else { roleList.value = []; // 确保总是数组 console.warn("RoleList is not an array or is undefined"); } resolve(); }) .catch((error) => { reject(error); }); }); } /** * 获取用户信息 * * @returns {UserInfo} 用户信息 */ function getUserInfo() { return new Promise<UserInfo>((resolve, reject) => { UserAPI.getInfo() .then((data) => { if (!data) { reject("Verification failed, please Login again."); return; } Object.assign(userInfo.value, { ...data }); resolve(data); }) .catch((error) => { reject(error); }); }); }
这行代码是关键,
const userInfo = useStorage<UserInfo>("userInfo", {} as UserInfo);
它使用了 useStorage 函数(通常来自 @vueuse/core 或类似的工具库)来创建一个响应式的引用,该引用会自动将数据持久化到浏览器的本地存储中。
具体分析:
-
存储时机:当调用
getUserInfo()方法时,通过这行代码:Object.assign(userInfo.value, { ...data });将获取到的用户数据赋值给
userInfo.value,此时useStorage会自动将这些数据保存到 localStorage。 -
存储位置:数据会被存储在浏览器的 localStorage 中,键名为
"userInfo"。 -
自动持久化:
useStorage会自动处理:- 初始时从 localStorage 读取数据
- 数据变更时自动写入 localStorage
- 页面刷新后数据仍然保持
验证方式:
你可以在浏览器开发者工具中查看:
- 打开「Application」标签页
- 选择「Local Storage」
- 查看你的网站域名下是否存在
userInfo这个键,其值就是序列化的用户信息
注意:clearUserData() 函数中没有直接清除 userInfo,你可能需要在其中添加:
userInfo.value = {} as UserInfo; // 保持响应式一致性 roleList.value = [] as RoleInfo[]; // 保持响应式一致性
来确保用户登出时本地存储的用户信息也被清除。
localStorage 与 useStorage 的区别
localStorage(原生 API)
直接操作本地存储的原生浏览器 API:
// 存储数据 localStorage.setItem('userInfo', JSON.stringify(userData)); // 读取数据 const data = JSON.parse(localStorage.getItem('userInfo') || '{}'); // 删除数据 localStorage.removeItem('userInfo'); // 清空所有 localStorage.clear();
特点:
- ✅ 浏览器原生支持,无需额外依赖
- ❌ 需要手动序列化/反序列化(JSON.stringify/JSON.parse)
- ❌ 不是响应式的,数据变更不会自动更新界面
- ❌ 需要手动处理存储和读取逻辑
useStorage(VueUse 工具函数)
基于 localStorage 封装的响应式工具函数:
import { useStorage } from '@vueuse/core'; // 自动处理序列化和响应式 const userInfo = useStorage('userInfo', {} as UserInfo); // 直接赋值,自动保存到 localStorage userInfo.value = newData; // 读取数据,直接使用即可 console.log(userInfo.value);
特点:
- ✅ 自动序列化/反序列化
- ✅ 响应式,数据变更自动更新界面
- ✅ 与 Vue 响应式系统无缝集成
- ✅ 类型安全(TypeScript 支持)
- ❌ 需要安装 @vueuse/core 依赖
对比示例
使用 localStorage:
// 需要手动处理 const user = ref(JSON.parse(localStorage.getItem('userInfo') || '{}')); function updateUser(newData) { user.value = newData; localStorage.setItem('userInfo', JSON.stringify(newData)); // 手动保存 }
使用 useStorage:
// 自动处理一切 const user = useStorage('userInfo', {}); function updateUser(newData) { user.value = newData; // 自动保存到 localStorage }
总结
| 特性 | localStorage | useStorage |
|---|---|---|
| 响应式 | ❌ 否 | ✅ 是 |
| 自动序列化 | ❌ 需要手动 | ✅ 自动 |
| Vue 集成 | ❌ 需要封装 | ✅ 无缝集成 |
| 类型安全 | ❌ 有限 | ✅ 完整TS支持 |
| 使用便捷性 | ❌ 较低 | ✅ 极高 |
推荐使用 useStorage,特别是在 Vue 项目中,它能极大简化本地存储的操作并提供更好的开发体验。
删除 userInfo 的 key 方法
1. 使用 useStorage 的推荐方式
如果你使用的是 @vueuse/core 的 useStorage:
import { useStorage } from '@vueuse/core'; // 清除数据并移除 key function clearUserData() { return new Promise<void>((resolve) => { clearToken(); usePermissionStoreHook().resetRouter(); useDictStoreHook().clearDictionaryCache(); //清除数据,key 还在 userInfo.value = {} as UserInfo; // 推荐方式:直接设置为 null 或 undefined,useStorage 会自动移除 key userInfo.value = null as any; // 或者 // userInfo.value = undefined as any; resolve(); }); }
2. 手动删除 localStorage 的 key
function clearUserData() { return new Promise<void>((resolve) => { clearToken(); usePermissionStoreHook().resetRouter(); useDictStoreHook().clearDictionaryCache(); // 方式1:直接操作 localStorage localStorage.removeItem('userInfo'); // 方式2:如果还想保持响应式,需要重新赋值 userInfo.value = {} as UserInfo; resolve(); }); }
3. 完整的清除方案
/** * 清理用户数据(完全清除) */ function clearUserDataCompletely() { return new Promise<void>((resolve) => { clearToken(); usePermissionStoreHook().resetRouter(); useDictStoreHook().clearDictionaryCache(); // 方案A:使用 localStorage.removeItem(推荐) localStorage.removeItem('userInfo'); userInfo.value = {} as UserInfo; // 保持响应式一致性 // 方案B:设置为 null/undefined(useStorage 会自动处理) // userInfo.value = null as any; resolve(); }); }
4. 验证 key 是否被删除
// 检查 key 是否存在的方法 function isUserInfoKeyExists(): boolean { return localStorage.getItem('userInfo') !== null; } // 使用示例 console.log('userInfo key exists:', isUserInfoKeyExists()); // false
推荐方案
在你的 clearUserData 函数中这样实现:
function clearUserData() { return new Promise<void>((resolve) => { clearToken(); usePermissionStoreHook().resetRouter(); useDictStoreHook().clearDictionaryCache(); // 同时操作 localStorage 和响应式变量 localStorage.removeItem('userInfo'); // 删除 key userInfo.value = {} as UserInfo; // 重置响应式变量 resolve(); }); }
这样就能确保:
- ✅ localStorage 中的
userInfokey 被完全删除 - ✅ 响应式变量保持一致性
- ✅ 下次访问时不会读取到旧的缓存数据