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

news2024/12/27 11:14:16

在开发中,你是否会碰到过这样的需求:需要监听 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);

    // 模拟触发自定义的 StorageEvent
    const 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.coloradmin.cn/o/2160481.html

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

相关文章

中电金信多模态鉴伪技术抵御AI造假威胁

AI换脸技术&#xff0c;属于深度伪造最常见方式之一&#xff0c;是一种利用人工智能生成逼真的虚假人脸图片或视频的技术。基于深度学习算法&#xff0c;可以将一个人的面部特征映射到另一个人的面部&#xff0c;创造出看似真实的伪造内容。近年来&#xff0c;以AI换脸为代表的…

关于Cursor使用的小白第一视角

最近看破局感觉洋哥总是提到cursor&#xff0c;感觉好火&#xff0c;所以打算学习一下怎么用Cursor&#xff0c;如果可以希望能做一个我自己的网站。 之前从来没用过Cursor。所以&#xff0c;这是一篇小白视角的Cursor使用教程。 如果你也是一个小白&#xff0c;并且对Cursor…

【Python】Spyder:科学 Python 开发环境

在数据科学和科学计算领域&#xff0c;Python 已经成为了一个不可或缺的工具。为了提高开发效率和改善编程体验&#xff0c;一个功能强大且用户友好的开发环境是必需的。Spyder&#xff08;Scientific Python Development Environment&#xff09;正是这样一个为科学计算和数据…

Redhat 7,8,9系(复刻系列) 一键部署Oracle19c rpm

Oracle19c前言 Oracle 19c 是甲骨文公司推出的一款企业级关系数据库管理系统,它带来了许多新的功能和改进,使得数据库管理更加高效、安全和可靠。以下是关于 Oracle 19c 的详细介绍: 主要新特性 多租户架构:支持多租户架构,允许多个独立的数据库实例在同一个物理服务器上…

网络PPP协议802.11协议以太网协议IPV4协议在思科模拟器的实现

1&#xff09;PPP协议 1. 选择2620系列交换机&#xff0c;添加WIC-2t模块&#xff0c;具有两个serial串行接口&#xff1b; 2.Router>enable:进入特权模式 Router#configure terminal&#xff1a;全局配置模式 Enter configuration commands, one per line. End with CNTL…

配置win10开电脑时显示可登录账号策略

有1台公用的windows10电脑&#xff0c;电脑上有N多用户&#xff0c;使用人员登录时选择相应的账号登录即可。但在某次使用脚本加固后&#xff0c;发现之前显示的用户都不能显示了。检查加固脚本&#xff0c;是脚本启用了“交互式登录&#xff1a;不显示上次登录”策略。因此&am…

基于SpringBoot+Vue的垃圾分类回收管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

RHCSA认证-Linux(RHel9)-Linux入门

文章目录 概要一、创建、查看和编辑⽂本1.1 输出重定向1.2 vim编辑器1.3 shell 变量1.5 获取帮助 二、管理本地用户和组2.1 描述用户2.2 切换用户和赋权2.3 用户管理2.4 用户组管理2.5 密码策略 三、控制文件访问3.1 列出文件和文件权限3.2 更改文件权限和拥有者3.3 控制默认权…

【中级通信工程师】终端与业务(二):终端产品

【零基础3天通关中级通信工程师】 终端与业务(二)&#xff1a;终端产品 本文是中级通信工程师考试《终端与业务》科目第二章《终端产品》的复习资料和真题汇总。终端与业务是通信考试里最简单的科目&#xff0c;有效复习通过率可达90%以上&#xff0c;本文结合了高频考点和近几…

JUC并发编程_阻塞队列 BlockingQueue

JUC并发编程_阻塞队列 BlockingQueue 一、基本概念二、主要特性三、常用方法四、实现类ArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueueSynchronousQueue 五、使用场景六、注意事项 一、基本概念 阻塞队列是一种特殊的队列&#xff0c;它除了支持普通队列的插入&…

电动车车牌识别系统源码分享

电动车车牌识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer V…

VUE-CLI配置全局SCSS变量

一.引入node-sass和sass-loader依赖 npm install --save-dev sass-loader node-sass 二. 在项目根目录中创建一个scss变量文件&#xff0c;比如constant.scss&#xff1a; 三.在vue.config.js文件中引入这个变量文件&#xff1a; // vue.config.js module.exports {css: {l…

艾体宝产品丨无需代码开发!Redis数据集成助你轻松优化数据库

我们不仅致力于加速应用程序的构建过程&#xff0c;更专注于助力您达成最终目标——实现应用的高效运行。因此&#xff0c;我们欣然宣布&#xff0c;Redis 数据集成&#xff08;Redis Data Integration&#xff0c;RDI&#xff09;(https://redis.io/data-integration/) 已经正…

自动化学习1:pytest自动化框架的基本用法:注意事项/断言assert/测试结果分析

一.注意事项&#xff1a; ①创建test开头的文件&#xff08;test_&#xff09;/类/函数或方法 ②pytest中以每一个函数或方法&#xff0c;作为用例 ③pytest启动方式&#xff1a;pytest def test01(): # 函数&#xff08;写在类外边是函数&#xff09;passclass Test:def t…

【机器学习】决策树算法

目录 算法引入 基尼系数&#xff1a; 决策树算法概述 决策树的关键概念 决策树的构建 代码实现 1. 定义决策树节点 2. 计算信息增益 3. 选择最佳分割特征 4. 构建决策树 5. 决策树预测 决策树的评估指标&#xff1a; 决策树的优缺点 优点&#xff1a; 缺点&…

Mitsuba 渲染基础

Mitsuba 渲染基础 0. Abstract1. 安装 Mitsuba21.1 下载 Mitsuba2 源码1.2 选择后端 (variants)1.3 编译 2. [Mitsuba2PointCloudRenderer](https://github.com/tolgabirdal/Mitsuba2PointCloudRenderer)2.1 Mitsuba2 渲染 XML2.2 Scene 场景的 XML 文件格式2.2.1 chair.npy to…

哪种电容笔更好用?2024精选推荐五款黄金畅销平替电容笔!

在当今信息化高速发展的时代&#xff0c;电容笔已成为众多电子设备用户不可或缺的重要配件。无论是专业的绘画创作者&#xff0c;还是日常学习、办公的人群&#xff0c;都对电容笔有着广泛的需求。可是市面上的品牌很多&#xff0c;到底哪种电容笔更好用呢&#xff1f;大家别担…

基于TCP协议的网络通信

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

【Windows 同时安装 MySQL5 和 MySQL8 - 详细图文教程】

卸载 MySQL 参考文章&#xff1a; 完美解决Mysql彻底删除并重装_怎么找到mysql并卸载-CSDN博客使用命令卸载mysql_卸载mysql服务命令-CSDN博客 先管理员方式打开 cmd &#xff0c;切换到 MySQL 安装目录的 bin 文件夹下&#xff0c;执行如下命令&#xff0c;删除 MySQL 服务mys…

基于DPU的OpenStack裸金属服务快速部署及存储解决方案

1 方案背景和挑战 Openstack作为开源云计算领域的领军项目&#xff0c;凭借其强大的功能、灵活的架构以及活跃的社区支持&#xff0c;在全球范围内得到了广泛的采用。通过Openstack&#xff0c;企业和云服务提供商可以更加高效地管理和利用计算资源、存储资源和网络资源&#…