4、数据源对接:API接口设计、WebSocket实时数据、静态数据模拟

数据源对接,说白了就是大屏的「粮食供应」问题。

你想想看,大屏做得再炫酷,如果数据拿不到、拿不准、拿不快,那跟一张静态海报有什么区别?我个人习惯把数据源分成三类来对待:API接口、WebSocket实时流、静态模拟数据。这三者各有各的用场,也各有各的坑。

4.1 API接口设计:别让后端猜你要什么

我在项目中遇到过最头疼的事,就是前端和后端对接口字段的理解不一致。后端返回一个 status,前端以为是 0/1,后端给的是 true/false。这种问题在开发阶段还好,上了生产环境就是事故。

所以,我建议在项目启动时,先和后端敲定一份接口规范。别嫌麻烦,这是省时间的捷径。

核心原则:接口设计要「前端友好」。后端不要返回嵌套过深的数据结构,尽量扁平化。

举个例子,一个典型的大屏数据接口,我通常会这样设计:

// 请求示例
GET /api/dashboard/overview

// 响应示例
{
  "code": 200,
  "message": "success",
  "data": {
    "totalUsers": 12890,
    "activeUsers": 3456,
    "todayOrders": 892,
    "revenue": 456789.00,
    "trendData": [
      { "date": "2024-01-01", "value": 1200 },
      { "date": "2024-01-02", "value": 1350 }
    ],
    "alerts": [
      { "id": 1, "level": "warning", "message": "服务器负载过高" }
    ]
  }
}

嗯,这里要注意几个细节:

  • 统一返回格式:code、message、data 三层结构,前端统一处理错误
  • 时间格式:统一用 ISO 8601 格式,别搞什么时间戳和字符串混用
  • 空值处理:数组为空返回 [],不要返回 null
  • 分页参数:统一用 pagepageSize,别用 offsetlimit

我的小技巧:在项目初期,我会用 Postman 或 Swagger 先模拟一套接口文档。前端先按这个文档开发,后端再按这个文档实现。这样两边可以并行推进,互不阻塞。

4.2 WebSocket实时数据:别让大屏变成「死屏」

大屏最怕什么?最怕数据不动。

你想想看,一个监控大屏,如果数据每隔 30 秒才刷新一次,那跟看 PPT 有什么区别?实时数据,必须用 WebSocket。

我曾经在一个智慧城市项目中,一开始用了轮询(Polling),每 5 秒请求一次接口。结果呢?服务器压力大,前端页面卡顿,用户体验极差。后来改成 WebSocket,问题迎刃而解。

避坑指南:我曾经在 WebSocket 连接上吃过亏——没有做断线重连。结果网络波动一下,大屏就「死」了,数据一动不动,直到运维手动刷新页面。后来我学乖了,一定要加上重连机制。

一个健壮的 WebSocket 连接,我通常会这样封装:

class RealtimeSocket {
  constructor(url) {
    this.url = url;
    this.ws = null;
    this.reconnectInterval = 3000; // 3秒重连
    this.init();
  }

  init() {
    this.ws = new WebSocket(this.url);

    this.ws.onopen = () => {
      console.log('WebSocket 连接成功');
      // 可以发送订阅消息
      this.ws.send(JSON.stringify({ type: 'subscribe', channel: 'dashboard' }));
    };

    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      // 触发自定义事件,让组件订阅
      window.dispatchEvent(new CustomEvent('realtime-data', { detail: data }));
    };

    this.ws.onclose = () => {
      console.log('WebSocket 断开,准备重连...');
      setTimeout(() => this.init(), this.reconnectInterval);
    };

    this.ws.onerror = (err) => {
      console.error('WebSocket 错误:', err);
      this.ws.close();
    };
  }

  send(data) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data));
    }
  }
}

// 使用示例
const socket = new RealtimeSocket('wss://api.example.com/ws');

这里有几个关键点:

  • 心跳检测:每隔 30 秒发一个 ping,确保连接存活
  • 消息格式:统一用 JSON,包含 type 和 payload 字段
  • 订阅机制:不要一股脑把所有数据都推过来,按频道订阅
  • 数据缓存:断线重连后,先拉一次全量数据,再接收增量更新

