Bootstrap Table 实时数据更新:WebSocket 连接实现

Bootstrap Table 实时数据更新:WebSocket 连接实现

【免费下载链接】bootstrap-table wenzhixin/bootstrap-table: 是一个基于 Bootstrap 的表格插件,它没有使用数据库。适合用于数据展示,特别是对于需要使用 Bootstrap 和表格展示的场景。特点是表格插件、Bootstrap、无数据库。 【免费下载链接】bootstrap-table 项目地址: https://gitcode.com/gh_mirrors/bo/bootstrap-table

引言:数据可视化的实时困境与解决方案

在现代 Web 应用开发中,数据可视化的实时性已成为用户体验的关键指标。当你面对以下场景时:

  • 监控系统需要秒级更新设备状态
  • 交易平台需实时展示价格波动
  • 物流系统要动态呈现货物位置

传统的轮询机制(Polling)会导致资源浪费延迟问题,而基于 HTTP 长轮询(Long Polling)的方案又难以应对高频数据更新场景。Bootstrap Table 作为基于 Bootstrap 的轻量级表格插件,虽然原生未提供 WebSocket(WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议)支持,但通过其灵活的扩展机制和事件系统,我们可以构建高效的实时数据更新解决方案。

读完本文你将掌握:

  • WebSocket 与传统轮询的技术对比
  • Bootstrap Table 数据更新的核心 API
  • 从零构建 WebSocket 实时更新组件
  • 断线重连与性能优化策略
  • 完整实现代码与场景化示例

技术原理:为什么选择 WebSocket?

数据更新方案对比

特性轮询(Polling)长轮询(Long Polling)WebSocket
连接方式短连接,周期性请求长连接,服务器有数据时响应持久连接,双向通信
延迟高(取决于轮询间隔)中(服务器响应后需重新建立连接)低(毫秒级)
服务器负载高(大量无效请求)中(连接保持时间长)低(单连接多消息)
数据方向客户端请求-服务器响应客户端请求-服务器响应全双工(双向实时)
适用场景低频率更新(如日报表)中等频率更新(如通知)高频实时数据(如监控、交易)

Bootstrap Table 数据更新机制

Bootstrap Table 提供了灵活的 API 用于数据操作,核心方法包括:

// 加载数据(会替换现有数据)
$table.bootstrapTable('load', newData);

// 追加数据
$table.bootstrapTable('append', newData);

// 更新指定行数据
$table.bootstrapTable('updateRow', {
  index: rowIndex,
  row: updatedData
});

// 刷新表格(保留当前页码等状态)
$table.bootstrapTable('refresh');

这些方法为实现实时更新提供了基础,但需要结合 WebSocket 实现数据推送。

实现步骤:从零构建实时更新组件

1. 准备工作:环境与依赖

项目结构(基于 Bootstrap Table 扩展机制):

bootstrap-table/
├── src/
│   ├── extensions/
│   │   └── websocket/          # 新建 WebSocket 扩展目录
│   │       ├── bootstrap-table-websocket.js  # 核心逻辑
│   │       └── bootstrap-table-websocket.scss # 样式文件
└── site/
    └── docs/
        └── extensions/
            └── websocket.md    # 文档说明

前端依赖

  • Bootstrap Table 核心库
  • WebSocket API(现代浏览器原生支持)

2. 核心实现:WebSocket 扩展开发

创建扩展文件 src/extensions/websocket/bootstrap-table-websocket.js

/**
 * Bootstrap Table WebSocket Extension
 * Author: Your Name
 * License: MIT
 */

