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

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

  •  
  •   lurenjiaMAX · 7 天前 via Android · 1479 次点击

    程序是这样的:

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

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

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

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

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

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