mirror of
https://github.com/liubinfighter/csv-lite.git
synced 2026-07-22 05:43:52 +00:00
87 lines
No EOL
3.3 KiB
Markdown
87 lines
No EOL
3.3 KiB
Markdown
# CSV Lite 插件右键菜单问题排查与重构全记录
|
||
|
||
## 背景
|
||
|
||
在开发 CSV Lite 插件的过程中,遇到了表格右键菜单(context menu)在 UI/UX 上表现异常、事件处理混乱、菜单关闭不及时等一系列问题。为提升用户体验和代码可维护性,我们对菜单功能进行了彻底重构。本文详细记录了整个排查、修复与重构的过程。
|
||
|
||
---
|
||
|
||
## 1. 问题现象
|
||
|
||
- 右键菜单弹出后,**点击菜单项第一次不会消失,第二次才消失**。
|
||
- 菜单项 hover 效果偶尔失效。
|
||
- 菜单关闭后高亮未能及时清除。
|
||
- 控制台报错 `addClass/removeClass is not a function` 或 `tableEl.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. 参考代码片段
|
||
|
||
```ts
|
||
// 绑定菜单的正确方式
|
||
this.headerContextMenuCleanup = setupHeaderContextMenu(
|
||
this.tableEl,
|
||
{
|
||
selectRow: (rowIndex) => this.highlightManager.selectRow(rowIndex),
|
||
// ... 其他回调 ...
|
||
}
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
本次重构极大提升了插件的健壮性和用户体验,也为后续功能扩展打下了坚实基础。 |