!function ($) {
  'use strict';

  var BootstrapTable = $.fn.bootstrapTable.Constructor;
  var _init = BootstrapTable.prototype.init;
  var _destroy = BootstrapTable.prototype.destroy;

  // 扩展默认选项
  $.extend($.fn.bootstrapTable.defaults, {
    // WebSocket 连接地址
    websocketUrl: null,
    // 自动连接
    websocketAutoConnect: true,
    // 数据处理函数
    websocketDataHandler: function (data) {
      // 默认处理:直接加载数据
      return data;
    },
    // 连接状态变化回调
    onWebSocketStatusChange: function (status) {
      // status: 'connecting', 'connected', 'disconnected', 'error'
      console.log('WebSocket status:', status);
    }
  });

  BootstrapTable.prototype.init = function () {
    _init.apply(this, Array.prototype.slice.apply(arguments));
    
    // 初始化 WebSocket
    if (this.options.websocketUrl && this.options.websocketAutoConnect) {
      this.initWebSocket();
    }
  };

  // WebSocket 初始化
  BootstrapTable.prototype.initWebSocket = function () {
    var that = this;
    var url = this.options.websocketUrl;
    
    if (!window.WebSocket) {
      this.options.onWebSocketStatusChange('error');
      throw new Error('Browser does not support WebSocket');
    }

    // 创建 WebSocket 实例
    this.websocket = new WebSocket(url);
    
    // 连接建立
    this.websocket.onopen = function () {
      that.options.onWebSocketStatusChange('connected');
      // 可选:发送认证信息
      // that.websocket.send(JSON.stringify({ type: 'auth', token: 'xxx' }));
    };
    
    // 接收消息
    this.websocket.onmessage = function (event) {
      try {
        var data = JSON.parse(event.data);
        var processedData = that.options.websocketDataHandler(data);
        if (processedData) {
          that.bootstrapTable('load', processedData);
        }
      } catch (e) {
        console.error('WebSocket message error:', e);
      }
    };
    
    // 连接关闭
    this.websocket.onclose = function () {
      that.options.onWebSocketStatusChange('disconnected');
      // 断线重连逻辑
      if (that.options.websocketAutoConnect) {
        setTimeout(function () {
          that.initWebSocket();
        }, 3000); // 3秒后重连
      }
    };
    
    // 错误处理
    this.websocket.onerror = function () {
      that.options.onWebSocketStatusChange('error');
    };
  };

  // 手动连接/重连
  BootstrapTable.prototype.websocketConnect = function () {
    if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
      return; // 已连接
    }
    this.initWebSocket();
  };

  // 手动断开连接
  BootstrapTable.prototype.websocketDisconnect = function () {
    if (this.websocket) {
      this.websocket.close();
    }
  };

  // 发送消息
  BootstrapTable.prototype.websocketSend = function (message) {
    if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
      this.websocket.send(JSON.stringify(message));
    } else {
      throw new Error('WebSocket connection not established');
    }
  };

  // 销毁时清理 WebSocket
  BootstrapTable.prototype.destroy = function () {
    if (this.websocket) {
      this.websocket.close();
      this.websocket = null;
    }
    _destroy.apply(this, Array.prototype.slice.apply(arguments));
  };

  // 注册插件
  $.fn.bootstrapTable.BootstrapTable = BootstrapTable;

}(jQuery);

3. 集成与使用:前端实现

3.1 HTML 结构
<!-- 引入依赖 -->
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.21.4/bootstrap-table.min.css">

<!-- 表格容器 -->
<table id="realtimeTable" class="table table-striped table-bordered">
  <thead>
    <tr>
      <th data-field="id">ID</th>
      <th data-field="name">名称</th>
      <th data-field="value">数值</th>
      <th data-field="timestamp">更新时间</th>
    </tr>
  </thead>
</table>

<!-- 引入脚本 -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.21.4/bootstrap-table.min.js"></script>
<!-- 引入自定义 WebSocket 扩展 -->
<script src="src/extensions/websocket/bootstrap-table-websocket.js"></script>
3.2 JavaScript 初始化
$(function() {
  var $table = $('#realtimeTable');
  
  $table.bootstrapTable({
    // WebSocket 连接地址(替换为实际服务地址)
    websocketUrl: 'ws://localhost:8080/realtime-data',
    // 自定义数据处理函数
    websocketDataHandler: function(data) {
      // 假设服务器返回格式:{ code: 0, data: [...] }
      if (data.code === 0) {
        // 为每条数据添加格式化时间
        return data.data.map(item => ({
          ...item,
          timestamp: new Date(item.timestamp).toLocaleString()
        }));
      }
      return [];
    },
    // 连接状态变化处理
    onWebSocketStatusChange: function(status) {
      // 更新状态指示器
      const $status = $('#connectionStatus');
      switch(status) {
        case 'connected':
          $status.text('已连接').removeClass('text-danger').addClass('text-success');
          break;
        case 'disconnected':
          $status.text('断开连接').removeClass('text-success').addClass('text-danger');
          break;
        case 'error':
          $status.text('连接错误').removeClass('text-success').addClass('text-danger');
          break;
        default:
          $status.text('连接中...').removeClass('text-success text-danger');
      }
    },
    // 其他表格选项
    pagination: true,
    search: true,
    showRefresh: true
  });
  
  // 手动控制按钮(可选)
  $('#connectBtn').click(function() {
    $table.bootstrapTable('websocketConnect');
  });
  
  $('#disconnectBtn').click(function() {
    $table.bootstrapTable('websocketDisconnect');
  });
});

4. 后端示例:Node.js WebSocket 服务

使用 ws 库实现简单的 WebSocket 服务器:

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

