first commit
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
李启源
2026-07-20 10:21:30 +08:00
commit ea832229da
75 changed files with 16864 additions and 0 deletions

View File

@@ -0,0 +1,645 @@
# WaveformChart 按系统拆分重构计划
## 🎯 目标
将现有的组件按功能系统重新组织,每个系统有独立的目录,提高代码的可维护性和可理解性。
## 📊 当前结构分析
### 当前组件列表
```
src/components/
├── WaveformChart.vue # 主容器组件 (~1143 行)
├── WaveformToolbar.vue # 工具栏 (174 行)
├── WaveformEditor.vue # 编辑器 (149 行)
├── WaveformTooltip.vue # 悬浮提示 (111 行)
├── WaveformTrack.vue # 波形轨道 (317 行)
├── WaveformAnnotationLayer.vue # 标注层 (281 行)
├── waveform.ts # 类型定义
├── waveform-markup.ts # 标注相关类型和工具
└── index.ts # 导出
```
### 功能系统识别
通过分析代码,可以识别出以下核心系统:
1. **基础绘制系统 (Rendering System)**
- 波形轨道渲染(网格、坐标轴、波形线)
- SVG 基础图形绘制
- D3 图表渲染
2. **缩放系统 (Zoom System)**
- 缩放行为管理
- 变换状态管理
- 独立/共享缩放模式
3. **交互系统 (Interaction System)**
- 鼠标事件处理
- 悬浮检测
- 十字线显示
- 工具栏和模式切换
4. **标注系统 (Annotation System)**
- 标注管理(创建、编辑、删除)
- 图形管理(垂直线、时间区间)
- 标注渲染
- 编辑器
5. **数据系统 (Data System)**
- 数据规范化
- 类型定义
- 轨道布局计算
## 🏗️ 目标目录结构
```
src/components/
├── WaveformChart.vue # 主容器(协调各系统)
├── index.ts # 公共导出
├── core/ # 核心系统
│ ├── types.ts # 共享类型定义
│ ├── constants.ts # 常量(颜色、尺寸等)
│ └── index.ts
├── data/ # 数据系统
│ ├── types.ts # 数据相关类型
│ ├── normalize.ts # 数据规范化
│ ├── layout.ts # 轨道布局计算
│ └── index.ts
├── rendering/ # 基础绘制系统
│ ├── WaveformTrack.vue # 波形轨道组件
│ ├── Grid.vue # 网格组件(可选)
│ ├── Axis.vue # 坐标轴组件(可选)
│ ├── types.ts # 渲染相关类型
│ └── index.ts
├── zoom/ # 缩放系统
│ ├── useZoom.ts # 缩放组合式函数
│ ├── types.ts # 缩放相关类型
│ └── index.ts
├── interaction/ # 交互系统
│ ├── WaveformToolbar.vue # 工具栏
│ ├── WaveformTooltip.vue # 悬浮提示
│ ├── useInteraction.ts # 交互组合式函数
│ ├── useHover.ts # 悬浮逻辑
│ ├── types.ts # 交互相关类型
│ └── index.ts
└── annotation/ # 标注系统
├── WaveformAnnotationLayer.vue # 标注渲染层
├── WaveformEditor.vue # 标注编辑器
├── useAnnotation.ts # 标注管理逻辑
├── markup.ts # 标注工具函数
├── types.ts # 标注相关类型
└── index.ts
```
## 📦 系统划分详情
### 1. Core System (核心系统)
**职责**:提供共享的类型、常量和工具
**文件**
- `core/types.ts` - 基础类型定义
```typescript
export interface DisplaySeries { ... }
export interface TrackLayout { ... }
export interface WaveformPoint { ... }
```
- `core/constants.ts` - 常量
```typescript
export const channelColors = [...]
export const margin = { top: 18, right: 24, bottom: 52, left: 64 }
export const minimumHeight = 180
```
**来源**:从 `WaveformChart.vue` 和 `waveform.ts` 提取
---
### 2. Data System (数据系统)
**职责**:数据规范化、轨道布局计算
**文件**
- `data/types.ts` - 数据类型
```typescript
export type WaveformData = ...
export type WaveformDisplayMode = ...
export interface WaveformSeries { ... }
```
- `data/normalize.ts` - 数据规范化
```typescript
export function normalizeWaveformData(data: WaveformData): WaveformSeries[]
export function normalizeWaveformSeries(data: WaveformData): DisplaySeries[]
```
- `data/layout.ts` - 轨道布局计算
```typescript
export function computeTrackLayouts(
series: DisplaySeries[],
displayMode: WaveformDisplayMode,
innerWidth: number,
innerHeight: number,
...
): TrackLayout[]
```
**来源**
- `waveform.ts` → `data/types.ts` + `data/normalize.ts`
- `WaveformChart.vue` 中的 `trackLayouts` 计算逻辑 → `data/layout.ts`
---
### 3. Rendering System (基础绘制系统)
**职责**:渲染波形轨道、网格、坐标轴、波形线
**文件**
- `rendering/WaveformTrack.vue` - 波形轨道组件(已存在)
- `rendering/types.ts` - 渲染相关类型
```typescript
export interface RenderingProps { ... }
export interface AxisConfig { ... }
```
**可选优化**
- `rendering/Grid.vue` - 独立网格组件
- `rendering/Axis.vue` - 独立坐标轴组件
**来源**
- `WaveformTrack.vue` → `rendering/WaveformTrack.vue`
---
### 4. Zoom System (缩放系统)
**职责**:管理缩放行为、变换状态
**文件**
- `zoom/useZoom.ts` - 缩放组合式函数
```typescript
export function useZoom(options: ZoomOptions) {
const sharedTransform = shallowRef<ZoomTransform>(zoomIdentity)
const independentTransforms = shallowRef<ZoomTransform[]>([])
function configureZoom() { ... }
function resetViewport() { ... }
function handleSharedZoom(event: D3ZoomEvent) { ... }
function handleIndependentZoom(event: D3ZoomEvent, trackIndex: number) { ... }
return {
sharedTransform,
independentTransforms,
configureZoom,
resetViewport,
handleSharedZoom,
handleIndependentZoom,
}
}
```
- `zoom/types.ts` - 缩放相关类型
```typescript
export interface ZoomOptions { ... }
export interface ZoomState { ... }
```
**来源**:从 `WaveformChart.vue` 提取缩放相关逻辑
---
### 5. Interaction System (交互系统)
**职责**:处理用户交互(悬浮、点击、工具栏)
**文件**
- `interaction/WaveformToolbar.vue` - 工具栏(已存在)
- `interaction/WaveformTooltip.vue` - 悬浮提示(已存在)
- `interaction/useInteraction.ts` - 交互管理
```typescript
export function useInteraction(options: InteractionOptions) {
const interactionMode = ref<WaveformInteractionMode>('zoom')
function setInteractionMode(mode: WaveformInteractionMode) { ... }
function handleOverlayClick(event: PointerEvent, trackIndex?: number) { ... }
return {
interactionMode,
setInteractionMode,
handleOverlayClick,
}
}
```
- `interaction/useHover.ts` - 悬浮逻辑
```typescript
export function useHover(options: HoverOptions) {
const hoveredSeriesPoints = ref<HoveredSeriesPoint[]>([])
const hoveredTrackIndex = ref<number | null>(null)
const hoverPosition = ref({ x: 0, y: 0 })
function handlePointerMove(event: PointerEvent, trackIndex?: number) { ... }
function clearHover() { ... }
function nearestPoint(series: DisplaySeries, xValue: number) { ... }
return {
hoveredSeriesPoints,
hoveredTrackIndex,
hoverPosition,
handlePointerMove,
clearHover,
nearestPoint,
}
}
```
- `interaction/types.ts` - 交互相关类型
```typescript
export type WaveformInteractionMode = 'zoom' | 'select' | 'annotation' | ...
export interface InteractionOptions { ... }
export interface HoverOptions { ... }
```
**来源**
- `WaveformToolbar.vue` → `interaction/WaveformToolbar.vue`
- `WaveformTooltip.vue` → `interaction/WaveformTooltip.vue`
- `WaveformChart.vue` 中的交互逻辑 → `interaction/useInteraction.ts` + `interaction/useHover.ts`
---
### 6. Annotation System (标注系统)
**职责**:管理标注和图形(创建、编辑、删除、渲染)
**文件**
- `annotation/WaveformAnnotationLayer.vue` - 标注渲染层(已存在)
- `annotation/WaveformEditor.vue` - 标注编辑器(已存在)
- `annotation/useAnnotation.ts` - 标注管理逻辑
```typescript
export function useAnnotation(options: AnnotationOptions) {
const selection = ref<WaveformMarkupSelection>(null)
const editingDraft = ref<EditingDraft | null>(null)
const rangeDraft = ref<RangeDraft | null>(null)
function createAnnotation(point: WaveformPoint, seriesId: string) { ... }
function editAnnotation(id: string) { ... }
function deleteAnnotation(id: string) { ... }
function selectMarkup(kind: 'annotation' | 'shape', id: string) { ... }
return {
selection,
editingDraft,
rangeDraft,
createAnnotation,
editAnnotation,
deleteAnnotation,
selectMarkup,
}
}
```
- `annotation/markup.ts` - 标注工具函数
```typescript
export function layoutAnnotationBox(...) { ... }
export function resolveAnnotationStyle(...) { ... }
export function resolveShapeStyle(...) { ... }
export function normalizeRangeShape(...) { ... }
```
- `annotation/types.ts` - 标注相关类型
```typescript
export interface WaveformAnnotation { ... }
export interface WaveformShape { ... }
export interface RenderedAnnotation { ... }
export interface RenderedShape { ... }
```
**来源**
- `WaveformAnnotationLayer.vue` → `annotation/WaveformAnnotationLayer.vue`
- `WaveformEditor.vue` → `annotation/WaveformEditor.vue`
- `waveform-markup.ts` → `annotation/markup.ts` + `annotation/types.ts`
- `WaveformChart.vue` 中的标注管理逻辑 → `annotation/useAnnotation.ts`
---
## 🔄 重构策略
### 阶段 1: 创建新目录结构(不破坏现有代码)
1. 创建新的目录结构
2. 复制现有文件到新位置
3. 不修改任何逻辑
### 阶段 2: 提取共享类型和常量
1. 创建 `core/types.ts` 和 `core/constants.ts`
2. 从各个文件中提取共享类型
3. 更新导入路径
### 阶段 3: 重构数据系统
1. 创建 `data/` 目录
2. 将 `waveform.ts` 拆分为 `data/types.ts` 和 `data/normalize.ts`
3. 从 `WaveformChart.vue` 提取布局计算逻辑到 `data/layout.ts`
### 阶段 4: 重构缩放系统
1. 创建 `zoom/useZoom.ts`
2. 从 `WaveformChart.vue` 提取缩放相关逻辑
3. 在主组件中使用组合式函数
### 阶段 5: 重构交互系统
1. 移动 `WaveformToolbar.vue` 和 `WaveformTooltip.vue` 到 `interaction/`
2. 创建 `interaction/useInteraction.ts` 和 `interaction/useHover.ts`
3. 从 `WaveformChart.vue` 提取交互逻辑
### 阶段 6: 重构标注系统
1. 移动 `WaveformAnnotationLayer.vue` 和 `WaveformEditor.vue` 到 `annotation/`
2. 将 `waveform-markup.ts` 拆分为 `annotation/markup.ts` 和 `annotation/types.ts`
3. 创建 `annotation/useAnnotation.ts`
4. 从 `WaveformChart.vue` 提取标注管理逻辑
### 阶段 7: 重构渲染系统
1. 移动 `WaveformTrack.vue` 到 `rendering/`
2. 更新所有导入路径
### 阶段 8: 更新主组件和公共导出
1. 简化 `WaveformChart.vue`,使用各系统的组合式函数
2. 更新 `components/index.ts` 导出
3. 确保向后兼容
### 阶段 9: 测试和验证
1. 运行所有测试
2. 验证功能完整性
3. 检查性能
---
## 📝 重构后的主组件结构
```vue
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
// 数据系统
import { normalizeWaveformSeries, computeTrackLayouts } from './data'
import type { WaveformData, DisplayMode } from './data/types'
// 缩放系统
import { useZoom } from './zoom'
// 交互系统
import { useInteraction, useHover } from './interaction'
import WaveformToolbar from './interaction/WaveformToolbar.vue'
import WaveformTooltip from './interaction/WaveformTooltip.vue'
// 标注系统
import { useAnnotation } from './annotation'
import WaveformAnnotationLayer from './annotation/WaveformAnnotationLayer.vue'
import WaveformEditor from './annotation/WaveformEditor.vue'
// 渲染系统
import WaveformTrack from './rendering/WaveformTrack.vue'
// 核心
import { channelColors, margin } from './core/constants'
const props = defineProps<{
data: WaveformData
displayMode?: DisplayMode
// ...
}>()
// 数据处理
const chartSeries = computed(() => normalizeWaveformSeries(props.data))
const trackLayouts = computed(() => computeTrackLayouts(chartSeries.value, ...))
// 缩放系统
const {
sharedTransform,
independentTransforms,
configureZoom,
resetViewport,
} = useZoom({ /* options */ })
// 交互系统
const { interactionMode, setInteractionMode } = useInteraction({ /* options */ })
const { hoveredSeriesPoints, handlePointerMove, clearHover } = useHover({ /* options */ })
// 标注系统
const {
selection,
editingDraft,
createAnnotation,
editAnnotation,
deleteAnnotation,
} = useAnnotation({ /* options */ })
// 生命周期和监听
watch(() => props.data, resetViewport)
</script>
<template>
<div class="waveform-chart">
<svg>
<!-- 轨道渲染 -->
<WaveformTrack
v-for="track in trackLayouts"
:key="track.index"
:track="track"
@pointer-move="handlePointerMove($event, track.index)"
/>
<!-- 标注层 -->
<WaveformAnnotationLayer
:annotations="renderedAnnotations"
:shapes="renderedShapes"
/>
</svg>
<!-- 工具栏 -->
<WaveformToolbar
:interaction-mode="interactionMode"
@update:interaction-mode="setInteractionMode"
/>
<!-- 编辑器 -->
<WaveformEditor
v-if="editingDraft"
:draft="editingDraft"
/>
<!-- Tooltip -->
<WaveformTooltip
:visible="hoveredSeriesPoints.length > 0"
:series-points="hoveredSeriesPoints"
/>
</div>
</template>
```
---
## ✅ 收益
### 1. 可维护性 ⭐⭐⭐⭐⭐
- **按功能定位**:需要修改缩放功能?直接到 `zoom/` 目录
- **职责清晰**:每个系统有独立的目录和文件
- **减少耦合**:系统之间通过明确的接口通信
### 2. 可测试性 ⭐⭐⭐⭐⭐
- **独立测试**:每个系统可以独立测试
- **组合式函数**:易于单元测试,不需要挂载组件
```typescript
// 测试缩放系统
describe('useZoom', () => {
it('should handle zoom transform', () => {
const { sharedTransform, handleSharedZoom } = useZoom(options)
// 测试逻辑
})
})
```
### 3. 可扩展性 ⭐⭐⭐⭐⭐
- **添加新功能**:在对应系统目录下添加
- **替换实现**:可以替换整个系统而不影响其他部分
- **插件化**:各系统可以作为独立插件使用
### 4. 可理解性 ⭐⭐⭐⭐⭐
- **目录即文档**:从目录结构就能理解系统功能
- **代码组织**:相关代码放在一起,容易理解上下文
- **新人友好**:新开发者可以快速定位到相关代码
---
## ⚠️ 风险和注意事项
### 1. 大规模重构风险
**风险**:移动文件可能导致测试失败、功能损坏
**缓解措施**
- 分阶段重构,每个阶段运行测试
- 保持向后兼容
- 使用 Git 分支,可以随时回滚
### 2. 导入路径变更
**风险**:大量文件的导入路径需要更新
**缓解措施**
- 使用 IDE 的重构功能
- 在新目录的 `index.ts` 中保持导出一致
- 逐步迁移,保留旧路径的重导出
### 3. 类型依赖复杂
**风险**:类型定义分散后可能产生循环依赖
**缓解措施**
- 明确类型依赖关系
- 共享类型放在 `core/types.ts`
- 避免系统之间直接依赖类型
### 4. 性能影响
**风险**:拆分可能影响打包体积和加载性能
**缓解措施**
- 使用 Tree-shaking 优化
- 合理使用动态导入
- 监控打包体积变化
---
## 🎯 实施建议
### 渐进式重构
**推荐方案**:不是一次性重构所有内容,而是:
1. **先提取组合式函数**(最小影响)
- 创建 `zoom/useZoom.ts`
- 创建 `interaction/useHover.ts`
- 创建 `annotation/useAnnotation.ts`
- 在主组件中使用,不移动其他文件
2. **再按系统移动组件**(中等影响)
- 移动 `WaveformToolbar.vue` 到 `interaction/`
- 移动 `WaveformAnnotationLayer.vue` 到 `annotation/`
- 更新导入路径
3. **最后重构类型定义**(最大影响)
- 拆分 `waveform.ts` 和 `waveform-markup.ts`
- 创建 `core/types.ts`
- 更新所有类型导入
### 向后兼容
在 `components/index.ts` 保持原有导出:
```typescript
// 新的导出路径
export { default as WaveformChart } from './WaveformChart.vue'
export { default as WaveformToolbar } from './interaction/WaveformToolbar.vue'
export { default as WaveformTooltip } from './interaction/WaveformTooltip.vue'
// 类型导出
export type * from './core/types'
export type * from './data/types'
export type * from './annotation/types'
```
---
## 📅 预估时间
- **阶段 1-2**(目录结构 + 类型提取2-3 小时
- **阶段 3-4**(数据系统 + 缩放系统3-4 小时
- **阶段 5-6**(交互系统 + 标注系统3-4 小时
- **阶段 7-8**(渲染系统 + 主组件更新2-3 小时
- **阶段 9**测试和验证1-2 小时
**总计**11-16 小时
---
## 🤔 用户决策点
1. **是否采用组合式函数?**
- ✅ 推荐:更易测试,逻辑复用
- ❌ 备选:保持当前结构,只移动文件
2. **是否进一步拆分组件?**
- 例如:将 `WaveformTrack` 拆分为 `Grid` + `Axis` + `Line`
- ✅ 更细粒度,更灵活
- ❌ 可能过度工程化
3. **是否一次性重构?**
- ✅ 推荐:渐进式重构,每个阶段测试
- ❌ 备选:一次性完成(风险较大)
4. **公共导出路径?**
- 选项 A`import { WaveformChart } from '@/components'`(保持现状)
- 选项 B`import { WaveformChart } from '@/components/WaveformChart.vue'`(显式路径)
- 选项 C系统级导出 `import { WaveformAnnotationLayer } from '@/components/annotation'`

266
.claude/plan.md Normal file
View File

@@ -0,0 +1,266 @@
# WaveformChart 组件拆分计划
## 目标
`WaveformChart.vue`1743 行)拆分为更小的、职责单一的子组件:
1. **WaveformTooltip.vue** - 悬浮提示组件
2. **WaveformTrack.vue** - 单个波形轨道组件
3. **WaveformAnnotationLayer.vue** - 标注层组件
## 当前代码分析
### 主组件职责(过多)
- ✅ 数据管理和状态协调
- ✅ 缩放和交互事件处理
- 🔴 渲染波形轨道(网格、轴、波形线、十字线)
- 🔴 渲染标注和图形
- 🔴 渲染悬浮提示
- ✅ 工具栏管理(已拆分)
- ✅ 编辑器管理(已拆分)
### 模板结构1055-1743 行)
```vue
<div class="waveform-chart">
<svg>
<g transform="translate(margin)">
<!-- 1. 轨道循环100+ 包含网格标签波形线十字线 -->
<g v-for="track in trackLayouts">...</g>
<!-- 2. 标注和图形层135 -->
<g class="waveform-chart__markup-layer">
<g v-for="shape in renderedShapes">...</g>
<g v-for="annotation in renderedAnnotations">...</g>
<g v-for="preview in renderedRangePreview">...</g>
</g>
</g>
</svg>
<!-- 3. Tooltip15 -->
<div v-if="showTooltip && hoveredPoint" class="waveform-chart__tooltip">...</div>
<!-- 4. 已拆分组件 -->
<WaveformToolbar />
<WaveformEditor />
</div>
```
## 拆分策略
### 组件 1: WaveformTooltip.vue (~80 行)
**职责**:显示鼠标悬浮时的数据点信息
**Props**
```typescript
interface Props {
visible: boolean
position: { x: number, y: number }
timeUnit: 's' | 'ms'
hoveredPoint: WaveformPoint | null
seriesPoints: Array<{
trackIndex: number
name: string
color: string
unit?: string
point: WaveformPoint
}>
}
```
**提取内容**
- 模板1463-1478 行16 行)
- 样式1682-1742 行61 行)
- 计算属性:`tooltipStyle`471-477 行)
---
### 组件 2: WaveformTrack.vue (~300 行)
**职责**渲染单个波形轨道网格、坐标轴、波形线、十字线、overlay
**Props**
```typescript
interface Props {
track: TrackLayout
clipPathId: string
margin: { top: number, right: number, bottom: number, left: number }
innerWidth: number
showTooltip: boolean
zoomable: boolean
displayMode: WaveformDisplayMode
activeInteractionMode: WaveformInteractionMode
frameNumber?: string | number
timeUnit: 's' | 'ms'
hoveredPoint?: HoveredSeriesPoint // 用于显示十字线
}
```
**Emits**
```typescript
interface Emits {
(e: 'pointer-move', event: PointerEvent): void
(e: 'pointer-leave'): void
(e: 'pointer-down', event: PointerEvent): void
(e: 'pointer-up', event: PointerEvent): void
(e: 'pointer-cancel', event: PointerEvent): void
(e: 'click', event: PointerEvent): void
}
```
**提取内容**
- 模板1099-1265 行166 行)
- 相关函数:
- `shouldShowYAxisLabel` (247-262)
- `resolveYAxisLabel` (239-241)
- `resolveFrameNumber` (804-810)
- `crosshairX` (794-797)
- `crosshairY` (799-802)
- `trackHoverPoint` (790-792)
- 样式:部分轨道相关样式
**注意事项**
- 轨道组件需要在父组件中接收 D3 渲染的坐标轴
- 或者在 `onMounted` 中自己调用 D3 渲染坐标轴
---
### 组件 3: WaveformAnnotationLayer.vue (~250 行)
**职责**渲染所有标注和图形annotations + shapes
**Props**
```typescript
interface Props {
renderedAnnotations: RenderedAnnotation[]
renderedShapes: RenderedShape[]
renderedRangePreview: RenderedShape[]
activeInteractionMode: WaveformInteractionMode
selection: WaveformMarkupSelection
innerWidth: number
clipPathId: string
}
```
**Emits**
```typescript
interface Emits {
(e: 'select-markup', kind: 'annotation' | 'shape', id: string): void
(e: 'edit-markup', kind: 'annotation' | 'shape', id: string): void
}
```
**提取内容**
- 模板1285-1419 行135 行)
- 相关函数:
- `isSelected` (491-493)
- `safeDomId` (503-505)
- `arrowMarkerId` (507-509)
- `annotationBoxStyle` (511-518)
- `shapeLabelWidth` (520-522)
- `shapeLabelX` (524-531)
- `shapeLabelStyle` (533-538)
- 样式标注相关样式1581-1660 行)
---
## 实施步骤
### 阶段 1: 创建 WaveformTooltip.vue ✅
1. 创建组件文件
2. 提取模板和样式
3. 实现计算属性 `tooltipStyle`
4. 更新主组件使用新组件
### 阶段 2: 创建 WaveformAnnotationLayer.vue ✅
1. 创建组件文件
2. 提取标注层模板
3. 提取相关工具函数
4. 提取样式
5. 更新主组件
### 阶段 3: 创建 WaveformTrack.vue ✅
1. 创建组件文件
2. 提取轨道渲染逻辑
3. 处理 D3 坐标轴渲染(使用 `ref` + `onMounted`
4. 提取相关样式
5. 更新主组件
### 阶段 4: 验证和测试 ✅
1. 运行类型检查 `pnpm typecheck`
2. 运行代码规范检查 `pnpm lint`
3. 运行单元测试 `pnpm test`
4. 手动测试功能完整性
---
## 设计决策
### 1. 类型共享
`DisplaySeries`, `HoveredSeriesPoint`, `TrackLayout`, `RenderedAnnotation`, `RenderedShape` 等接口移到独立的类型文件中,供多个组件使用。
**创建** `src/components/waveform-chart-types.ts`
### 2. D3 渲染策略
对于 WaveformTrack 中的坐标轴渲染:
- **方案 A推荐**:在 Track 组件内部使用 `ref` + `onMounted` 调用 D3
- **方案 B**:父组件渲染后通知子组件
- **选择 A**:更符合组件封装原则
### 3. 事件冒泡
所有交互事件click, pointer-move 等)通过 emit 向上传递,保持主组件的事件协调职责。
### 4. 样式隔离
每个组件使用 `<style scoped>`,但共享的样式变量可以提取到 CSS 变量中。
---
## 预期收益
### 代码规模
- **主组件**1743 → ~1100 行(-37%
- **新组件**
- WaveformTooltip: ~80 行
- WaveformAnnotationLayer: ~250 行
- WaveformTrack: ~300 行
### 可维护性
- 每个组件职责清晰
- 修改轨道渲染不影响标注层
- Tooltip 可独立测试和复用
### 可测试性
- 每个子组件可独立单元测试
- 减少主组件测试的复杂度
---
## 风险和注意事项
### 1. D3 上下文问题
坐标轴渲染依赖 D3 操作 DOM需要确保 ref 正确传递和挂载时机。
**解决方案**:在 Track 组件中使用 `watch` 监听 `track` prop 变化,触发重新渲染。
### 2. 性能影响
拆分组件可能增加 Vue 的更新开销。
**解决方案**
- 使用 `shallowRef` 存储 D3 对象
- 对于大数组tracks, annotations使用稳定的 `:key`
- 如有性能问题,可使用 `v-memo` 指令
### 3. 向后兼容
确保 props 和 emits 接口不变。
**验证方法**:运行现有的 24 个单元测试。
---
## 实施时间估算
- 阶段 1Tooltip30 分钟
- 阶段 2AnnotationLayer1 小时
- 阶段 3Track1.5 小时
- 阶段 4测试30 分钟
**总计**:约 3.5 小时