【React与Vue】如何在页签中监听 LocalStorage 变化?这些方法你都试过吗?

在开发中,你是否会碰到过这样的需求:需要监听 LocalStorage 的变化。这在不同浏览器页签间是相对简单的,因为浏览器提供了内置的 storage 事件。但在同一个浏览器页签下,却没有直接的方式实现。今天,我们探讨下有几种高效且易用的解决方案,可以帮助轻松应对这种需求!

在这里插入图片描述

传统方案的痛点

先来看看浏览器是如何帮助我们处理不同页签的 LocalStorage 变化:

window.addEventListener("storage", (event) => {      if (event.key === "myKey") {// 执行相应操作}
});

通过监听 storage 事件,当在其他页签修改 LocalStorage 时,你可以在当前页签捕获到这个变化。但问题是:这种方法只适用于跨页签的 LocalStorage 修改,在同一页签下无法触发该事件。于是,很多开发者开始寻求替代方案,比如:

1、轮询(Polling)

轮询是一种最直观的方式,它定期检查 localStorage 的值是否发生变化。然而,这种方法性能较差,尤其在高频轮询时会对浏览器性能产生较大的影响,因此不适合作为长期方案。

let lastValue = localStorage.getItem('myKey');setInterval(() => {const newValue = localStorage.getItem('myKey');if (newValue !== lastValue) {lastValue = newValue;console.log('Detected localStorage change:', newValue);}
}, 1000); // 每秒检查一次

这种方式实现简单,不依赖复杂机制。但是性能较差,频繁轮询会影响浏览器性能。

2、监听代理(Proxy)或发布-订阅模式

这种方式通过创建一个代理来拦截 localStorage.setItem 的调用。每次数据变更时,我们手动发布一个事件,通知其他监听者。

(function() {const originalSetItem = localStorage.setItem;const subscribers = [];localStorage.setItem = function(key, value) {originalSetItem.apply(this, arguments);subscribers.forEach(callback => callback(key, value));};function subscribe(callback) {subscribers.push(callback);}subscribe((key, value) => {if (key === 'myKey') {console.log('Detected localStorage change:', value);}});localStorage.setItem('myKey', 'newValue');
})();

这种比较灵活,可以用于复杂场景。但是需要手动拦截 setItem,维护成本高(但也是值得推荐的)。

然而,这些方案往往存在性能问题或者开发的复杂度,在高频数据更新的情况下,有一定的性能问题,而且存在一定的风险性。那么有没有可以简单快速,风险性还小的方案呢?

高效的解决方案

既然浏览器不支持同一页签的 storage 事件,我们可以手动触发事件,以此来实现同一页签下的 LocalStorage 变化监听。

1、自定义 Storage 事件

通过手动触发 StorageEvent,你可以在 LocalStorage 更新时同步分发事件,从而实现同一页签下的监听。

localStorage.setItem('myKey', 'value');// 手动创建并分发 StorageEvent
const storageEvent = new StorageEvent('storage', {key: 'myKey',url: window.location.href
});
window.dispatchEvent(storageEvent);

你可以使用相同的监听逻辑来处理数据变化,无论是同一页签还是不同页签:

window.addEventListener("storage", (event) => {if (event.key === "myKey") {// 处理 LocalStorage 更新}
});

这种实现简单、轻量、快捷。但是需要手动触发事件。

2、基于 CustomEvent 的自定义事件

StorageEvent 类似,你可以使用 CustomEvent 手动创建并分发事件,实现 localStorage 的同步监听。

localStorage.setItem('myKey', 'newValue');const customEvent = new CustomEvent('localStorageChange', {detail: { key: 'myKey', value: 'newValue' }
});
window.dispatchEvent(customEvent);

这种方式适合更加灵活的事件触发场景。CustomEvent不局限于 localStorage 事件,可以扩展到其他功能。

window.addEventListener('localStorageChange', (event) => {const { key, value } = event.detail;if (key === 'myKey') {console.log('Detected localStorage change:', value);}
});
3、MessageChannel(消息通道)

MessageChannel API 可以在同一个浏览器上下文中发送和接收消息。我们可以通过 MessageChannellocalStorage 的变化信息同步到其他部分,起到类似事件监听的效果。

const channel = new MessageChannel();channel.port1.onmessage = (event) => {console.log('Detected localStorage change:', event.data);
};localStorage.setItem('myKey', 'newValue');
channel.port2.postMessage(localStorage.getItem('myKey'));

适合组件通信和复杂应用场景,消息机制较为灵活。相对复杂的实现,可能不适合简单场景。

4、BroadcastChannel

BroadcastChannel 提供了一种更高级的浏览器通信机制,允许多个窗口或页面之间广播消息。你可以通过这个机制将 localStorage 变更同步到多个页面或同一页面的不同部分。

const channel = new BroadcastChannel('storage_channel');channel.onmessage = (event) => {console.log('Detected localStorage change:', event.data);
};localStorage.setItem('myKey', 'newValue');
channel.postMessage({ key: 'myKey', value: 'newValue' });

支持跨页面通信,方便在不同页面间同步数据,易于实现。适用场景较为具体,通常用于复杂的页面通信需求。

这4个方法,主打的就是一个见缝插针,简单快速,风险性低。但是客观角度来讲,每种方案都是有各自优势的。

优势对比

方案优点缺点适用场景
轮询实现简单,适合低频监控需求性能差,频繁轮询影响浏览器性能简单场景或临时方案
监听代理/发布-订阅模式灵活扩展,适合复杂项目需要手动拦截 setItem,维护成本高需要手动事件发布的场景
自定义 StorageEvent实现简单,原生支持 storage 事件监听需要手动触发事件同页签下 localStorage 监听
自定义事件灵活的事件管理,适合不同场景需要手动触发事件需要自定义触发条件的场景
MessageChannel适合组件通信和复杂应用场景实现复杂,不适合简单场景高级组件通信需求
BroadcastChannel跨页面通信,适合复杂通信需求使用场景较具体复杂的多窗口通信

如何在 React / Vue 使用

在主流前端框架(如 React 和 Vue)中,监听 LocalStorage 变化并不困难。无论是 React 还是 Vue,你都可以使用自定义的 StorageEvent 或其他方法来实现监听。在此,我们以自定义 StorageEvent 为例,展示如何在 React 和 Vue 中实现 LocalStorage 的监听。

1. 在 React 中使用自定义 StorageEvent

React 是一个基于组件的框架,我们可以使用 React 的生命周期函数(如 useEffect)来监听和处理 LocalStorage 的变化。

import React, { useEffect } from 'react';const LocalStorageListener = () => {useEffect(() => {// 定义 storage 事件监听器const handleStorageChange = (event) => {if (event.key === 'myKey') {console.log('Detected localStorage change:', event.newValue);}};// 添加监听器window.addEventListener('storage', handleStorageChange);// 模拟触发自定义的 StorageEventconst triggerCustomStorageEvent = () => {const storageEvent = new StorageEvent('storage', {key: 'myKey',newValue: 'newValue',url: window.location.href,});window.dispatchEvent(storageEvent);};// 组件卸载时移除监听器return () => {window.removeEventListener('storage', handleStorageChange);};}, []); // 空依赖数组表示该 effect 只会在组件挂载时运行return (<div><button onClick={() => localStorage.setItem('myKey', 'newValue')}>修改 localStorage</button><button onClick={() => window.dispatchEvent(new StorageEvent('storage', {key: 'myKey',newValue: localStorage.getItem('myKey'),url: window.location.href,}))}>手动触发 StorageEvent</button></div>);
};export default LocalStorageListener;
  • useEffect 是 React 的一个 Hook,用来处理副作用,在这里我们用它来注册和清除事件监听器。
  • 我们手动触发了 StorageEvent,以便在同一页面中监听 LocalStorage 的变化。
2. 在 Vue 中使用自定义 StorageEvent

在 Vue 3 中,我们可以使用 onMountedonUnmounted 这两个生命周期钩子来管理事件监听器。(Vue 3 Composition API):

<template><div><button @click="updateLocalStorage">修改 localStorage</button><button @click="triggerCustomStorageEvent">手动触发 StorageEvent</button></div>
</template><script lang="ts" setup>
import { onMounted, onUnmounted } from 'vue';const handleStorageChange = (event: StorageEvent) => {if (event.key === 'myKey') {console.log('Detected localStorage change:', event.newValue);}
};const updateLocalStorage = () => {localStorage.setItem('myKey', 'newValue');
};const triggerCustomStorageEvent = () => {const storageEvent = new StorageEvent('storage', {key: 'myKey',newValue: 'newValue',url: window.location.href,});window.dispatchEvent(storageEvent);
};onMounted(() => {window.addEventListener('storage', handleStorageChange);
});onUnmounted(() => {window.removeEventListener('storage', handleStorageChange);
});
</script>
  • 使用了 Vue 的 Composition API,其中 onMountedonUnmounted 类似于 React 的 useEffect,用于在组件挂载和卸载时管理副作用。
  • 同样手动触发了 StorageEvent 来监听同一页面中的 LocalStorage 变化。

提炼封装一下

无论是 React 还是 Vue,将自定义 StorageEvent 实现为一个组件或工具函数是常见的做法。你可以将上面的逻辑提取到一个独立的 hook 或工具函数中,方便在项目中多次使用。

在 React 中提取为 Hook
import { useEffect } from 'react';const useLocalStorageListener = (key, callback) => {useEffect(() => {const handleStorageChange = (event) => {if (event.key === key) {callback(event.newValue);}};window.addEventListener('storage', handleStorageChange);return () => {window.removeEventListener('storage', handleStorageChange);};}, [key, callback]);
};export default useLocalStorageListener;
在 Vue 中提取为工具函数
import { onMounted, onUnmounted } from 'vue';export const useLocalStorageListener = (key: string, callback: (value: string | null) => void) => {const handleStorageChange = (event: StorageEvent) => {if (event.key === key) {callback(event.newValue);}};onMounted(() => {window.addEventListener('storage', handleStorageChange);});onUnmounted(() => {window.removeEventListener('storage', handleStorageChange);});
};
  • 在 React 中,我们创建了一个自定义 Hook useLocalStorageListener,通过传入监听的 key 和回调函数来捕获 LocalStorage 的变化。
  • 在 Vue 中,我们创建了一个工具函数 useLocalStorageListener,同样通过传入 key 和回调函数来监听变化。

在这里插入图片描述

总结

在同一个浏览器页签中监听 localStorage 的变化并非难事,但不同场景下需要不同的方案。从简单的轮询到高级的 BroadcastChannel,本文介绍的几种方案各有优缺点。根据你的实际需求,选择合适的方案可以帮助你更高效地解决问题。

  • 简单需求:可以考虑使用自定义 StorageEventCustomEvent 实现监听。
  • 复杂需求:对于更高级的场景,如跨页面通信,MessageChannelBroadcastChannel 是更好的选择。

如果你有其他的优化技巧或问题,欢迎在评论区分享,让我们一起交流更多的解决方案!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/149688.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

化工企业如何精准网络营销

合作咨询联系竑图 hongtu201988 网络推广已成为各行各业不可或缺的一部分&#xff0c;尤其是化工企业&#xff0c;其产品的专业性和复杂性更需要通过有效的网络推广手段来触达目标客户群体。以下是一份针对化工企业的网络推广方案&#xff0c;旨在通过多渠道的营销策略&#x…

【机器学习】---元强化学习

目录 1. 元学习简介1.1 什么是元学习&#xff1f;1.2 元学习的应用 2. 强化学习基础2.1 什么是强化学习&#xff1f;2.2 强化学习的基本框架2.3 深度强化学习 3. 元强化学习的概念与工作原理3.1 元强化学习是什么&#xff1f;3.2 元强化学习与普通强化学习的区别 4. 元强化学习…

Google 提供基于AI的模糊测试框架

人工智能驱动的 OSS-Fuzz 工具可以帮助发现漏洞&#xff0c;并与自动修补管道相结合。 模糊测试可以成为找出软件中零日漏洞的宝贵工具。为了鼓励开发人员和研究人员使用它&#xff0c;谷歌周三宣布&#xff0c;免费提供其模糊测试框架OSS-Fuzz。 根据谷歌的说法&#xff0c;通…

初学51单片机之I2C总线与E2PROM

首先先推荐B站的I2C相关的视频I2C入门第一节-I2C的基本工作原理_哔哩哔哩_bilibili 看完视频估计就大概知道怎么操作I2C了&#xff0c;他的LCD1602讲的也很不错&#xff0c;把数据建立tsp和数据保持thd&#xff0c;比喻成拍照时候的摆pose和按快门两个过程&#xff0c;感觉还是…

什么是SSL证书?它能保护你的网络安全!

相信大家在浏览网页时经常会看到一些网址前面有个“小锁”图标&#xff0c;它代表的网站是安全的&#xff0c;而这背后的秘密就是SSL证书。那SSL证书到底是什么&#xff1f;它有什么用呢&#xff1f; 什么是SSL证书&#xff1f; SSL证书的全称是Secure Sockets Layer证书&…

C/C++指针的前世今生

前言 老早之前就想写这个内容了&#xff0c;打了草稿后闲置了两个月&#xff0c;因为其他事就没再动过这个东西了&#xff0c;今天翻草稿箱的时候发现了它&#xff0c;就把它完善出来&#xff0c;顺便我也学习学习。 正文 指针的前世今生 前面先说一下&#xff0c;故事是随…

Linux文件IO(十一)-复制文件描述符与截断文件

1.复制文件描述符 在 Linux 系统中&#xff0c;open 返回得到的文件描述符 fd 可以进行复制&#xff0c;复制成功之后可以得到一个新的文件描述符&#xff0c;使用新的文件描述符和旧的文件描述符都可以对文件进行 IO 操作&#xff0c;复制得到的文件描述符和旧的文件描述符拥…

想转行AI大模型开发但不知如何下手?看这篇就够了!

原创 最近有很多小伙伴问我&#xff0c;之前从事的其他领域的编程&#xff0c;现在想要学习AI大模型开发的相关技能&#xff0c;不知道从哪下手&#xff0c;应该学习些什么&#xff0c;下面四个是我认为从事大模型开发&#xff0c;必须掌握的四个开源工具&#xff0c;大家可以…

2024年模式识别与图像分析国际学术会议(PRIA 2024)

2024年模式识别与图像分析国际学术会议&#xff08;PRIA 2024&#xff09; 2024 International Conference on Pattern Recognition and Image Analysis 2024年10月18-20日 南京 三轮截稿日期&#xff1a;10月10日 2024年模式识别与图像分析国际学术会议&#xff08;PRIA 2…

流水线部署失败排查指南

在现代软件开发中&#xff0c;CI/CD&#xff08;持续集成/持续交付&#xff09;流水线是确保代码质量和快速交付的重要工具。然而&#xff0c;部署失败时&#xff0c;排查问题的能力至关重要。以下是一些常见的故障排查步骤和技巧。 ## 1. 检查流水线日志 首先&#xff0c;查看…

【JAVA-数据结构】时间空间复杂度计算案例

接着上一篇文章&#xff0c;对应举一些例子。 1.时间复杂度 【实例1】 // 计算func2的时间复杂度&#xff1f; void func2(int N) {int count 0;for (int k 0; k < 2 * N ; k) {count;} int M 10;while ((M--) > 0) {count;} System.out.println(count); } 基本操作…

什么是远程过程调用(RPC)

进程间通信(IPC) 进程间通信(Inter-Process Communication)是指两个进程或者线程之间传送数据或者信号的一些技术或者方法。进程是计算机进行资源分配的最小的单位。每个进程都有自己独立的系统资源,而且彼此之间是相对隔离的。为了使得不同的进程之间能够互相访问,相互协…

简单的mybatis batch插入批处理

简单的mybatis batch插入批处理 1.需求 公司的权限管理功能有一个岗位关联资源的分配操作&#xff0c;如果新增一个岗位&#xff0c;有时候需要将资源全部挂上去&#xff0c;原有的是for循环插入资源信息&#xff0c;发现有时候执行速度过慢&#xff0c;所以此处想修改为批处…

基于TCP协议的网络通信

TCP即传输控制协议&#xff0c;基于TCP协议的网络通信总是面向连接的&#xff0c;在通信过程中需要进行“三次握手&#xff0c;四次挥手”&#xff0c;这是众所周知的&#xff0c;所以这里不过多赘述。我们都知道TCP协议传输数据比较稳定&#xff0c;那么为什么稳定&#xff0c…

pip的安装和使用

pip的安装和使用 1、 pip 是一个现代的&#xff0c;通用的 Python 包管理工具。提供了对 Python 包的查找、下载、安装、卸载的功能。便于我们对Python的资源包进行管理。 2、注&#xff1a;pip 已内置于 Python 3.4 和 2.7 及以上版本&#xff0c;其他版本需另行安装。 3、在安…

RAG高级优化:一文看尽query的转换之路

准确地找到与用户查询最相关的信息是RAG系统成功的关键&#xff0c;如何帮助检索系统提升召回的效果是RAG系统研究的热门方向。本文将介绍三种query理解的方法&#xff0c;以增强检索增强生成(RAG)系统中的检索过程&#xff1a; 查询重写&#xff1a; 重新定义查询&#xff0c;…

[Python学习日记-29] 开发基础练习2——三级菜单与用户登录

[Python学习日记-29] 开发基础练习2——三级菜单与用户登录 简介 三级菜单 用户登录 简介 该练习使用了列表、字典、字符串等之前学到的数据类型&#xff0c;用于巩固实践之前学习的内容。 三级菜单 一、题目 数据结构&#xff1a; menu { 北京: { 海淀: { …

什么是unix中的fork函数?

一、前言 在本专栏之前的文档中已经介绍过unix进程环境相关的概念了&#xff0c;本文将开始介绍unix中一个进程如何创建出新进程&#xff0c;主要是通过fork函数来实现此功能。本文将包含如下内容&#xff1a; 1.fork函数简介 2.父进程与子进程的特征 3.如何使用fork创建新进程…

基于单片机的指纹打卡系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52RC&#xff0c;采用两个按键替代指纹&#xff0c;一个按键按下&#xff0c;LCD12864显示比对成功&#xff0c;则 采用ULN2003驱动步进电机转动&#xff0c;表示开门&#xff0c;另一个…