liubinfighter_csv-lite/docs/DEBUG_MENU_REFACTOR.md

3.3 KiB
Raw Permalink Blame History

CSV Lite 插件右键菜单问题排查与重构全记录

背景

在开发 CSV Lite 插件的过程中遇到了表格右键菜单context menu在 UI/UX 上表现异常、事件处理混乱、菜单关闭不及时等一系列问题。为提升用户体验和代码可维护性,我们对菜单功能进行了彻底重构。本文详细记录了整个排查、修复与重构的过程。


1. 问题现象

  • 右键菜单弹出后,点击菜单项第一次不会消失,第二次才消失
  • 菜单项 hover 效果偶尔失效。
  • 菜单关闭后高亮未能及时清除。
  • 控制台报错 addClass/removeClass is not a functiontableEl.addEventListener is not a function
  • 工具栏等 UI 元素偶尔消失。

2. 初步排查

2.1 事件监听与 DOM 结构

  • 检查菜单项点击事件,发现菜单关闭逻辑依赖于 addClass/removeClass,但项目中并未实现这两个方法。
  • 事件监听器绑定和解绑时机混乱,可能导致多次绑定或未解绑。

2.2 代码实现问题

  • 右键菜单相关代码分散UI 与数据操作耦合严重。
  • 菜单项点击后,closeMenu() 并未总是被及时调用。
  • 绑定菜单的时机不对,tableEl 可能还未渲染。

3. 修复与重构过程

3.1 替换 addClass/removeClass

  • 将所有 addClass/removeClass 替换为标准的 classList.add/classList.remove,保证样式切换生效。

3.2 菜单关闭逻辑梳理

  • 确保菜单项点击后立即关闭菜单,并清除高亮。
  • 保证 ESC/点击外部也能关闭菜单。

3.3 彻底重构菜单栏功能

  • 新增 MenuManager 类,专门负责菜单的创建、显示、关闭。
  • 菜单项通过参数传递,点击后自动关闭并调用回调。
  • 只在表格渲染后(即 refresh() 里)绑定右键菜单,确保 tableEl 已经是 <table> 元素。
  • UI/UX 逻辑与数据操作彻底解耦,接口兼容原有调用方式。

3.4 修正绑定时机

  • 移除 onOpen 里的菜单绑定,只在 refresh() 里绑定,避免 tableEl 未初始化时报错。

4. 最终方案

  • 右键菜单功能完全由 MenuManager 管理UI 逻辑清晰、无副作用。
  • 菜单项包括:行/列的插入、删除、交换,所有操作均通过回调传递。
  • 菜单关闭时自动清除高亮,支持 ESC/点击外部关闭。
  • 只在表格渲染后绑定菜单,避免 DOM 未就绪导致的报错。
  • 工具栏、表格等 UI 元素全部恢复正常。

5. 经验总结

  • UI 组件应与数据操作解耦,便于维护和扩展。
  • 事件监听器的绑定和解绑时机必须严格把控,避免内存泄漏和多次绑定。
  • DOM 操作要确保目标元素已渲染,否则易出现 xxx is not a function 报错。
  • 标准化样式操作,优先使用 classList,避免自定义扩展污染全局。
  • 重构时优先考虑单一职责原则,如本次将菜单管理独立为 MenuManager

6. 参考代码片段

// 绑定菜单的正确方式
this.headerContextMenuCleanup = setupHeaderContextMenu(
    this.tableEl,
    {
        selectRow: (rowIndex) => this.highlightManager.selectRow(rowIndex),
        // ... 其他回调 ...
    }
);

本次重构极大提升了插件的健壮性和用户体验,也为后续功能扩展打下了坚实基础。