vxe-table 实战:金融、监控实时刷新与高频数据更新优化

在金融、监控等场景中,表格数据需要高频刷新(如每秒更新一次股票价格)。若采用全量替换 data 的方式,会导致表格整体重绘,造成性能浪费和滚动位置丢失。
vxe-table 提供了高效的局部更新能力:我们只需修改数据源中对应行的属性,表格会自动触发视图更新,从而实现流畅的实时刷新。

本文将演示如何基于 vxe-grid 模拟股票行情数据,并实现每秒更新一次、同时保持滚动位置与选中状态的实时表格。

概览

关键点 解决方案
高频数据更新 使用 Object.assign 修改现有行对象,避免替换整个数组
保持表格状态 不重新赋值 data,只修改行内字段,表格不会丢失滚动条和选中行
性能优化 开启虚拟滚动 (virtualXConfig / virtualYConfig) 支持大量数据
定时器管理 组件卸载时 (onUnmounted) 清除定时器,避免内存泄漏
模拟真实数据 使用 XEUtils.random 生成随机股票指标,包括涨跌、成交量、量比等

代码

实现了每秒刷新一次真实行情数据的表格。

Video_2026-06-02_095541-ezgif.com-video-to-gif-converter

<template>
  <div class="demo-page-wrapper">
    <vxe-grid v-bind="gridOptions"></vxe-grid>
  </div>
</template>

<script setup>
import { reactive, onUnmounted } from 'vue'
import XEUtils from 'xe-utils'

const formatAmount = ({ cellValue }) => {
  if (cellValue) {
    return XEUtils.commafy(cellValue, { digits: 2 })
  }
  return ''
}

const formatWanAmount = ({ cellValue }) => {
  if (cellValue) {
    return `${XEUtils.floor(cellValue / 10000, 2)}万`
  }
  return ''
}

const gridOptions = reactive({
  border: true,
  loading: false,
  stripe: true,
  showOverflow: true,
  height: '100%',
  columnConfig: {
    drag: true,
    resizable: true
  },
  columnDragConfig: {
    trigger: 'cell',
    showIcon: false,
    showGuidesStatus: true
  },
  rowConfig: {
    isHover: true
  },
  toolbarConfig: {
    custom: true,
    zoom: true
  },
  checkboxConfig: {
    range: true
  },
  cellStyle({ column, row }) {
    const cellValue = row[column.field]
    switch (column.field) {
      case 'increaseRatio':
      case 'udRatio':
      case 'expansionRatio':
      case 'netInflow':
      case 'theCommittee':
        return {
          color: cellValue < 0 ? 'green' : 'red'
        }
      case 'volumeRatio':
        return {
          color: cellValue < 1 ? 'green' : 'red'
        }
      case 'maxNum':
        return {
          color: row.increaseRatio < 0 ? 'green' : 'red'
        }
      case 'minNum':
        return {
          color: row.expansionRatio < 0 ? 'green' : 'red'
        }
    }
  },
  mouseConfig: {
    selected: true
  },
  keyboardConfig: {
    isEdit: true,
    isArrow: true,
    isEnter: true,
    isBack: true,
    isDel: true,
    isEsc: true
  },
  virtualXConfig: {
    gt: 0,
    enabled: true
  },
  virtualYConfig: {
    gt: 0,
    enabled: true
  },
  columns: [
    { field: 'name', title: '名称', fixed: 'left', width: 200 },
    { field: 'currentPrice', title: '现价', minWidth: 100, formatter: formatAmount },
    { field: 'increaseRatio', title: '涨幅', minWidth: 100 },
    { field: 'udRatio', title: '涨跌', minWidth: 100 },
    { field: 'expansionRatio', title: '涨速', minWidth: 100 },
    { field: 'transactionQuantity', title: '成交量', minWidth: 100, formatter: formatWanAmount },
    { field: 'netInflow', title: '净流入', minWidth: 100, formatter: formatWanAmount },
    { field: 'maxNum', title: '最高', minWidth: 100 },
    { field: 'minNum', title: '最低', minWidth: 100 },
    { field: 'theCommittee', title: '委比', minWidth: 100 },
    { field: 'volumeRatio', title: '量比', minWidth: 100 },
    { field: 'turnoverRate', title: '换手率', minWidth: 100 },
    { field: 'priceEarningsRatio', title: '市盈率', minWidth: 100 },
    { field: 'totalMarketValue', title: '总市值', minWidth: 100, formatter: formatWanAmount },
    {
      field: 'circulatingMarketValue',
      title: '流通市值',
      minWidth: 100,
      formatter: formatWanAmount
    }
  ],
  data: []
})