// 模拟数据生成
function generateMockData() {
  return {
    code: 0,
    data: Array.from({ length: 10 }, (_, i) => ({
      id: i + 1,
      name: `设备 ${i + 1}`,
      value: Math.random().toFixed(2),
      timestamp: Date.now()
    }))
  };
}

// 客户端连接时
wss.on('connection', function connection(ws) {
  console.log('Client connected');
  
  // 每秒发送一次模拟数据
  const interval = setInterval(() => {
    ws.send(JSON.stringify(generateMockData()));
  }, 1000);
  
  // 接收客户端消息
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);
    // 可以处理客户端发送的控制指令
  });
  
  // 客户端断开连接时
  ws.on('close', function() {
    console.log('Client disconnected');
    clearInterval(interval);
  });
});

console.log('WebSocket server running on ws://localhost:8080');

高级特性:断线重连与性能优化

断线重连策略

在实际应用中,网络波动可能导致连接中断,我们需要实现智能重连机制

// 改进的断线重连逻辑(在 WebSocket 扩展中)
BootstrapTable.prototype.initWebSocket = function () {
  var that = this;
  var url = this.options.websocketUrl;
  var reconnectAttempts = 0;
  var maxReconnectAttempts = 10; // 最大重连次数
  var reconnectDelay = 1000; // 初始重连延迟(毫秒)
  
  // ... 其他代码 ...
  
  this.websocket.onclose = function (event) {
    that.options.onWebSocketStatusChange('disconnected');
    
    if (that.options.websocketAutoConnect && reconnectAttempts < maxReconnectAttempts) {
      // 指数退避策略:重连间隔逐渐增加
      setTimeout(function () {
        reconnectAttempts++;
        that.initWebSocket();
        reconnectDelay *= 2; // 下次重连延迟翻倍
      }, reconnectDelay);
    }
  };
};

性能优化建议

  1. 数据增量更新:只传输变化的数据而非全量数据

    // 服务器端跟踪数据变化
    let previousData = generateMockData().data;
    
    setInterval(() => {
      const newData = generateMockData().data;
      // 找出变化的行
      const changedData = newData.filter((newItem, index) => {
        const oldItem = previousData[index];
        return newItem.value !== oldItem.value;
      });
    
      if (changedData.length > 0) {
        ws.send(JSON.stringify({
          code: 1, // 增量更新标识
          data: changedData
        }));
      }
      previousData = newData;
    }, 1000);
    
  2. 客户端节流处理:限制高频更新的渲染频率

    // 使用 lodash 的 throttle 函数
    const throttledLoad = _.throttle(function(data) {
      $table.bootstrapTable('load', data);
    }, 500); // 每 500ms 最多渲染一次
    
    // 在 websocketDataHandler 中使用
    websocketDataHandler: function(data) {
      throttledLoad(data);
      return false; // 阻止默认 load
    }
    
  3. 连接状态管理:在 UI 中提供明确的连接状态指示

    <div class="alert alert-info" role="alert">
      连接状态: <span id="connectionStatus">连接中...</span>
    </div>
    

对比实现:与 Auto Refresh 扩展的区别

Bootstrap Table 官方提供了 Auto Refresh 扩展,其原理是通过定时器周期性请求数据,与 WebSocket 方案的对比:

实现对比

特性Auto Refresh 扩展WebSocket 方案
技术原理定时 AJAX 请求WebSocket 持久连接
实时性依赖刷新间隔(默认 60 秒)毫秒级实时推送
服务器负载随刷新频率增加而增加低(单连接多消息)
数据流量全量数据请求可实现增量更新
浏览器兼容性所有支持 AJAX 的浏览器现代浏览器(IE10+)
配置复杂度简单(设置间隔时间)中等(需 WebSocket 服务器)

代码对比:Auto Refresh 扩展使用

<!-- Auto Refresh 扩展使用示例 -->
<table id="autoRefreshTable" 
       data-toggle="table"
       data-url="/api/data"
       data-auto-refresh="true"
       data-auto-refresh-interval="5" <!-- 5秒刷新一次 -->
       data-pagination="true">
  <thead>
    <tr>
      <th data-field="id">ID</th>
      <th data-field="name">名称</th>
      <th data-field="value">数值</th>
    </tr>
  </thead>
</table>

<script src="extensions/auto-refresh/bootstrap-table-auto-refresh.js"></script>

结论:对于低频更新场景(如每几分钟更新一次),Auto Refresh 扩展足够简单易用;而对于高频实时数据(如监控、仪表盘),WebSocket 方案能提供更优的性能和用户体验。

完整示例:实时监控仪表盘

