Skip to content

fix(handleResize):修复响应式情况下 addEventListener 事件错误,导致菜单异常关闭#662

Merged
kanyxmo merged 1 commit into
mineadmin:masterfrom
westng:fix/handle-resize
Jul 24, 2025
Merged

fix(handleResize):修复响应式情况下 addEventListener 事件错误,导致菜单异常关闭#662
kanyxmo merged 1 commit into
mineadmin:masterfrom
westng:fix/handle-resize

Conversation

@westng

@westng westng commented Jul 23, 2025

Copy link
Copy Markdown
Member

问题描述

handleResize.ts 中未正确管理事件监听器,导致在窗口尺寸变化非移动端的情况下造成菜单无法正常显示。

修改说明

  • 引入 listenerEvent 缓存变量,防止多次绑定事件;
  • 在适当时机添加监听,避免逻辑异常;
  • 清晰分离监听逻辑,提升可维护性。

影响范围

  • 文件:/web/src/utils/handleResize.ts
  • 功能:响应式宽度小于1024情况下,菜单无法正常打开。
redpandacompress_.2025-07-23.124122.mp4

Summary by CodeRabbit

  • 优化
    • 改进了移动设备下子菜单的关闭逻辑,提升了在移动端和小屏设备上的使用体验,避免重复事件监听,提高性能和稳定性。

@coderabbitai

coderabbitai Bot commented Jul 23, 2025

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

本次变更在 handleResize.ts 中引入了 WeakMap 用于管理每个元素的 mousemove 事件监听器,确保每个元素仅有一个监听器。新增 isMobileDevice 函数用于判断是否为移动设备,并调整了监听器的添加逻辑,仅在移动设备且窗口宽度小于 1024px 时添加监听器。

Changes

文件/分组 变更摘要
web/src/utils/handleResize.ts 新增 isMobileDevice 函数;引入 listenerMap 追踪监听器;调整监听器的添加与移除逻辑,仅在移动端宽度小于 1024px 时生效

Estimated code review effort

2 (~12 分钟)

Suggested labels

bug, size:S

Poem

🐇
代码如风轻轻吹,
监听器悄悄藏身回,
移动设备识别明,
窗口宽度细细推。
弱映射守护无重叠,
小改动,体验飞!

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or PR title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. bug labels Jul 23, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
web/src/utils/handleResize.ts (2)

28-30: 考虑更可靠的移动设备检测方法

当前的用户代理检测方法可以满足基本需求,但用户代理字符串可能被修改或伪造。考虑以下更可靠的替代方案:

function isMobileDevice() {
-  return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent)
+  // 结合多种检测方法提高准确性
+  const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0
+  const isUserAgentMobile = /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent)
+  return isTouchDevice && isUserAgentMobile
}

35-53: 事件监听器管理逻辑正确,但需注意响应式引用的使用

这个实现很好地解决了多重事件监听器绑定的问题:

  1. 正确地移除之前的监听器防止重复绑定
  2. 仅在移动设备且宽度小于1024px时添加监听器
  3. 使用WeakMap进行内存管理

不过需要注意一个潜在问题:isOutside.value 在监听器函数中的使用是正确的,因为它在运行时访问响应式引用的当前值。

建议添加一些防御性检查:

  if (isMobileDevice() && window.innerWidth < 1024) {
    const listenerEvent = () => {
+     if (!targetEl || !settingStore) return
      if (settingStore.getMobileSubmenuState() && isOutside.value) {
        settingStore.setMobileSubmenuState(false)
      }
    }

    document.addEventListener('mousemove', listenerEvent)
    listenerMap.set(targetEl, listenerEvent)
  }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c57753e and 80eed3c.

📒 Files selected for processing (1)
  • web/src/utils/handleResize.ts (2 hunks)
🔇 Additional comments (1)
web/src/utils/handleResize.ts (1)

13-13: 优秀的设计选择!

使用 WeakMap 来管理事件监听器是一个很好的解决方案,它可以防止内存泄漏并确保每个元素只有一个监听器实例。WeakMap 会在元素被垃圾回收时自动清理相关的监听器引用。

@people-sea
people-sea requested a review from Copilot July 23, 2025 05:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes an issue with responsive event listener management in the mobile menu system. The problem was that addEventListener events were not properly managed, causing menus to close unexpectedly when the window was resized on non-mobile devices.

  • Introduces a listenerMap WeakMap to cache event listeners and prevent duplicate bindings
  • Adds proper listener cleanup logic to remove existing listeners before adding new ones
  • Separates mobile device detection logic and improves the conditions for when listeners should be active

Comment thread web/src/utils/handleResize.ts
Comment thread web/src/utils/handleResize.ts
import { useMouseInElement, useResizeObserver } from '@vueuse/core'
import type { Ref } from 'vue'

const listenerMap = new WeakMap<HTMLElement, (e: MouseEvent) => void>()

Copilot AI Jul 23, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The global WeakMap could lead to memory leaks if not properly cleaned up. Consider moving this inside the function scope or ensuring proper cleanup when components are destroyed.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

使用这个方法的组件就挂载一次 不存在泄露的

@people-sea

Copy link
Copy Markdown
Member

晚点我跑一下看看

@zds-s
zds-s requested a review from kanyxmo July 23, 2025 11:17

@people-sea people-sea left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅LGTM

import { useMouseInElement, useResizeObserver } from '@vueuse/core'
import type { Ref } from 'vue'

const listenerMap = new WeakMap<HTMLElement, (e: MouseEvent) => void>()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

使用这个方法的组件就挂载一次 不存在泄露的

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jul 23, 2025
@kanyxmo
kanyxmo merged commit 6882046 into mineadmin:master Jul 24, 2025
19 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants