V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
lurenjiaMAX
V2EX  ›  问与答

请教一个前端设计及实现问题, 绘制并实时更新 96 个折线图的最佳实践?

  •  
  •   lurenjiaMAX · 10 天前 via Android · 1536 次点击

    程序是这样的:

    • 后端维护 96 个通道, 每个通道中有三种需要绘图的数据, 每隔 1s 更新一次.
    • 前端则需要将 96 个通道数据绘制在折线图上, 但是不一定全部都挤在一个屏幕显示上.
    • 二者交互的数据包含三种浮点数数据和他们对应的时间戳, 二者之间可以通过 ws 通信, 也可通过 ipc 通信.

    问题有这些:

    • 交互时, 是将 96 个通道数据一起发送好还是单独发送好.
    • 前端绘制时选择什么绘图库性能好, 可选交互功能.
    • 选择这种前后端分离的架构, 却追求性能, 是不是从一开始就走错了方向? 如果有其他架构, 选择什么样的架构比较合适?

    目前我选择的是 tauri+vite+react+highchart(highstock).

    我调研的每一个绘图库都吹榜他的性能, 包括上面选择的 highstock.

    后端性能没有问题, 交互时后端单独发送数据, 前端使用 debounce.

    但是性能却非常糟糕.

    34 条回复    2025-01-24 15:50:29 +08:00
    paopjian
        1
    paopjian  
       10 天前   ❤️ 1
    好家伙,每秒 96*3 的数据还要时时绘图?这不现实吧,前端折线图这种每次改数据是要重新计算的,你这算得过来吗
    miloooz
        2
    miloooz  
       10 天前   ❤️ 2
    echarts 的大数据量绘图还可以啊 。
    通道数据在绘图时 是追加到原有数据还是覆盖了所有数据额,覆盖可能会有性能问题,追加的话会好很多。
    miloooz
        3
    miloooz  
       10 天前
    https://echarts.apache.org/zh/api.html#echartsInstance.setOption
    不过我没做过这么大数据量的测试,你可以试试看哈
    shadowyue
        4
    shadowyue  
       10 天前
    你说的性能糟糕具体指什么现象
    shadowyue
        5
    shadowyue  
       10 天前   ❤️ 1
    你不如直接丢一份测试数据出来,还有设计图
    crazyBlack
        6
    crazyBlack  
       10 天前   ❤️ 1
    首先肯定是一起发好,减少连接数

    你这数据量到底有多大,能碰到绘图库的性能上限,你先确认一下是数据存的太大内存顶不住了,还是点太多绘制上去有问题,如果是绘制的问题正常的库都会有给数据抽稀的方法,过大的数据直接绘制上去意义不大,我盲猜是数据量过大存不住了,你可以研究一下接一层 bff 做数据整合和抽稀,或者交给后端做

    然后前后端分离和追求性能不冲突,如果你觉得是交给客户端的计算量过大可以试试尽量把计算放在 worker 里或者考虑 next 或者 remix 这样的服务端渲染框架
    crazyBlack
        7
    crazyBlack  
       10 天前
    我才看到技术栈里还有个 tauri ,next 或者 remix 当我没说,生产敢用这东西的都是个猛人,打扰了
    andyskaura
        8
    andyskaura  
       10 天前   ❤️ 1
    1.都差不多,但只要前端的主线程没出现堵塞,一起发送更好。websocket 会自己分片,ipc (我不知道你前端的什么 ipc ,只说 electron 的 ipc )大数据会有一定延迟( 4k 的 bmp 会有 200ms 左右延迟),但预估你数据量还没那么大。
    2.别用 svg ,用 canvas ,绘图的渲染性能都没什么压力的,毕竟都只是 2d 的。我猜测问题可能出在数据处理上,可以用帧循环来将 96 个数据排序提交,减小并发。用 woker 或者 wasm 来优化处理,无论怎样,千万别让主线程卡住了。
    3.感觉前后端分不分离和性能没啥联系。

    最好还是把问题现象描述的清楚一点,主要不好分析出现在堵在哪儿了
    chairuosen
        9
    chairuosen  
       10 天前   ❤️ 1
    不用 UI 框架的内置 state ,直接调用绘图框架的更新方法更新,绘图框架最好是 canvas 的,这样就只有数据计算过程,没有 vdom 的更新
    lurenjiaMAX
        10
    lurenjiaMAX  
    OP
       10 天前 via Android
    @shadowyue 好的 我后面收拾一下代码 做一个最小可运行实例分享出来
    thulof
        11
    thulof  
       10 天前   ❤️ 1
    你是不是每次都全量重绘的…… 只追加 delta 就好了吧
    sgiyy
        12
    sgiyy  
       10 天前   ❤️ 1
    你要把你的效果图和具体数据量大小放出来才好评估
    thulof
        13
    thulof  
       10 天前
    @thulof #11 哦不对,仔细看了下是折线图,那确实需要全量重绘。那考虑下分片分批处理吧
    lurenjiaMAX
        14
    lurenjiaMAX  
    OP
       10 天前 via Android
    @sgiyy 效果图是这样的 ![图片]( )
    Moierby
        15
    Moierby  
       10 天前   ❤️ 1
    我做过一个页面 30 个折线和柱状图的需求,直接一次性请求过来一把梭,用的 echarts ,基本没有明显卡顿。不过我的绘图数据比较小,每张 chart 上也就几十个节点。
    现在流行的 chart 库都是用 canvas 实现的,每次绘制都是清理完之前的图层,重算重绘,也就是图表渲染这一步你基本没有什么可优化的空间。
    你看一下你的瓶颈在哪,针对性的优化:
    如果数据请求太慢,你就不要一个接口一次性返回所有 data ;
    如果单个 chart 数据节点太多,一次性绘制 96 个太占资源,你就判断哪些出现在当前 viewport 才绘制哪个
    lurenjiaMAX
        16
    lurenjiaMAX  
    OP
       10 天前 via Android
    @crazyBlack 数据量没有多少 就是每隔一秒获取一次最新的数据 增量更新
    MRG0
        17
    MRG0  
       10 天前   ❤️ 1
    一次性显示 96 个,这眼睛看的过来吗
    lurenjiaMAX
        18
    lurenjiaMAX  
    OP
       10 天前 via Android
    @Moierby 这是个好主意! 我测试只显示 4 个图的话是没有多大压力的
    horizon
        19
    horizon  
       10 天前   ❤️ 1
    先 measure ,再优化
    moooooooo
        20
    moooooooo  
       10 天前   ❤️ 1
    为什么要一次显示 96 个....一点交互都不讲究的?
    lurenjiaMAX
        21
    lurenjiaMAX  
    OP
       10 天前 via Android
    @shadowyue 这是把前端的部分摘出来后的测试代码:
    https://codesandbox.io/p/devbox/youthful-yalow-hfzycq
    msmmbl
        22
    msmmbl  
       10 天前   ❤️ 2
    之前做过 120 个摄像头的缩略图+状态数据,但是不像楼主要秒级的,用的是 websocket ,大概有:
    1. 判断哪些图像在浏览器滚动条外面,看不见的不刷新。
    2. 一个批次一个批次的请求。比如先一次请求 10 个画面,然后画到页面上,观察用了多久时间,根据时间动态调整下一个批次的数量,尽量让每个预览窗口雨露均沾。
    3. 状态用了增量数据,每次只发送上次和这次的改变。
    crazyBlack
        23
    crazyBlack  
       10 天前
    @lurenjiaMAX 这个链接是个空项目,你看看哪里出了问题,上面只渲染当前可见是个好方法,试试 react-window
    sampeng
        24
    sampeng  
       10 天前
    所以我特别好奇 grafana 如何做到的。几乎页面不带卡的
    lurenjiaMAX
        25
    lurenjiaMAX  
    OP
       10 天前 via Android
    lurenjiaMAX
        26
    lurenjiaMAX  
    OP
       10 天前 via Android
    @miloooz 是的 我就是用的追加数据. 后面我也试一下 echarts
    lurenjiaMAX
        27
    lurenjiaMAX  
    OP
       10 天前
    @chairuosen #9 我是参考 https://www.npmjs.com/package/highcharts-react-official#optimal-way-to-update 这个绘图库提到的最优方式来更新的
    okakuyang
        28
    okakuyang  
       10 天前
    用 canvas 不是随便画,都是些 2d 的简单东西。而且不需要同屏显示,看到那些就显示那些。
    ty29022
        29
    ty29022  
       10 天前 via iPhone
    重采样呗 有啥纠结的
    sgiyy
        30
    sgiyy  
       10 天前
    @lurenjiaMAX #14 总的需求就是 96 个图表,不过每个图表的数据量不大。
    回答你的问题:
    1. 数据量不大的话,一个接口发送最好。
    2. 流行的图表库一般都没问题,我这边最常用的是 Echarts 。
    3. 这种架构没问题

    其他:另外一个最需要注意的是图表的数量以及重绘的压力,js 有个 IntersectionObserver 的 API 可以判断元素是否在可视区域,可以封装个 hook ,不在可视范围内就不更新图表,这样页面性能压力会大大减小。
    214L
        31
    214L  
       10 天前
    我遇到 echarts 等库解决不了的图表性能问题的时候,采用的解决方案是自己写了一个。单纯的 canvas 绘制图表和缩放,更新,交互逻辑,最终表现流畅。
    数据量大概是 600 条线,每条 1300 个点?
    不过我的图表是我们这个项目的核心模块,所以有时间去打磨。
    qping
        32
    qping  
       10 天前 via iPhone
    你这么大的图,一屏幕也显示不了几个。后台数据可以接收,但是不用都渲染,监听下 scroll ,只刷新可见区域
    ccdjh
        33
    ccdjh  
       10 天前
    就是前端的问题。
    1 ,你这小数据。强迫症的话,就服务器压缩一下。
    2 ,市面上的都可以,根本上应该是 react 和 vue 这些机制是会重刷或遍历一次整个页面,所有会导致性能下降。你前端接收数据,如果坚持使用上面的框架,就局部渲染。
    3 ,技术都没错。技术不到位。

    实际就是搞个 1 秒心跳一下的客户端。技术压力在前端了。一般会写但不太了解原理的前端导致的。-_- 自己画几个,highchart 钱省了,性能也上来了。就是前端工资价格贵了。
    thulof
        34
    thulof  
       9 天前
    用虚拟列表吧,react-window 了解一下
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   701 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 22:11 · PVG 06:11 · LAX 14:11 · JFK 17:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.