react 单页面应用限制多开登录
情景
测试小姐姐提了一个 BUG : 在同一浏览器中打开两个页面,两个页面分别登录不同的账号.A 页面先登录A, B 页面再登录B,此时回到 A 页面,交互时账号数据应该刷新为 B 登录的账号
分析
这个问题,其实没什么必要,因为我不认为我们这个系统的单个使用者会同时拥有多个账号,但人家非说会有,那行吧,我说了不算,还是考虑解决问题吧。
React 也是 SPA 应用,在一个页面中变更数据并不会直接影响到另一个页面,所以我们得让我们的应用与某些全局共享的东西保持同步,比如站点在浏览器中的本地存储。欸,这不就来了嘛,用户登录的数据(token,基础信息等)我们往往会通过 localStorage 持久化存储在浏览器中,只需要监听存储在 localStorage 的用户数据变化即可同步我们的页面。
方案
- 在一个全局存在的组件中(如 Header,或者干脆 RootApp 等),监听 storage
- 绑定监听 storage 触发的函数,获取 localStorage 中的 userInfo(假设我们把登录成功后的用户数据全存在 userInfo 字段)
- 如果 userInfo 存在,更新 react 应用的状态中共享的用户数据,并强制刷新页面
- 如果 userInfo 没了,清空 react 应用的状态中共享的用户数据,并强制退出登录
伪代码
userManage.ts: 一些存取变更 localStorage 用户数据的封装
ts
export default {
state,
login, // 这个就是 localStorage.setItem("userInfo", val)
logout, // 这个就是 localStorage.clear(); 和 window.location 重定向到登录页
getUserInfo, // 这个就是尝试 localStorage.getItem("userInfo") 并反序列化
};
某个全局组件:
ts
useEffect(() => {
/** 监听 localStorage 的 userInfo 变化并同步 */
async function syncUser() {
const userInfo = localStorage.getItem('userInfo');
if (userInfo) {
userManage.login(JSON.parse(userInfo));
window.location.reload();
} else {
userManage.logout();
}
}
window.addEventListener('storage', syncUser);
return () => {
window.removeEventListener('storage', syncUser);
};
}, []);
这样就基本实现了多开限制,Bingo!