Files
EmailBill/openspec/changes/migrate-to-chartjs/design.md

166 lines
6.1 KiB
Markdown
Raw Normal View History

## Context
EmailBill 是一个移动端预算追踪应用,使用 Vue 3 + Vite + Vant UI 构建。当前使用 ECharts 6.0 作为图表库,涵盖了以下图表类型:
- **仪表盘Gauge**:预算健康度展示
- **折线图Line**:日趋势、燃尽图
- **柱状图Bar**:月度对比、方差分析
- **饼图Pie**:分类统计
**约束条件**
- 必须保持所有图表的现有功能和交互逻辑不变
- 必须适配移动端触控交互tap, pinch, swipe
- 必须兼容 Vant UI 的主题系统(支持暗色模式)
- 必须保持现有的响应式布局
## Goals / Non-Goals
**Goals:**
- 使用 Chart.js 替换 ECharts减少 bundle 体积约 600KB
- 提升图表渲染性能和动画流畅度
- 统一图表配色方案,使用更现代化的视觉风格
- 提供通用的 Chart.js 封装组件,便于后续扩展
**Non-Goals:**
- 不改变现有业务逻辑和数据流
- 不添加新的图表类型或功能
- 不重构非图表相关的组件
- 不改变图表的数据格式(仅转换配置项)
## Decisions
### 1. 图表库选择Chart.js vs Recharts vs Victory
**决策**:使用 **Chart.js 4.x + vue-chartjs 5.x**
**理由**
- **包体积**Chart.js (~200KB) << ECharts (~800KB)
- **Vue 集成**vue-chartjs 提供了开箱即用的 Composition API 支持
- **移动端优化**原生支持触控手势HammerJS 集成
- **社区成熟度**GitHub 66k+ stars文档完善
- **主题定制**:支持 CSS 变量集成,易于适配 Vant 主题
**替代方案**
- RechartsReact 生态,不适用
- Victory包体积更大学习曲线陡峭
- uCharts功能较简单扩展性不足
### 2. 组件封装策略:包装器 vs 直接使用
**决策**:创建通用包装器组件 `BaseChart.vue`
**理由**
- 统一主题配置(颜色、字体、动画)
- 统一响应式处理resize observer
- 统一错误边界和加载状态
- 减少重复代码4 个组件共享配置)
**实现**
```vue
<template>
<div class="base-chart" ref="chartContainer">
<component
:is="chartComponent"
:data="chartData"
:options="mergedOptions"
/>
</div>
</template>
<script setup>
import { Line, Bar, Pie, Doughnut } from 'vue-chartjs'
// 统一主题配置
const baseOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { /* Vant 主题配色 */ }
}
}
</script>
```
### 3. 图表类型映射
| ECharts 类型 | Chart.js 类型 | 组件 |
|-------------|--------------|------|
| gauge (仪表盘) | doughnut + 自定义插件 | BudgetChartAnalysis.vue |
| line (折线图) | line | DailyTrendChart.vue, Burndown |
| bar (柱状图) | bar | MonthlyExpenseCard.vue, Variance |
| pie (饼图) | pie | ExpenseCategoryCard.vue |
**特殊处理**
- **仪表盘**Chart.js 无原生 gauge使用 Doughnut + 自定义 centerText 插件模拟
- **燃尽图**:使用双 Y 轴配置(理想线 + 实际线)
### 4. 迁移顺序
**阶段 1**基础设施1-2 小时)
1. 安装依赖:`pnpm add chart.js vue-chartjs`
2. 创建 `BaseChart.vue` 和主题配置文件
3. 创建 Gauge 插件(仪表盘专用)
**阶段 2**组件迁移3-4 小时)
1. MonthlyExpenseCard.vue柱状图最简单
2. ExpenseCategoryCard.vue饼图
3. DailyTrendChart.vue折线图
4. BudgetChartAnalysis.vue5 个图表,最复杂)
**阶段 3**验证与清理1 小时)
1. 功能测试(所有图表交互)
2. 视觉回归测试(截图对比)
3. 移除 ECharts 依赖
4. 构建产物分析(验证体积优化)
## Risks / Trade-offs
### 风险 1仪表盘实现复杂度
**[风险]** Chart.js 无原生 gauge 支持,需要自定义插件
**→ 缓解措施**:使用社区验证的 centerText 插件方案(参考 Chart.js Doughnut with center text预先实现并测试
### 风险 2动画效果差异
**[风险]** Chart.js 的默认动画可能与 ECharts 不一致,影响用户体验
**→ 缓解措施**:保留 ECharts 动画时长和缓动函数配置Chart.js 支持 `animation.duration``easing` 自定义
### 风险 3暗色模式适配
**[风险]** Vant 暗色模式下,图表颜色需要动态切换
**→ 缓解措施**:使用 CSS 变量(`var(--van-text-color)`Chart.js 配置支持响应式更新
### 风险 4性能回归
**[风险]** 大数据量场景下(如年度数据 365 个点),性能可能不如预期
**→ 缓解措施**
- 启用 Chart.js 的 `decimation` 插件(数据抽样)
- 使用 `parsing: false` 跳过数据解析
- 移动端限制数据点上限(最多 100 个)
### Trade-off功能丰富度 vs 包体积
**[取舍]** Chart.js 功能不如 ECharts 全面(如 3D 图表、地图)
**→ 项目影响**EmailBill 仅使用基础图表类型,不受影响;未来如需高级图表,可按需引入 ECharts 特定模块
## Migration Plan
### 部署策略
1. **Feature Flag**:使用环境变量 `VITE_USE_CHARTJS=true` 控制新旧图表切换
2. **灰度发布**:先在测试环境验证 1 周观察性能指标Lighthouse 分数、FCP
3. **回滚方案**:保留 ECharts 代码至少 1 个版本,通过 Git revert 快速回滚
### 验证指标
- **包体积**`pnpm build``dist/` 大小减少 > 500KB
- **性能**Lighthouse Performance 分数提升 > 5 分
- **功能**:所有图表的交互测试通过(手动测试清单见 `docs/chart-migration-checklist.md`
### 回滚触发条件
- 任何核心图表功能失效(如仪表盘无法显示)
- Lighthouse 性能分数下降 > 3 分
- 用户报告严重视觉 Bug如图表错位、颜色错误
## Open Questions
1. **是否需要支持图表导出功能?**
Chart.js 支持 `toBase64Image()` 导出 PNGECharts 支持 SVG 导出。如果需要矢量图导出,需额外集成 `chartjs-plugin-export`
2. **是否保留图表动画?**
移动端用户可能更关注首屏加载速度。可考虑通过 `prefers-reduced-motion` 媒体查询禁用动画。
3. **是否需要国际化i18n**
Chart.js 的日期格式化依赖 `date-fns``dayjs`。项目已使用 `dayjs`,可直接集成。