什么?LocalStorage 也能被监听?为什么我试了却不行?

745 阅读2分钟

我们都知道,localStorage 是前端开发中常用的浏览器本地存储方案之一。然而你是否遇到过这样的情况:

网上说 localStorage 可以被监听,但我尝试了之后却没有任何效果?

本文将从源码出发,带你彻底理解 localStorage 的监听机制,并揭秘背后的行为逻辑。


## 事件监听的基本方式

我们先来看一段简单的代码:

```js
window.addEventListener('storage', (event) => {
  console.log('Storage changed:', event);
});

运行这段代码后,监听器貌似什么也没有触发?为什么?


localStorage 的监听机制:跨页面才会触发

关键点在于:

storage 事件 只有在其他页面(同源)对 localStorage 进行修改时,当前页面才会触发事件!

这意味着你在当前页面执行以下代码:

localStorage.setItem('key', 'value');

不会触发当前页面上的 storage 事件的。

但如果你打开了两个同源页面 A.htmlB.html

  • B.html 中执行 localStorage.setItem(...)
  • 此时 A.html 中的监听函数会被触发!

官方文档如何说?

MDN StorageEvent 可以得知:

  • 事件只会在 其他页面改变 localStorage 时触发
  • 同一个页面内的操作不会触发

监听 localStorage 的正确方式

如果你的目标是监听 本页面的 localStorage 改变,你需要自己封装监听机制。

自定义封装方案

比如重写 setItem

(function () {
  const originalSetItem = localStorage.setItem;
  localStorage.setItem = function (key, value) {
    const event = new Event('localstorage-change');
    event.key = key;
    event.newValue = value;
    window.dispatchEvent(event);
    originalSetItem.apply(this, arguments);
  };
})();

然后监听:

window.addEventListener('localstorage-change', (e) => {
  console.log('Key changed:', e.key);
});

原理揭秘:源码中的事件派发

浏览器的 localStorage 事件是如何派发的?

Chromium 源码分析

我们来看 Chromium 的 DOMStorage 实现中一个关键函数:

void StorageArea::DispatchStorageEvent(...) {
  if (SameOrigin && NotCurrentPage) {
    // 触发 storage 事件
  }
}

可见浏览器明确判断了:

  • 当前页面是否是修改者
  • 若是当前页面,不触发事件

为什么要设计成这样?

这是为了避免循环事件触发。设想以下场景:

  • 页面 A 设置 localStorage
  • 触发事件回调,又设置 localStorage
  • 又触发事件,进入死循环...

为防止这种问题,浏览器选择只在「非当前页面」中触发事件。


如何验证?

你可以打开两个同源页面,分别执行如下操作来验证:

  1. 页面 A 中:
window.addEventListener('storage', (e) => {
  console.log('页面 A 收到事件:', e);
});
  1. 页面 B 中:
localStorage.setItem('demo', '123');

你会在页面 A 的控制台中看到触发的 storage 事件。


结语

总结一下:

  • localStoragestorage 事件只能在 其他窗口或标签页触发
  • 如果你想监听 本页面localStorage 变化,需要手动封装逻辑
  • 浏览器这样设计,是为了避免事件循环带来的性能与逻辑问题