以下是一个完整的实时监控仪表盘实现,整合了本文介绍的 WebSocket 扩展:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>实时数据监控仪表盘</title>
  <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/css/bootstrap.min.css">
  <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.21.4/bootstrap-table.min.css">
  <style>
    .status-indicator {
      display: inline-block;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      margin-right: 5px;
    }
    .status-connected {
      background-color: #28a745;
    }
    .status-disconnected {
      background-color: #dc3545;
    }
    .status-connecting {
      background-color: #ffc107;
    }
    .dashboard-header {
      margin-bottom: 20px;
      padding-bottom: 15px;
      border-bottom: 1px solid #eee;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="dashboard-header">
      <h1>实时数据监控仪表盘</h1>
      <div class="mt-3">
        <span id="connectionStatus">
          <span class="status-indicator status-connecting"></span>连接中...
        </span>
        <button id="connectBtn" class="btn btn-sm btn-primary ms-2">连接</button>
        <button id="disconnectBtn" class="btn btn-sm btn-danger ms-1">断开</button>
      </div>
    </div>

    <table id="realtimeTable" class="table table-striped table-hover">
      <thead>
        <tr>
          <th data-field="id" data-sortable="true">设备ID</th>
          <th data-field="name" data-sortable="true">设备名称</th>
          <th data-field="temperature" data-sortable="true">温度(°C)</th>
          <th data-field="humidity" data-sortable="true">湿度(%)</th>
          <th data-field="status" data-sortable="true" data-formatter="statusFormatter">状态</th>
          <th data-field="timestamp" data-sortable="true">更新时间</th>
        </tr>
      </thead>
    </table>
  </div>

  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.21.4/bootstrap-table.min.js"></script>
  <script src="src/extensions/websocket/bootstrap-table-websocket.js"></script>

  <script>
    // 状态格式化函数
    function statusFormatter(value) {
      const className = value === 'normal' ? 'badge bg-success' : 'badge bg-danger';
      const text = value === 'normal' ? '正常' : '异常';
      return `<span class="${className}">${text}</span>`;
    }

    $(function() {
      $('#realtimeTable').bootstrapTable({
        websocketUrl: 'ws://localhost:8080/realtime-data',
        websocketAutoConnect: true,
        pagination: true,
        search: true,
        showColumns: true,
        showRefresh: true,
        websocketDataHandler: function(data) {
          if (data.code === 0) {
            return data.data;
          }
          return [];
        },
        onWebSocketStatusChange: function(status) {
          const $status = $('#connectionStatus');
          const $indicator = $status.find('.status-indicator');
          
          switch(status) {
            case 'connected':
              $status.html('<span class="status-indicator status-connected"></span>已连接');
              break;
            case 'disconnected':
              $status.html('<span class="status-indicator status-disconnected"></span>已断开');
              break;
            case 'error':
              $status.html('<span class="status-indicator status-disconnected"></span>连接错误');
              break;
            default:
              $status.html('<span class="status-indicator status-connecting"></span>连接中...');
          }
        }
      });

      $('#connectBtn').click(function() {
        $('#realtimeTable').bootstrapTable('websocketConnect');
      });

      $('#disconnectBtn').click(function() {
        $('#realtimeTable').bootstrapTable('websocketDisconnect');
      });
    });
  </script>
</body>
</html>

总结与展望

通过本文介绍的方法,我们基于 Bootstrap Table 构建了 WebSocket 实时数据更新解决方案,该方案具有以下优势:

  1. 低延迟:相比传统轮询,实现毫秒级数据更新
  2. 高可靠性:包含断线重连和错误处理机制
  3. 易扩展性:通过 Bootstrap Table 扩展机制实现,可复用现有表格功能
  4. 性能优化:支持增量更新和节流处理,减少资源消耗

未来改进方向

  • 支持 WebSocket 子协议(如 STOMP)以实现更复杂的消息路由
  • 集成数据本地缓存,提高离线体验
  • 添加数据变更动画效果,提升用户体验
  • 实现多表共享单个 WebSocket 连接,减少连接开销

希望本文能帮助你在实际项目中实现高效的实时数据展示功能。如果觉得有用,请点赞收藏并关注获取更多 Bootstrap Table 高级用法!

参考资料

【免费下载链接】bootstrap-table wenzhixin/bootstrap-table: 是一个基于 Bootstrap 的表格插件,它没有使用数据库。适合用于数据展示,特别是对于需要使用 Bootstrap 和表格展示的场景。特点是表格插件、Bootstrap、无数据库。 【免费下载链接】bootstrap-table 项目地址: https://gitcode.com/gh_mirrors/bo/bootstrap-table

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值