小问题背后的思考: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是最简单的。如果你有更好的方案,可以贴在评论区,共同探讨。

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

相关推荐
右耳朵猫AI4 分钟前
Web前端周刊2026W37 | Shopify 转原生、React 编译 Rust 化、Vitest 5.0、Rslib 1.0
前端·javascript·react.js·typescript·node.js
涛涛ing10 分钟前
2026年,这5个JS新趋势正在悄悄改写前端
前端
moonsims19 分钟前
AiBrainBox-UGV 的目标跟踪从“传统视觉跟踪”升级为“语义目标跟踪”
前端·人工智能·量子计算
驭渊的小故事26 分钟前
Spring Boot 注解详解01
java·前端·spring boot
pe7er32 分钟前
引子
前端·后端·架构
SimonKing34 分钟前
阅后即焚的加密便签:Cryptgeon,你口袋里的秘密信使
java·后端·程序员
恋猫de小郭40 分钟前
CPF-Flutter 社区提出折叠场景分栏(平行视界) 方案
android·前端·flutter
aixingpan1 小时前
aixingpan.cn API开发文档:api_docs_trichart_natal_marx_transit2接口指南
前端·php
csj501 小时前
前端基础之《React(13)—表单绑定、列表渲染》
前端·react.js
csj501 小时前
前端基础之《React(12)—条件渲染》
前端·react.js