背景
开发谷歌插件的过程中,监听用户浏览器操作行为,vue3 项目使用hash路由,发现在 background 脚本中通过chrome.tabs.onUpdated和chrome.webNavigation.onHistoryStateUpdated事件监听时,点击切换菜单路由时都会触发一个链接跳转事件,导致监听事件重复。
这里说明一下为什么在chrome.tabs.onUpdated和chrome.webNavigation.onHistoryStateUpdated事件都写了那个链接跳转事件,是因为发现在某些网站上有时候chrome.tabs.onUpdated会触发,有时候chrome.webNavigation.onHistoryStateUpdated会单独触发,为了兼容处理各种情况,所以就在这两个事件里都处理了链接跳转事件,结果引发了文章开头描述的问题
思考过程
引发上述问题可能的原因包括:
- 事件触发条件重叠:当Hash变化时,chrome.tabs.onUpdated可能因为URL变化而触发,而chrome.webNavigation.onHistoryStateUpdated则因为历史状态改变而触发,两者同时发生。
- 事件监听器的重复注册:可能在插件的不同地方多次注册了相同的监听器,导致重复触发。
- 事件传播或页面加载阶段的问题:比如在页面加载的不同阶段,两个事件可能被多次触发。
接下来,我需要考虑如何解决这个问题。可能的解决方案包括:
- 合并事件监听:将两个事件的监听逻辑合并,避免重复处理。例如,将两个事件的回调函数统一到一个处理函数中,并添加防重机制。
- 条件判断过滤:在事件回调中添加条件判断,比如检查URL变化是否是Hash变化,或者是否是用户主动触发的路由跳转,从而决定是否执行逻辑。
- 使用防重或节流:在回调函数中使用防重(如setTimeout和clearTimeout)或节流来避免短时间内多次触发。
- 选择更精确的事件监听:如果Hash路由主要通过hashchange事件触发,可能只需要监听chrome.webNavigation.onHistoryStateUpdated,而chrome.tabs.onUpdated可能包含更多的触发场景,可以考虑移除其中一个监听。(PASS,原因看开头背景说明)
解决方案
1. 合并事件监听逻辑,避免重复处理
js
// 合并后的处理函数
function handleUrlChange(details) {
if (isHandling) return; // 防止重复触发
isHandling = true;
// 具体处理逻辑(如获取当前URL的Hash值,上报动作)
// 具体处理逻辑
// 延迟重置标志位,避免短时间内重复触发
setTimeout(() => {
isHandling = false;
}, 200); // 根据需求调整延迟时间
}
// 监听 chrome.tabs.onUpdated
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.url && changeInfo.url.includes('#')) {
handleUrlChange({ url: changeInfo.url });
}
});
// 监听 chrome.webNavigation.onHistoryStateUpdated
chrome.webNavigation.onHistoryStateUpdated.addListener((details) => {
handleUrlChange(details);
});
2. 过滤非必要的触发条件
在回调函数中添加条件判断,比如 仅在Hash值变化时执行逻辑,根据自己项目实际情况进行判断。
js
function handleUrlChange(details) {
const currentUrl = details.url || chrome.tabs.getSelected().url;
const newHash = new URL(currentUrl).hash;
// 获取上一次记录的Hash值(需维护一个变量)
if (lastHash === newHash) return; // 如果Hash未变化,直接返回
lastHash = newHash;
// 执行实际逻辑
console.log('Hash changed:', newHash);
}
关键注意事项
-
防重机制:
- 使用标志位(如
isHandling
)或节流函数(如setTimeout
)防止短时间内重复触发。
- 使用标志位(如
-
状态维护:
- 通过
lastHash
记录上一次的Hash值,避免重复处理相同Hash的事件。
- 通过