先说结果:没完美解决

问题描述

项目框架:vue3 + vue router 4.6 + vite + 其他的
浏览器:Edge 44.0.3719.115(2026.2.6 检测的最新版本)

今天项目迭代时,突然出现的情况,浏览器无法最小化,表现为最小化时会成功,但是立马又弹出来

疑点如下:

  1. 只有当前激活的标签页访问的是该项目时才会复现(本地运行或部署的远程环境都可以),切换到其他标签页就没事
  2. 其他浏览器没事,Edge 的问题
  3. 代码回退到前几天没出问题的时候,仍能复现

初步分析结果:

  1. Edge 可能发生过更新(最近没有动过配置)
  2. 其他页面没问题,应该是项目中代码触发了问题

定位问题

垃圾 Edge 没办法动,只能去代码中找找是哪里触发了。

既然是页面最小化时触发的问题,那先监听一下浏览器事件,编写了一个脚本去查看注册了那些事件,并在触发的时候打印下回调函数内容:

function monitorExistingEventListeners() {
  console.log('🎯 监控已有事件监听器...')

  if (typeof getEventListeners !== 'function') {
    console.error('❌ API getEventListeners 不可用')
    return
  }

  // 监控的目标元素
  const targets = [window, document]

  targets.forEach(target => {
    const listeners = getEventListeners(target)
    const eventTypes = Object.keys(listeners)

    console.log(`📋 ${target === window ? 'window' : 'document'} 事件:`)

    eventTypes.forEach(eventType => {
      const eventListeners = listeners[eventType]

      console.log(`   📍 ${eventType}: ${eventListeners.length} 个监听器`)

      eventListeners.forEach((listenerInfo, index) => {
        // 替换监听器
        const originalListener = listenerInfo.listener

        const wrappedListener = function (...args) {
          console.log(`📢 [${eventType}] 触发`)
          console.log(`       函数: ${listenerInfo.listener.toString()}.`)

          // 执行原始监听器
          return originalListener.apply(this, args)
        }

        // 移除原监听器,添加包装后的监听器
        target.removeEventListener(eventType, originalListener, listenerInfo.useCapture)
        target.addEventListener(eventType, wrappedListener, listenerInfo.useCapture)
      })
    })
  })

  console.log('✅ 已有事件监听器监控已启动')
}

// 立即执行
monitorExistingEventListeners()

页面加载完后,在粘贴到控制台执行,结果如下:

在这里插入图片描述
操作一下最小化,控制台日志如下:

在这里插入图片描述

移除这个 visibilitychange 事件后,最小化就正常了:

const listeners = getEventListeners(document).visibilitychange
if (listeners && listeners.length > 0) {
  listeners.forEach(listener => {
    document.removeEventListener('visibilitychange', listener.listener)
  })
  console.log('已移除 visibilitychange 监听器')
}

接着全局搜 beforeUnloadListener 方法,看看是哪里调用的,原来是 vue router 的代码:

在这里插入图片描述
问题应该是出现在 history.replaceState 这里,尝试重写它的逻辑:

// 这块代码写在引入 vue router 之后
const originalReplaceState = window.history.replaceState

window.history.replaceState = function (state, title, url) {
  console.log('🚨 history.replaceState 被调用,已拦截')
  // originalReplaceState.call(this, {}, '', '')
  originalReplaceState.call(this, null, '', '')
}

测试结果:当 replaceState 接收的第一个参数不为空时就会复现,即修改历史记录会复现。

模拟问题复现

为了进一步确认问题仅来源于 history.replaceState 方法,编写一个 demo 去复现:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Edge最小化问题测试</title>
  </head>
  <body>
    <script>
      function beforeUnloadListener() {
        if (document.visibilityState === 'hidden') {
          // window.history.pushState({}, '')
          window.history.replaceState({}, '')
        }
      }

      // 页面加载后立即绑定监听器
      window.addEventListener('load', function () {
        // 绑定 visibilitychange 事件监听器
        document.addEventListener('visibilitychange', beforeUnloadListener)

        console.log('监听器已绑定')
      })
    </script>
  </body>
</html>

一开始直接用本地文件访问的方式(浏览器直接打开文件:File 协议)打开,并没有复现。

后来反复对比了 vue router 代码没有发现什么差异,想到可能是运行方式不同,于是用 Live Server 起了个服务再去访问,终于复现成功了。

尝试了下 pushState 也能复现。

临时解决

至此事情已经搞清楚了:垃圾 Edge 在处理页面的历史状态更新时,可能触发了内部的 “页面激活” 机制。

查不到 Edge 有没有配置能够修改这个,Edge 动不了。

beforeUnloadListener 是 vue router 一直都有的重要逻辑,回退版本也存在。

为了不影响客户正常访问,暂时只能先把 replaceState 重写,等 Edge 修正了这个问题再把功能还原:

window.history.replaceState = function () {
  console.log('🚨 history.replaceState 被调用,已拦截')
  return
}

!!垃圾 Edge

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