性能建议:WebSocket 消息不要过于频繁。我见过有人每秒推送 100 次数据,结果浏览器渲染跟不上,页面直接卡死。一般来说,每秒 10-20 次更新就足够了。如果数据变化太快,可以在后端做降频处理。

4.3 静态数据模拟:开发阶段的「救命稻草」

说实话,大屏开发最怕等后端接口。

后端说「接口明天好」,结果拖了一周。前端干等着?不行。所以,我习惯在项目一开始就准备一套静态模拟数据。

静态数据模拟,说白了就是「造假数据」。但造假也要造得专业。

我通常会建一个 mock 文件夹,里面放各种数据文件:

// mock/dashboard.js
export const mockOverview = {
  totalUsers: 12890,
  activeUsers: 3456,
  todayOrders: 892,
  revenue: 456789.00,
  trendData: Array.from({ length: 30 }, (_, i) => ({
    date: `2024-01-${String(i + 1).padStart(2, '0')}`,
    value: Math.floor(Math.random() * 2000) + 500
  }))
};

// 模拟实时数据
export const mockRealtimeData = () => ({
  timestamp: Date.now(),
  cpuUsage: (Math.random() * 100).toFixed(2),
  memoryUsage: (Math.random() * 100).toFixed(2),
  networkTraffic: Math.floor(Math.random() * 10000)
});

我的经验:模拟数据要尽量「真实」。比如用户量不能忽高忽低,要有趋势变化。我通常会写一个简单的数据生成器,模拟出「早上低、下午高、晚上回落」的规律。这样开发出来的大屏,视觉效果才自然。

静态数据模拟的好处很明显:

  • 前端独立开发:不用等后端,自己就能跑起来
  • 边界测试:可以模拟各种极端情况,比如数据为空、数据量超大、数据格式错误
  • 演示方便:给客户演示时,不用依赖真实环境,本地就能跑

注意:模拟数据不能「太完美」。我曾经犯过一个错误——模拟数据全是整数、全是正数、全是规律变化。结果上线后,真实数据一进来,图表显示异常。所以,模拟数据里一定要包含一些「脏数据」:比如 null、负数、超大值、空字符串。这样才能提前发现前端代码的健壮性问题。

4.4 三种数据源的切换策略

在实际项目中,这三种数据源不是互斥的,而是可以灵活切换的。

我通常会在项目中设计一个数据源切换层:

// dataSource.js
const DATA_SOURCE = {
  MOCK: 'mock',
  API: 'api',
  WEBSOCKET: 'websocket'
};

// 当前数据源,可以通过环境变量或配置切换
const currentSource = process.env.VUE_APP_DATA_SOURCE || DATA_SOURCE.MOCK;

export async function fetchOverview() {
  switch (currentSource) {
    case DATA_SOURCE.MOCK:
      return mockOverview;
    case DATA_SOURCE.API:
      return axios.get('/api/dashboard/overview').then(res => res.data);
    case DATA_SOURCE.WEBSOCKET:
      // 通过 WebSocket 获取,这里返回一个 Promise
      return new Promise((resolve) => {
        const handler = (event) => {
          resolve(event.detail);
          window.removeEventListener('realtime-data', handler);
        };
        window.addEventListener('realtime-data', handler);
      });
  }
}

这样做的好处是:

  • 开发阶段:用 mock 数据,快速迭代
  • 联调阶段:切换到 API,对接真实接口
  • 生产环境:切换到 WebSocket,接收实时数据

而且,切换只需要改一个配置变量,不用改任何业务代码。

总结一下:数据源对接这件事,看似简单,但做不好就是大坑。API 接口要规范,WebSocket 要健壮,静态数据要真实。三者灵活切换,才能让大屏开发事半功倍。

嗯,下一章我们会聊到「数据可视化组件选型与封装」,到时候再细说怎么把这些数据漂亮地展示出来。