- Migrated 4 components from ECharts to Chart.js: * MonthlyExpenseCard.vue (折线图) * DailyTrendChart.vue (双系列折线图) * ExpenseCategoryCard.vue (环形图) * BudgetChartAnalysis.vue (仪表盘 + 多种图表) - Removed all ECharts imports and environment variable switches - Unified all charts to use BaseChart.vue component - Build verified: pnpm build success ✓ - No echarts imports remaining ✓ Refs: openspec/changes/migrate-remaining-echarts-to-chartjs
6.1 KiB
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 个组件共享配置)
实现:
<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 小时)
- 安装依赖:
pnpm add chart.js vue-chartjs - 创建
BaseChart.vue和主题配置文件 - 创建 Gauge 插件(仪表盘专用)
阶段 2:组件迁移(3-4 小时)
- MonthlyExpenseCard.vue(柱状图,最简单)
- ExpenseCategoryCard.vue(饼图)
- DailyTrendChart.vue(折线图)
- BudgetChartAnalysis.vue(5 个图表,最复杂)
阶段 3:验证与清理(1 小时)
- 功能测试(所有图表交互)
- 视觉回归测试(截图对比)
- 移除 ECharts 依赖
- 构建产物分析(验证体积优化)
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
部署策略
- Feature Flag:使用环境变量
VITE_USE_CHARTJS=true控制新旧图表切换 - 灰度发布:先在测试环境验证 1 周,观察性能指标(Lighthouse 分数、FCP)
- 回滚方案:保留 ECharts 代码至少 1 个版本,通过 Git revert 快速回滚
验证指标
- 包体积:
pnpm build后dist/大小减少 > 500KB - 性能:Lighthouse Performance 分数提升 > 5 分
- 功能:所有图表的交互测试通过(手动测试清单见
docs/chart-migration-checklist.md)
回滚触发条件
- 任何核心图表功能失效(如仪表盘无法显示)
- Lighthouse 性能分数下降 > 3 分
- 用户报告严重视觉 Bug(如图表错位、颜色错误)
Open Questions
-
是否需要支持图表导出功能?
Chart.js 支持toBase64Image()导出 PNG,ECharts 支持 SVG 导出。如果需要矢量图导出,需额外集成chartjs-plugin-export。 -
是否保留图表动画?
移动端用户可能更关注首屏加载速度。可考虑通过prefers-reduced-motion媒体查询禁用动画。 -
是否需要国际化(i18n)?
Chart.js 的日期格式化依赖date-fns或dayjs。项目已使用dayjs,可直接集成。