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

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

相关推荐
狸克先生11 分钟前
如何用AI写小说(二):Gradio 超简单的网页前端交互
前端·人工智能·chatgpt·交互
sinat_3842410913 分钟前
在有网络连接的机器上打包 electron 及其依赖项,在没有网络连接的机器上安装这些离线包
javascript·arcgis·electron
baiduopenmap25 分钟前
百度世界2024精选公开课:基于地图智能体的导航出行AI应用创新实践
前端·人工智能·百度地图
loooseFish33 分钟前
小程序webview我爱死你了 小程序webview和H5通讯
前端
小牛itbull37 分钟前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
请叫我欧皇i1 小时前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
533_1 小时前
[vue] 深拷贝 lodash cloneDeep
前端·javascript·vue.js
本当迷ya1 小时前
💖2025年不会Stream流被同事排挤了┭┮﹏┭┮(强烈建议实操)
后端·程序员
guokanglun1 小时前
空间数据存储格式GeoJSON
前端
GIS瞧葩菜1 小时前
局部修改3dtiles子模型的位置。
开发语言·javascript·ecmascript