在React JS中如何禁止用户打开同一网站的多个标签页
限制网站单实例运行解决多标签页账号信息错乱问题
问题本质
同域下的localStorage是所有标签页共享的,当用户在多个标签页登录不同账号时,后写入的用户信息会覆盖已有的存储内容,导致各标签页读取的用户信息不匹配,出现业务逻辑错乱。
实现方案
我们可以通过全局实例锁+storage事件监听的机制实现单实例限制,具体实现逻辑如下:
基础实现步骤
- 页面初始化时生成当前实例的唯一ID:
const currentInstanceId = Math.random().toString(36).slice(2) - 读取localStorage中存储的全局运行实例标识
SITE_RUNNING_INSTANCE_ID:- 如果标识为空,说明当前无运行实例,将自身ID写入该字段,完成实例锁占用
- 如果标识不为空且不等于当前实例ID,说明已有其他实例运行,直接提示用户「当前网站仅允许同时打开一个标签页」,并调用
window.close()关闭当前页面
- 监听全局
storage事件,当检测到其他页面尝试修改SITE_RUNNING_INSTANCE_ID时,若当前页面是持有锁的实例,立即将该字段的值改回自身ID,拦截新实例的启动请求 - 监听页面
beforeunload事件,当前持锁实例关闭时,删除SITE_RUNNING_INSTANCE_ID字段释放锁,允许后续新实例启动
代码示例
// 初始化实例ID const currentInstanceId = Math.random().toString(36).slice(2); const INSTANCE_KEY = 'SITE_RUNNING_INSTANCE_ID'; // 检查是否已有运行实例 const existedInstanceId = localStorage.getItem(INSTANCE_KEY); if (existedInstanceId && existedInstanceId !== currentInstanceId) { alert('当前网站仅允许同时打开一个标签页'); window.close(); // 防止close被浏览器拦截的兜底逻辑,可以跳转到空白提示页 // window.location.href = 'about:blank'; } // 占用实例锁 localStorage.setItem(INSTANCE_KEY, currentInstanceId); // 监听其他实例的启动请求,拦截抢占 window.addEventListener('storage', (e) => { if (e.key === INSTANCE_KEY && e.newValue !== currentInstanceId) { localStorage.setItem(INSTANCE_KEY, currentInstanceId); } }); // 页面关闭时释放锁 window.addEventListener('beforeunload', () => { if (localStorage.getItem(INSTANCE_KEY) === currentInstanceId) { localStorage.removeItem(INSTANCE_KEY); } });
异常兼容优化
如果遇到页面崩溃、进程被强制杀死等极端情况,beforeunload事件不会触发,会导致实例锁一直被占用无法启动新实例,可以增加心跳机制解决:
- 持锁实例每隔10s往localStorage写入
SITE_INSTANCE_HEARTBEAT时间戳 - 新实例启动时如果发现存在实例锁,但心跳时间距离当前超过30s,判定旧实例已失效,直接抢占实例锁即可
可选替代方案
如果你的业务场景不需要严格限制单实例,只是要解决多账号登录信息不冲突的问题,可以将用户敏感信息存储到仅当前标签页生效的sessionStorage中,也可以避免跨标签页的信息覆盖问题。
内容的提问来源于stack exchange,提问作者Anjali Shah
相关产品推荐
相关产品推荐

