## 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`,可直接集成。