Files
booking_admin/doc/座位布局性能优化说明.md
杨志 8e308a75f6
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
修改布局,修复BUG
2025-12-08 11:49:09 +08:00

4.2 KiB
Raw Blame History

座位布局性能优化说明

问题描述

当座位布局节点数量很大时(例如 50x50 = 2500 个单元格),会出现明显的卡顿现象。主要原因是:

  1. 大量 DOM 节点:双重循环渲染所有单元格,创建大量 DOM 元素
  2. 重复计算:每次渲染时,getCellClassgetCellTextshouldRenderCell 等函数都会被调用,且内部包含循环遍历
  3. 查找性能:使用 Array.find() 查找单元格,时间复杂度为 O(n)
  4. 事件处理:鼠标移动事件触发频繁,没有有效节流

优化方案

1. 使用 Map 缓存单元格查找

优化前:

const getCell = (col: number, row: number) => {
  return cells.value.find((c) => c.col === col && c.row === row);
};

优化后:

// 单元格查找缓存 Map: key = "col,row", value = cell
const cellMapCache = ref<Map<string, ClassroomApi.ClassroomLayoutCell>>(new Map());

const getCell = (col: number, row: number) => {
  const key = `${col},${row}`;
  return cellMapCache.value.get(key);
};

效果: 查找时间复杂度从 O(n) 降低到 O(1)

2. 缓存合并单元格映射关系

优化前:

// 每次都要遍历所有单元格检查是否被合并
for (const otherCell of cells.value) {
  // ... 检查逻辑
}

优化后:

// 合并单元格主单元格映射: key = "col,row" (被合并的单元格), value = "col,row" (主单元格)
const mergeMasterMapCache = ref<Map<string, string>>(new Map());

// 使用缓存检查
const key = `${col},${row}`;
if (mergeMasterMapCache.value.has(key)) {
  return 'cell cell-merged';
}

效果: 避免了每次渲染时的 O(n²) 循环嵌套

3. 使用 shallowRef 优化响应式性能

优化前:

const cells = ref<ClassroomApi.ClassroomLayoutCell[]>([]);

优化后:

const cells = shallowRef<ClassroomApi.ClassroomLayoutCell[]>([]);

效果: 减少深度响应式监听,提升性能

4. 使用 requestAnimationFrame 优化鼠标移动事件

优化前:

mouseEnterTimer = window.setTimeout(() => {
  // 填充逻辑
}, 16);

优化后:

rafId = requestAnimationFrame(() => {
  // 填充逻辑
});

效果: 与浏览器渲染周期同步,更流畅

5. 使用 v-memo 指令缓存单元格渲染

优化后:

<div
  v-if="shouldRenderCell(col - 1, row - 1)"
  v-memo="[getCell(col - 1, row - 1)?.type, getCell(col - 1, row - 1)?.status, ...]"
  :class="getCellClass(col - 1, row - 1)"
  ...
>

效果: 只有当依赖项变化时才重新渲染,减少不必要的 DOM 更新

6. 统一更新缓存机制

在所有修改单元格的操作后,统一调用 updateCellMapCache() 更新缓存:

  • initLayout() - 初始化布局
  • loadLayout() - 加载布局
  • addColumnLeft/Right() - 添加列
  • addRowTop/Bottom() - 添加行
  • mergeCells() - 合并单元格
  • unmergeCell() - 取消合并
  • handleUndo() - 撤销操作
  • handleCellMouseUp() - 鼠标释放

性能提升

经过优化后,预期性能提升:

  1. 单元格查找:从 O(n) 降低到 O(1),查找速度提升 100-1000 倍(取决于单元格数量)
  2. 合并检查:从 O(n²) 降低到 O(1),检查速度提升 10000 倍以上
  3. 渲染性能:使用 v-memo 后,只有变化的单元格才会重新渲染
  4. 鼠标交互:使用 requestAnimationFrame 后,交互更流畅,帧率更稳定

注意事项

  1. 缓存一致性:所有修改单元格的操作都必须调用 updateCellMapCache() 保持缓存同步
  2. 内存占用Map 缓存会占用额外内存,但对于性能提升是值得的
  3. 兼容性v-memo 是 Vue 3.2+ 的特性,确保项目使用 Vue 3.2 或更高版本

后续优化建议

如果性能仍然不够理想,可以考虑:

  1. 虚拟滚动:只渲染可见区域的单元格(对 Grid 布局实现较复杂)
  2. Canvas 渲染:使用 Canvas 替代 DOM 渲染(需要重写交互逻辑)
  3. Web Worker:将复杂计算移到 Web Worker 中
  4. 分块渲染:将大布局分成多个块,按需加载