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

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

相关推荐
掘金安东尼25 分钟前
GPT-4.5 被 73% 的人误认为人类,“坏了?!我成替身了!”
人工智能·程序员
庸俗今天不摸鱼32 分钟前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
QTX1873033 分钟前
JavaScript 中的原型链与继承
开发语言·javascript·原型模式
黄毛火烧雪下39 分钟前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox1 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞1 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行1 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758101 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox
掘金一周1 小时前
金石焕新程 >> 瓜分万元现金大奖征文活动即将回归 | 掘金一周 4.3
前端·人工智能·后端
三翼鸟数字化技术团队1 小时前
Vue自定义指令最佳实践教程
前端·vue.js