Files
EmailBill/openspec/changes/archive/2026-02-17-migrate-to-chartjs/design.md
SunCheng c49f66757e
Some checks failed
Docker Build & Deploy / Build Docker Image (push) Waiting to run
Docker Build & Deploy / Deploy to Production (push) Has been cancelled
Docker Build & Deploy / Cleanup Dangling Images (push) Has been cancelled
Docker Build & Deploy / WeChat Notification (push) Has been cancelled
1
2026-02-18 21:16:45 +08:00

6.1 KiB
Raw Blame 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 个组件共享配置)

实现

<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.durationeasing 自定义

风险 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 builddist/ 大小减少 > 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-fnsdayjs。项目已使用 dayjs,可直接集成。