const neList = XEUtils.shuffle(['最牛科技', '老六软件', '智能科技', '非凡科技', '发展软件', '超级元宇宙', '航天概念', '短视频概念', '东方科技', '中华科技股', '中国最强概念股', '消费概念', '农业概念', '东方智能', '传播科技', '智慧企业', '智慧科技', '智能汽车', '区块链'])
const cacheList: RowVO[] = []

const loadMockData = (rSize) => {
  gridOptions.loading = true
  setTimeout(() => {
    for (let i = cacheList.length; i < rSize; i++) {
      const item = {
        id: 1000000 + i,
        name: neList[i % neList.length],
        currentPrice: 0,
        increaseRatio: 0,
        udRatio: 0,
        expansionRatio: 0,
        transactionQuantity: 0,
        netInflow: 0,
        maxNum: 0,
        minNum: 0,
        theCommittee: 0,
        volumeRatio: 0,
        turnoverRate: 0,
        priceEarningsRatio: 0,
        totalMarketValue: 0,
        circulatingMarketValue: 0
      }
      cacheList.push(item)
    }
    gridOptions.data = cacheList.slice(0, rSize)
    gridOptions.loading = false
    startUpdateData()
  }, 150)
}

let isStop = false
let udTime

const stopUpdateData = () => {
  isStop = true
  if (udTime !== undefined) {
    clearTimeout(udTime)
  }
}

const startUpdateData = () => {
  if (isStop) {
    clearTimeout(udTime)
    udTime = undefined
    return
  }
  gridOptions.data.forEach((row) => {
    Object.assign(row, {
      currentPrice: XEUtils.random(99999, 999999),
      increaseRatio: (XEUtils.random(1, 999) / 100) * (XEUtils.random(0, 1) ? 1 : -1),
      udRatio: (XEUtils.random(1, 99) / 100) * (XEUtils.random(0, 1) ? 1 : -1),
      expansionRatio: (XEUtils.random(1, 99) / 100) * (XEUtils.random(0, 1) ? 1 : -1),
      transactionQuantity: XEUtils.random(899999, 999999),
      netInflow: XEUtils.random(1, 9999) * (XEUtils.random(0, 1) ? 1 : -1),
      maxNum: XEUtils.random(899999, 999999),
      minNum: XEUtils.random(99999, 109999),
      theCommittee: XEUtils.random(1, 99) * (XEUtils.random(0, 1) ? 1 : -1),
      volumeRatio: XEUtils.random(1, 20) / 10,
      turnoverRate: XEUtils.random(1, 50) / 10,
      priceEarningsRatio: XEUtils.random(99, 99999) * (XEUtils.random(0, 1) ? 1 : -1),
      totalMarketValue: XEUtils.random(999999, 9999999),
      circulatingMarketValue: XEUtils.random(999999, 9999999)
    })
  })
  udTime = setTimeout(() => {
    startUpdateData()
  }, 1000)
}

onUnmounted(() => {
  stopUpdateData()
})

loadMockData(100)
</script>

性能优化

为什么不重新赋值 gridOptions.data?

如果采用:

gridOptions.data = newDataArray   // ❌ 会触发全量重绘

表格会丢弃原有 DOM 和滚动状态,导致视觉闪烁、选中丢失。

而采用:

gridOptions.data.forEach(row => {
  Object.assign(row, newValues)   // ✅ 只修改已有对象的属性
})

Vue 能检测到对象属性的变化,从而仅重新渲染变化的单元格,性能极高且状态保持。

定时器模式

  • 使用 setTimeout 递归调用实现周期性刷新(比 setInterval 更安全,避免回调堆积)。
  • 通过 isStop 标志和 onUnmounted 钩子确保组件销毁时停止所有定时器。

虚拟滚动配置

virtualXConfig: { gt: 0, enabled: true },
virtualYConfig: { gt: 0, enabled: true }

动态样式与格式化

  • cellStyle:根据数值正负或与其他列的关联动态设置文字颜色(如涨幅为红涨绿跌)。
  • formatter:对成交量、总市值等进行单位转换(万/亿)和千分位格式化,保持数据简洁。

控制刷新频率,实际业务中可根据数据源推送速率调整间隔(如 500ms、2s),避免过度频繁触发渲染。

https://vxetable.cn

posted @ 2026-06-08 14:39  你个老六  阅读(3)  评论(0)    收藏  举报