V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
rcocco
V2EX  ›  程序员

好奇移动端、桌面端是怎么实现列表控件渲染大量元素不卡顿的?

  •  1
     
  •   rcocco · 2024-07-22 23:17:07 +08:00 · 4590 次点击
    这是一个创建于 377 天前的主题,其中的信息可能已经有所发展或是发生改变。
    网页端是有多少 DOM 元素就渲染多少元素,不管这些元素是不是用户当前视口可见的,所以元素一多就会很卡。每次都要专门把渲染的逻辑写成虚拟滚动来解决这个问题,但用过的一些虚拟滚动库总是有各种各样的限制或小毛病,例如要求元素等高,滚动到底部不对劲等等。

    我就很好奇其他端,像移动端和桌面端都是怎么解决这个问题的?搜索 [安卓 虚拟滚动] 还搜不到什么东西
    40 条回复    2024-07-24 22:35:09 +08:00
    sduoduo233
        1
    sduoduo233  
       2024-07-22 23:33:46 +08:00 via Android
    安卓是 recyclerview
    abcbuzhiming
        2
    abcbuzhiming  
       2024-07-22 23:39:09 +08:00
    移动端我不清楚。但是桌面端的话,我没听说过传统 UI 的列表控件有用虚拟滚动的,实际上,当元素量够大的时候,一样会卡的,只是桌面端大部分时候处于性能溢出的状态,且内存管够,所以一般遇不到那个能让你卡的数量级。
    Irisxx
        3
    Irisxx  
       2024-07-22 23:40:59 +08:00 via iPhone
    主要是归功于对象池的重用机制,辅之以 LRU 这些缓存机制优化。
    verrickt
        4
    verrickt  
       2024-07-22 23:52:49 +08:00 via Android
    微软的 GUI 框架把相关的技术叫做 UI Virtualization ,有兴趣可以看看
    lmw2616
        5
    lmw2616  
       2024-07-22 23:56:57 +08:00
    android 用 recyclerview ,也是类似于虚拟滚动
    chiaf
        6
    chiaf  
       2024-07-22 23:58:52 +08:00
    你搜索 iOS UITableView 应该能搜到😄
    很早之前的经典面试题,说一下 UITableView Cell 的复用原理
    kemchenj
        7
    kemchenj  
       2024-07-22 23:59:56 +08:00
    其它端不清楚,iOS 端其实就是虚拟滚动,列表会维护一个重用池,把不可见的 cell 塞进去,然后需要展示的时候从里面拿 cell 出来显示,如果发现不够就再补充新的进来

    iOS 封装得还行,但也免不了各种限制和小毛病,都是需要开发者额外做一些处理去完善体验,例如预先计算每个元素的高度以便把列表撑开之类的...

    感兴趣的话可以搜一下“UITableView 重用”
    A4l1CteRQHlG1Bs8
        8
    A4l1CteRQHlG1Bs8  
       2024-07-23 00:45:14 +08:00 via Android
    回收,重用,缓存
    UnluckyNinja
        9
    UnluckyNinja  
       2024-07-23 01:10:27 +08:00
    如果从只渲染可见部分的组件来看,那基本上都可以算作虚拟滚动(否则就是加硬件了)
    你用的库总是有各种各样的不完美,可能是因为复杂度难以实现/怕导致库臃肿(所以固定高度),或者虚拟高度计算不正确。
    根本原因在于虚拟高度需要根据内部组件的实际大小调整以正确计算滚动位置。
    你看像 twitter 也是虚拟滚动,但你要是快速拉到中间再缓慢向上拉,会发现 twitter 的滚动条也在闪,(猜测)这就是在修正假定的未渲染元素的高度到渲染完后实际高度,并相应调整总高度。
    如果你是想写前端的一个虚拟滚动组件,可以参考 VueUse 推荐的这个独立虚拟滚动组件 https://vue-virtual-scroller-demo.netlify.app/dynamic
    superkeke
        10
    superkeke  
       2024-07-23 09:14:07 +08:00
    iOS UITableView ,android recyclerview ,移动端这是最基本的了。
    tangmanger
        11
    tangmanger  
       2024-07-23 09:17:21 +08:00
    wpf 大列表有虚拟化 只渲染展示的
    shunia
        12
    shunia  
       2024-07-23 09:25:42 +08:00
    解决方案全都是虚拟列表,没有例外。方案细节基本就是元素复用、列表两端留缓冲区、高度重新计算等。

    你觉得不好用是因为这个没有完美的解决方案,比如你说的等高问题,应该是想要考虑保留列表滚动位置,否则根本不必等高。

    这些都是一说出来就明白的基础原理,并不是哪个技术方案本身能解决的,桌面端列表大了不虚拟也会卡的爆炸,只是在同一个硬件设施上比网页端能多承受一些而已。
    hefengwqz
        13
    hefengwqz  
       2024-07-23 09:27:12 +08:00
    @abcbuzhiming 桌面端我们之前开发性能调优工具的时候,数据表格就是做的虚拟滚动,Android 手机录制几十秒解析出来的数据就有百万条。
    duanxianze
        14
    duanxianze  
       2024-07-23 09:43:11 +08:00
    都是虚拟滚动,上千上万的元素啥玩意也顶不住的,只不过浏览器的限制导致 dom 复用总有问题,安卓 ios 和 win 内置的相关逻辑
    chniccs
        15
    chniccs  
       2024-07-23 09:54:42 +08:00
    安卓最开始的时候是 listview ,后面换成了 recyclerview ,不过都是复用,但是当年试过,不复用的话,列表内容不复杂,不是太快的滑动也没有很卡,只是内存会爆
    wya93
        16
    wya93  
       2024-07-23 09:55:41 +08:00
    虚拟分页,
    iOCZS
        17
    iOCZS  
       2024-07-23 10:08:05 +08:00
    反正你只能看到一个屏幕的东西,我提前准备好三屏,够你看的了
    zpxshl
        18
    zpxshl  
       2024-07-23 10:14:56 +08:00 via Android
    你搜不到是因为,安卓列表基本都用 rv 实现,不存在问题,自然没有“解决方案”
    unco020511
        19
    unco020511  
       2024-07-23 10:15:02 +08:00   ❤️ 2
    移动端的列表控件会在背后做非常多的工作,以便解决列表渲染的性能.就拿 android 端的 recyclerview 举例:
    1. 视图复用( View Recycling ):RecyclerView 通过 ViewHolder 模式来复用已经创建的视图。简单来说,当某个视图滚出屏幕时,它不会被销毁,而是会被放入一个"回收池"中。当新的视图需要显示时,RecyclerView 会从这个回收池中取出一个视图,并绑定新的数据。这大大减少了视图创建和销毁的开销。

    2 .按需加载( Lazy Loading ):RecyclerView 只会创建和绑定当前屏幕上可见的视图,以及少量即将进入屏幕的视图。这样可以有效减少内存消耗和处理时间。

    3. 布局管理器( LayoutManager ):RecyclerView 使用 LayoutManager 来决定如何在屏幕上排列子视图。不同的 LayoutManager 可以实现不同的布局方式,如线性布局、网格布局等。LayoutManager 负责计算哪些视图需要显示,哪些可以复用。

    4 .差分更新( Diff Util ):RecyclerView 支持高效的数据更新。当数据集发生变化时,可以使用 DiffUtil 来计算新旧数据集之间的差异,只更新那些真正改变的部分,而不是重新渲染整个列表。
    debuggerx
        20
    debuggerx  
       2024-07-23 10:16:35 +08:00
    条目非等高的长列表还要带滚动条,原理上就没办法高效、完美解决,只能是各种取舍,有时是技术上,有时是设计上。
    或者换句话说,只要原理上无法避免大量元素 layout 的场景,怎么都会卡,只是在同等硬件配置下不同的语言和框架会出现卡顿现象的量级会有所不同。
    codehz
        21
    codehz  
       2024-07-23 10:21:34 +08:00
    (其实浏览器也不会绘制超过屏幕的内容( edge 有几个版本你就会在滚动过快的时候看到白屏被一个个方块填上的过程),但超过屏幕的内容不止是绘制的问题,还有布局等其他因素需要计算,加上为了 js 访问方便而设计的对象接口的 overhead 才会显得不能装太多东西,实际上就单纯说显示一个巨大的表格来说浏览器问题不是很大,好多设计起来就只有单页的文档(例如隔壁的 rust 的某文档,html5 的单页 spec ),其内容长度远超一般“无限滚动”正常用户能滚动到的范围,也可以顺利的在浏览器上呈现。。。(当然和一般无限滚动场景不同的是,它内容上是较为简单的,计算布局的压力也会小很多)
    桌面端除了用虚拟滚动的思路之外,还有一种叫 imgui 的思路,其抛弃了控件的抽象,根据滚动位置直接计算出需要展示的范围,然后每次都只画用到的部分,也可以解决这个问题==(有一些商业表格控件就是这个思路)
    之所以虚拟滚动一类的机制需要使用“复用”,其本质不是绘制元素有多慢,而是为了表达这个 UI 对象抽象带来的额外成本很高,不能简单的丢弃再重建,或者干脆建立一大堆放着
    ZGame
        22
    ZGame  
       2024-07-23 13:56:14 +08:00
    上面的都说了虚拟滚动,其实网页端还有种做法就是自己用画布做渲染。
    shadowyue
        23
    shadowyue  
       2024-07-23 14:02:11 +08:00
    关于你说的,移动端有多少 DOM 就渲染多少 DOM ,这个是测试出来的结果吗?
    这个问题应该是和样式有关,移动端的元素通过样式来定位有不少方式。
    导致很多情况,不全部渲染出来就无法确定一个 dom 的真实位置。
    但是据我所知浏览器也是尝试做了优化的,比如你包含很多 video 或者 canvas 元素的话,
    一般情况下,不在可视范围是不会去渲染的。
    lshbosheth
        24
    lshbosheth  
       2024-07-23 14:02:32 +08:00
    b821025551b
        25
    b821025551b  
       2024-07-23 14:04:28 +08:00
    不卡么?去 12306 上搜一下惠州到东莞的车票试试
    janus77
        26
    janus77  
       2024-07-23 14:32:17 +08:00
    你可以理解为虚拟滚动,但是他和 web 端的区别是:第一,他是官方维护的,第二,他的语言和框架就决定了性能够好。
    stew5566
        27
    stew5566  
       2024-07-23 14:33:36 +08:00
    @ZGame 很多时候网页会需要交互吧,感觉画布添加交互是不是会麻烦些
    ZGame
        28
    ZGame  
       2024-07-23 14:47:44 +08:00
    @stew5566 那肯定阿...
    icloudguizhou
        29
    icloudguizhou  
       2024-07-23 14:48:20 +08:00
    @b821025551b 最流畅的还是 M3Max 芯片 128GB RAM 的 mac 上安装拼多多 APP 买高铁票,一键免密支付还可以调用 touchID ,fantastic and smooth experiences than ever before,
    icloudguizhou
        30
    icloudguizhou  
       2024-07-23 14:52:13 +08:00
    现在 web 和 APP 客户端的边界已经模糊了。Mac 也支持 APP 了。 自动获取 Mac GPS 定位,调用 touch ID 验证支付密码,web url universal link 直接调到 APP 的 landing page, 原生运行比 iPhone mirroring 流畅
    LavaC
        31
    LavaC  
       2024-07-23 15:05:32 +08:00
    不定高的元素本来就没法实现完全准确的虚拟滚动,不渲染不知道高度->不知道高度就没法给你正确的 padding
    qrobot
        32
    qrobot  
       2024-07-23 15:07:49 +08:00
    @LavaC 不固定高的元素可以实现虚拟滚动啊, 因为元素最终渲染到页面一定是有高度的, 只不过自己需要计算出来高度而已
    qrobot
        33
    qrobot  
       2024-07-23 15:09:18 +08:00
    @LavaC 我自己实现的虚拟滚动, 在快速滚动下不闪动, 并且性能很强, 我试过 10w 列 * 10w 行, 这个时候才开始卡顿, 而且这个时候的卡顿还有优化空间
    Anarchy
        34
    Anarchy  
       2024-07-23 15:21:03 +08:00 via Android
    感觉搜不到 Android 虚拟滚动是这部分属于默认实现了,导致根本不理解什么是虚拟滚动。我刚开始学其他平台都好奇怎么没有 ListView 的组件只有个 scroll view ,都不怕卡的么。
    rcocco
        35
    rcocco  
    OP
       2024-07-23 18:01:04 +08:00
    学到了,原来各端其实都得做虚拟化列表,有的是开发者自己做,有的是原生控件内部在做。

    感谢大佬们
    furlxy
        36
    furlxy  
       2024-07-23 19:09:48 +08:00
    iOS UITableView 享元模式
    jones2000
        37
    jones2000  
       2024-07-23 22:38:13 +08:00
    虚拟表格+画布自己绘制表格,10W-100w 条数据显示完全不卡。
    Chuckle
        38
    Chuckle  
       2024-07-24 11:10:08 +08:00   ❤️ 1
    @LavaC 写过个不定高的虚拟瀑布流 demo ,准确的布局还是做得到的,https://list.qcqx.cn/#/list/virtualwaterfall
    HangoX
        39
    HangoX  
       2024-07-24 17:36:20 +08:00
    只有 web 端才有虚拟滚动的概念,其他平台都是原生滚动。虚拟滚动是相对于浏览器的滚动条而说。原生开发本来就是那个滚动条。只是原生开发都考虑会回收利用问题
    LavaC
        40
    LavaC  
       2024-07-24 22:35:09 +08:00
    @Chuckle 我固有印象了,该进行一个代码的学习
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   923 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 19:02 · PVG 03:02 · LAX 12:02 · JFK 15:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.