2. 看板架构基础
工程分析平台的看板模块,其架构设计直接决定了数据刷新效率与后续性能优化的空间。本章从三个核心维度展开:前后端分离架构、单向数据流模型以及组件化设计原则。理解这些基础,是后续实现增量更新、虚拟滚动与缓存策略的前提。
2.1 前后端分离架构
现代看板系统普遍采用前后端分离架构,将视图渲染与数据逻辑解耦。前端负责看板布局、图表渲染与用户交互;后端专注数据聚合、清洗与推送。这种分离带来了明确的职责边界与独立的扩展能力。
| 层次 | 职责 | 典型技术栈 | 性能关注点 |
|---|---|---|---|
| 前端层 | 看板布局、图表渲染、交互响应、本地缓存 | React / Vue + ECharts / D3.js | DOM 更新频率、虚拟滚动、组件卸载与复用 |
| 网关层 | 请求路由、鉴权、限流、协议转换 | Nginx / Kong / Envoy | 连接池复用、请求合并、超时控制 |
| 后端层 | 数据聚合、业务逻辑、缓存策略、推送服务 | Spring Boot / Go + Redis / WebSocket | 查询优化、缓存命中率、推送频率控制 |
| 数据层 | 时序数据存储、元数据管理、历史归档 | InfluxDB / ClickHouse / PostgreSQL | 分区裁剪、预聚合、TTL 策略 |
架构要点:前后端通过 RESTful API 或 WebSocket 通信。对于实时看板,推荐使用 WebSocket 建立长连接,避免 HTTP 轮询带来的头部开销与连接建立延迟。
2.2 单向数据流模型
看板的数据流遵循 单向数据流 原则:数据从后端流向状态管理容器,再流向视图组件;用户交互产生的动作反向触发数据请求,形成闭环。这种模型保证了数据变更的可预测性与可追踪性。
数据流核心阶段
- 数据采集与聚合:后端从数据层拉取原始指标,执行聚合计算(如平均值、百分位、同比环比),生成看板所需的结构化数据集。
- 推送与分发:通过 WebSocket 或 Server-Sent Events (SSE) 将增量数据推送到前端;前端网关层可对高频推送做 节流(throttle) 或 防抖(debounce) 处理。
- 状态管理:前端状态容器(如 Redux、Pinia、Zustand)接收数据,执行浅比较或不可变更新,仅触发受影响的组件重渲染。
- 视图渲染:组件从状态中读取切片数据,驱动图表库(ECharts / D3.js)更新 SVG 或 Canvas 画布。
性能关键点:单向数据流中,状态更新的粒度直接影响渲染性能。建议将看板拆分为多个独立的状态切片(如“温度指标切片”、“压力指标切片”),避免单个状态树过大导致全量 diff。
2.3 组件化设计原则
看板由多个可视化卡片(Widget)组成,每个卡片应视为一个独立组件。组件化设计遵循以下原则,以支撑高性能刷新与灵活布局。
核心原则
- 单一职责:每个组件只负责一种数据类型的展示(如“实时温度曲线”组件不混入“历史压力表格”逻辑),便于独立测试与性能优化。
- 可复用性:通过 props / slots 暴露配置项(如刷新间隔、数据源 ID、图表类型),使同一组件可在不同看板页面复用。
- 生命周期管理:组件挂载时建立 WebSocket 订阅或轮询定时器;卸载时主动清理连接与定时器,防止内存泄漏与无效请求。
- 虚拟化支持:当看板包含大量卡片时,仅渲染视口内的组件(虚拟滚动),未进入视口的组件暂停数据订阅。
组件层级示例
| 层级 | 组件名称 | 职责 | 性能关注点 |
|---|---|---|---|
| 容器层 | DashboardContainer | 管理看板布局、全局刷新策略、组件注册 | 布局重排开销、组件懒加载 |
| 卡片层 | MetricCard | 封装单个指标卡片的标题、数值、趋势箭头 | 数值动画性能、条件渲染 |
| 图表层 | TimeSeriesChart | 渲染时序曲线图,支持增量追加数据点 | Canvas 复用、数据点裁剪 |
| 数据层 | useMetricData | 自定义 Hook,封装数据订阅、缓存、重试逻辑 | 订阅去重、缓存失效策略 |
常见陷阱:避免在组件内部直接发起 HTTP 请求。应将数据获取逻辑提升到容器层或自定义 Hook 中,通过 props 向下传递数据。这样当刷新策略变更时,只需修改容器层,无需改动每个卡片组件。
组件通信模式
看板组件间推荐使用 发布-订阅 模式或 状态管理库 进行通信,避免多层 props 透传。例如,当用户切换全局时间范围时,容器层发布事件,各卡片组件订阅后重新请求数据。
// 示例:使用发布-订阅模式管理看板刷新事件
class RefreshBus {
constructor() {
this.subscribers = new Map();
}
subscribe(widgetId, callback) {
if (!this.subscribers.has(widgetId)) {
this.subscribers.set(widgetId, []);
}
this.subscribers.get(widgetId).push(callback);
}
publish(widgetId, payload) {
const callbacks = this.subscribers.get(widgetId);
if (callbacks) {
callbacks.forEach(cb => cb(payload));
}
}
unsubscribe(widgetId) {
this.subscribers.delete(widgetId);
}
}
// 使用示例
const bus = new RefreshBus();
bus.subscribe('temp-chart', (data) => updateChart(data));
bus.publish('temp-chart', { timestamp: Date.now(), value: 85.3 });
设计建议:为每个看板组件定义 刷新策略接口(如
refreshInterval、retryCount、cacheTTL),通过配置文件或后端下发的元数据驱动,避免硬编码刷新逻辑。