18 KiB
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 # 导出
功能系统识别
通过分析代码,可以识别出以下核心系统:
-
基础绘制系统 (Rendering System)
- 波形轨道渲染(网格、坐标轴、波形线)
- SVG 基础图形绘制
- D3 图表渲染
-
缩放系统 (Zoom System)
- 缩放行为管理
- 变换状态管理
- 独立/共享缩放模式
-
交互系统 (Interaction System)
- 鼠标事件处理
- 悬浮检测
- 十字线显示
- 工具栏和模式切换
-
标注系统 (Annotation System)
- 标注管理(创建、编辑、删除)
- 图形管理(垂直线、时间区间)
- 标注渲染
- 编辑器
-
数据系统 (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- 基础类型定义export interface DisplaySeries { ... } export interface TrackLayout { ... } export interface WaveformPoint { ... } -
core/constants.ts- 常量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- 数据类型export type WaveformData = ... export type WaveformDisplayMode = ... export interface WaveformSeries { ... } -
data/normalize.ts- 数据规范化export function normalizeWaveformData(data: WaveformData): WaveformSeries[] export function normalizeWaveformSeries(data: WaveformData): DisplaySeries[] -
data/layout.ts- 轨道布局计算export function computeTrackLayouts( series: DisplaySeries[], displayMode: WaveformDisplayMode, innerWidth: number, innerHeight: number, ... ): TrackLayout[]
来源:
waveform.ts→data/types.ts+data/normalize.tsWaveformChart.vue中的trackLayouts计算逻辑 →data/layout.ts
3. Rendering System (基础绘制系统)
职责:渲染波形轨道、网格、坐标轴、波形线
文件:
rendering/WaveformTrack.vue- 波形轨道组件(已存在)rendering/types.ts- 渲染相关类型export interface RenderingProps { ... } export interface AxisConfig { ... }
可选优化:
rendering/Grid.vue- 独立网格组件rendering/Axis.vue- 独立坐标轴组件
来源:
WaveformTrack.vue→rendering/WaveformTrack.vue
4. Zoom System (缩放系统)
职责:管理缩放行为、变换状态
文件:
-
zoom/useZoom.ts- 缩放组合式函数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- 缩放相关类型export interface ZoomOptions { ... } export interface ZoomState { ... }
来源:从 WaveformChart.vue 提取缩放相关逻辑
5. Interaction System (交互系统)
职责:处理用户交互(悬浮、点击、工具栏)
文件:
-
interaction/WaveformToolbar.vue- 工具栏(已存在) -
interaction/WaveformTooltip.vue- 悬浮提示(已存在) -
interaction/useInteraction.ts- 交互管理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- 悬浮逻辑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- 交互相关类型export type WaveformInteractionMode = 'zoom' | 'select' | 'annotation' | ... export interface InteractionOptions { ... } export interface HoverOptions { ... }
来源:
WaveformToolbar.vue→interaction/WaveformToolbar.vueWaveformTooltip.vue→interaction/WaveformTooltip.vueWaveformChart.vue中的交互逻辑 →interaction/useInteraction.ts+interaction/useHover.ts
6. Annotation System (标注系统)
职责:管理标注和图形(创建、编辑、删除、渲染)
文件:
-
annotation/WaveformAnnotationLayer.vue- 标注渲染层(已存在) -
annotation/WaveformEditor.vue- 标注编辑器(已存在) -
annotation/useAnnotation.ts- 标注管理逻辑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- 标注工具函数export function layoutAnnotationBox(...) { ... } export function resolveAnnotationStyle(...) { ... } export function resolveShapeStyle(...) { ... } export function normalizeRangeShape(...) { ... } -
annotation/types.ts- 标注相关类型export interface WaveformAnnotation { ... } export interface WaveformShape { ... } export interface RenderedAnnotation { ... } export interface RenderedShape { ... }
来源:
WaveformAnnotationLayer.vue→annotation/WaveformAnnotationLayer.vueWaveformEditor.vue→annotation/WaveformEditor.vuewaveform-markup.ts→annotation/markup.ts+annotation/types.tsWaveformChart.vue中的标注管理逻辑 →annotation/useAnnotation.ts
🔄 重构策略
阶段 1: 创建新目录结构(不破坏现有代码)
- 创建新的目录结构
- 复制现有文件到新位置
- 不修改任何逻辑
阶段 2: 提取共享类型和常量
- 创建
core/types.ts和core/constants.ts - 从各个文件中提取共享类型
- 更新导入路径
阶段 3: 重构数据系统
- 创建
data/目录 - 将
waveform.ts拆分为data/types.ts和data/normalize.ts - 从
WaveformChart.vue提取布局计算逻辑到data/layout.ts
阶段 4: 重构缩放系统
- 创建
zoom/useZoom.ts - 从
WaveformChart.vue提取缩放相关逻辑 - 在主组件中使用组合式函数
阶段 5: 重构交互系统
- 移动
WaveformToolbar.vue和WaveformTooltip.vue到interaction/ - 创建
interaction/useInteraction.ts和interaction/useHover.ts - 从
WaveformChart.vue提取交互逻辑
阶段 6: 重构标注系统
- 移动
WaveformAnnotationLayer.vue和WaveformEditor.vue到annotation/ - 将
waveform-markup.ts拆分为annotation/markup.ts和annotation/types.ts - 创建
annotation/useAnnotation.ts - 从
WaveformChart.vue提取标注管理逻辑
阶段 7: 重构渲染系统
- 移动
WaveformTrack.vue到rendering/ - 更新所有导入路径
阶段 8: 更新主组件和公共导出
- 简化
WaveformChart.vue,使用各系统的组合式函数 - 更新
components/index.ts导出 - 确保向后兼容
阶段 9: 测试和验证
- 运行所有测试
- 验证功能完整性
- 检查性能
📝 重构后的主组件结构
<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. 可测试性 ⭐⭐⭐⭐⭐
- 独立测试:每个系统可以独立测试
- 组合式函数:易于单元测试,不需要挂载组件
// 测试缩放系统
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 优化
- 合理使用动态导入
- 监控打包体积变化
🎯 实施建议
渐进式重构
推荐方案:不是一次性重构所有内容,而是:
-
先提取组合式函数(最小影响)
- 创建
zoom/useZoom.ts - 创建
interaction/useHover.ts - 创建
annotation/useAnnotation.ts - 在主组件中使用,不移动其他文件
- 创建
-
再按系统移动组件(中等影响)
- 移动
WaveformToolbar.vue到interaction/ - 移动
WaveformAnnotationLayer.vue到annotation/ - 更新导入路径
- 移动
-
最后重构类型定义(最大影响)
- 拆分
waveform.ts和waveform-markup.ts - 创建
core/types.ts - 更新所有类型导入
- 拆分
向后兼容
在 components/index.ts 保持原有导出:
// 新的导出路径
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 小时
🤔 用户决策点
-
是否采用组合式函数?
- ✅ 推荐:更易测试,逻辑复用
- ❌ 备选:保持当前结构,只移动文件
-
是否进一步拆分组件?
- 例如:将
WaveformTrack拆分为Grid+Axis+Line - ✅ 更细粒度,更灵活
- ❌ 可能过度工程化
- 例如:将
-
是否一次性重构?
- ✅ 推荐:渐进式重构,每个阶段测试
- ❌ 备选:一次性完成(风险较大)
-
公共导出路径?
- 选项 A:
import { WaveformChart } from '@/components'(保持现状) - 选项 B:
import { WaveformChart } from '@/components/WaveformChart.vue'(显式路径) - 选项 C:系统级导出
import { WaveformAnnotationLayer } from '@/components/annotation'
- 选项 A: