## 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 主题
**替代方案**:
- Recharts:React 生态,不适用
- Victory:包体积更大,学习曲线陡峭
- uCharts:功能较简单,扩展性不足
### 2. 组件封装策略:包装器 vs 直接使用
**决策**:创建通用包装器组件 `BaseChart.vue`
**理由**:
- 统一主题配置(颜色、字体、动画)
- 统一响应式处理(resize observer)
- 统一错误边界和加载状态
- 减少重复代码(4 个组件共享配置)
**实现**:
```vue
```
### 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.vue(5 个图表,最复杂)
**阶段 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()` 导出 PNG,ECharts 支持 SVG 导出。如果需要矢量图导出,需额外集成 `chartjs-plugin-export`。
2. **是否保留图表动画?**
移动端用户可能更关注首屏加载速度。可考虑通过 `prefers-reduced-motion` 媒体查询禁用动画。
3. **是否需要国际化(i18n)?**
Chart.js 的日期格式化依赖 `date-fns` 或 `dayjs`。项目已使用 `dayjs`,可直接集成。