我们都知道,localStorage
是前端开发中常用的浏览器本地存储方案之一。然而你是否遇到过这样的情况:
网上说 localStorage 可以被监听,但我尝试了之后却没有任何效果?
本文将从源码出发,带你彻底理解 localStorage 的监听机制,并揭秘背后的行为逻辑。
## 事件监听的基本方式
我们先来看一段简单的代码:
```js
window.addEventListener('storage', (event) => {
console.log('Storage changed:', event);
});
运行这段代码后,监听器貌似什么也没有触发?为什么?
localStorage 的监听机制:跨页面才会触发
关键点在于:
storage
事件 只有在其他页面(同源)对localStorage
进行修改时,当前页面才会触发事件!
这意味着你在当前页面执行以下代码:
localStorage.setItem('key', 'value');
是 不会触发当前页面上的 storage
事件的。
但如果你打开了两个同源页面 A.html
和 B.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
- 又触发事件,进入死循环...
为防止这种问题,浏览器选择只在「非当前页面」中触发事件。
如何验证?
你可以打开两个同源页面,分别执行如下操作来验证:
- 页面 A 中:
window.addEventListener('storage', (e) => {
console.log('页面 A 收到事件:', e);
});
- 页面 B 中:
localStorage.setItem('demo', '123');
你会在页面 A 的控制台中看到触发的 storage
事件。
结语
总结一下:
localStorage
的storage
事件只能在 其他窗口或标签页触发- 如果你想监听 本页面的
localStorage
变化,需要手动封装逻辑 - 浏览器这样设计,是为了避免事件循环带来的性能与逻辑问题