小问题背后的思考:Token如何跨页签同步

问题

昨天组员遇到了一个非常有意思的token问题,在群里寻找答案,过程是这样的。 在浏览器打开两个页签A和B,分别登录A系统,此时两个页签属于同域名、同会话状态。 前端的token是保存在storage中的,同时在vuexstore中也保存了一份。

此时打开B页签的页面,退出又重新登录系统,storage里面的token会重新生成,由于B页签是重新登录的,所以,B页签的页面是可以正常加载成功的。

此时问题出现了,A页签的系统在文件上传时报错,提示token失效,因为上传的token是通过vuexstore进行获取的,B页签虽然重新登录了系统,但是A页签的store数据并没有刷新,如何在不刷新A页签的情况下,及时更新Token状态?

如何解决

大家可以思考一下,这个问题都有哪些解决办法。

监听 Storage 变化

window添加storage事件,当localStorage或者sessionStorage值发生变化时执行。

js 复制代码
onMounted(() => {
  window.addEventListener("storage", function (event) {
    console.log(
      "Storage change: ",
      event.key,
      "from",
      event.oldValue,
      "to",
      event.newValue
    );
  });
});

动画演示:

开启两个页签,分别打开同一个页面,右侧点击登录时,会给localStorage写入一个token,此时左侧页面监听到了数据变化。

如果是真实业务场景,我们就可以根据登录token更新vuex中的状态,从而避免老页面过期问题。

进一步扩展

假设,右边的登录和退车操作后,左边保持同步,如何实现?

js 复制代码
<script setup>
import { ref, onMounted } from "vue";

const userName = ref("");
onMounted(() => {
  localStorage.removeItem("token");
  window.addEventListener("storage", function (event) {
    console.log(
      "Storage change: ",
      event.key,
      "from",
      event.oldValue,
      "to",
      event.newValue
    );
    if (event.key === "token" && event.newValue) {
      userName.value = "河畔一角";
    } else {
      userName.value = "";
    }
  });
});
// 登录
const login = () => {
  userName.value = "河畔一角";
  localStorage.setItem("token", "999");
};
// 退出
const logout = () => {
  userName.value = "";
  localStorage.removeItem("token");
};
</script>

我们在监听事件里面,根据token值来判断用户状态,从而保持同步。

动画演示:

增加登录和退出按钮,定义userName变量,登录后,给userName赋值,并向localStorage中写入token,退出后,清空值。

监听事件里面可以根据token的变化,控制userName的值,从而实现左右页面同步。

注意: 大家通过动画演示应该发现了一个问题,为什么右侧页面的控制台没有打印值?是因为原始页面作为触发源,不参与事件监听,通过这个机制可以实现多个页面之间的通信。

使用 BroadcastChannel

BroadcastChannel 允许在相同的源在浏览器上下文(windows,tabs,frames或者iframes)之间进行简单的通信。

它有几个知识点:

  • 创建频道:new BroadcastChannel('channel')
  • 发送消息:channel.postMessage(message);
  • 接收消息:channel.onmessage = (data)=>{}
  • 异常处理:channel.onmessageerror = (e)=>{}
  • 关闭频道:channel.close()

代码示例:

js 复制代码
<script setup>
import { ref, onMounted } from "vue";
const channel = new BroadcastChannel("myChannel");
const userName = ref("");
onMounted(() => {
  channel.onmessage = function (event) {
    console.log("Received message:", event.data);
    if (event.data === "token is null") {
      userName.value = "";
    }else{
      userName.value = "河畔一角";
    }
  };
});
// 登录
const login = () => {
  channel.postMessage("token is 999");
  userName.value = "河畔一角";
};
// 退出
const logout = () => {
  channel.postMessage("token is null");
  userName.value = "";
};
</script>

动画演示:

这个方案,其实就不需要去监听storage的变化了,因为在退出的时候,可以直接发布一条消息,接收到消息以后,就可以更新页面状态了。

其他

  • web worker主要用来做计算的,不能直接用于跨标签通信,不太适合。
  • window.postMessage主要用来实现跨页面通信,比如 A页面通过postMessage给B页面发送消息,此场景也不太适合。
  • websocket 固然可以实现,但用在这个场景岂不浪费。
  • 轮询:虽然也能勉强实现,但一般用在异步回调场景,此处也不适合。

总结

其实对于token更新,本身一个非常小的问题,甚至都可以忽略,如果出现了,手动刷新一下页面即可。但从解决问题的角度出发,它的确是一个问题,那就需要考虑跨页签通信。

上面的两套方案,我反而觉得监听storage是最简单的。如果你有更好的方案,可以贴在评论区,共同探讨。

感谢大家,我是河畔一角,一名普通的前端工程师。

相关推荐
Pedantic14 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘15 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆15 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师16 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆16 小时前
VSCode自动格式化三要素
前端
爱勇宝17 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
AskHarries17 小时前
工具失败时怎么办:重试、回滚、人工确认和风险提示
后端·程序员
kyriewen17 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user205855615181319 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端