166 lines
6.1 KiB
Markdown
166 lines
6.1 KiB
Markdown
|
|
## 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
|
|||
|
|
<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.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`,可直接集成。
|