当前位置: 首页 > news >正文

补充:Repeat 虚拟滚动与 cachedCount 到底怎么用

补充:Repeat 虚拟滚动与 cachedCount 到底怎么用

上一篇我们已经讲了ForEachLazyForEachRepeat的区别,也给出了一个结论:在 HarmonyOS 6.0 的新项目里,列表渲染可以优先考虑Repeat

但是在实际写聊天列表、商品列表、卡片流的时候,很容易把两个概念搞混:

  • Repeat.virtualScroll()
  • List.cachedCount()
  • Repeat.template(..., { cachedCount })

这几个名字里都出现了“虚拟滚动”“缓存”“懒加载”这些词,但它们负责的事情并不一样。

这篇补充就专门把这个点讲清楚。


一、先说结论:Repeat 不直接负责“上下预加载几条”

如果你想实现这样的效果:

当前屏幕最多展示 6 条数据,希望用户滚动时,屏幕上方和下方附近可以提前准备 3 条,减少快速滚动时的白屏和卡顿。

正确思路不是只写Repeat.template(..., { cachedCount: 3 }),而是:

List(){Repeat<Message>(this.messageList).virtualScroll({totalCount:this.messageList.length}).key((item:Message)=>item.id).each((ri:RepeatItem<Message>)=>{ListItem(){MessageItem({message:ri.item})}})}.cachedCount(3)

这里真正控制“可视区域外预加载”的是:

.cachedCount(3)

也就是ListcachedCount,不是Repeat.templatecachedCount

可以简单记成一句话:

Repeat.virtualScroll()负责开启虚拟滚动;List.cachedCount()负责可视区域外预加载;Repeat.template(..., { cachedCount })负责模板节点复用缓存池。


二、.virtualScroll()做了什么

先看这段代码:

Repeat<Message>(this.messageList).virtualScroll({totalCount:this.messageList.length})

它的意思是:

开启Repeat的虚拟滚动模式,让列表不要一次性创建所有 item,而是根据滚动容器的可视区域,按需创建和复用 UI 节点。

也就是说,如果你的聊天列表有 1000 条消息,它不会一开始就把 1000 个 UI 节点全部创建出来。

更准确的理解是:

屏幕内的数据:需要渲染 屏幕附近的数据:可能会根据缓存策略提前渲染 离屏幕很远的数据:不会提前创建 UI 节点

所以不要把virtualScroll理解成“只渲染屏幕内 6 条”。

更准确的说法是:

virtualScroll会让Repeat按可视区域附近的数据进行懒加载和节点复用,而不是全量渲染整个数组。


三、List.cachedCount(3)才是预加载附近 item

假设现在一个页面最多展示 6 条消息,每条消息高度大约是72vp,希望上下附近额外预加载 3 条,可以这样写:

interfaceMessage{id:stringtype:stringcontent:string}@ComponentV2struct ChatListDemo{@LocalmessageList:Message[]=[{id:'1',type:'text',content:'第一条消息'},{id:'2',type:'text',content:'第二条消息'},{id:'3',type:'text',content:'第三条消息'},{id:'4',type:'text',content:'第四条消息'},{id:'5',type:'text',content:'第五条消息'},{id:'6',type:'text',content:'第六条消息'},{id:'7',type:'text',content:'第七条消息'},{id:'8',type:'text',content:'第八条消息'},{id:'9',type:'text',content:'第九条消息'},{id:'10',type:'text',content:'第十条消息'}]build(){List(){Repeat<Message>(this.messageList).virtualScroll({totalCount:this.messageList.length}).key((item:Message)=>item.id).each((ri:RepeatItem<Message>)=>{ListItem(){Row(){Text(ri.item.content).fontSize(16).fontColor('#222222')}.width('100%').height(72).padding({left:16,right:16}).backgroundColor('#FFFFFF')}})}.width('100%').height(72*6)// 一屏大约展示 6 条.cachedCount(3)// 可视区域外额外预加载附近 item}}

这段代码里有两个关键点。

第一个关键点是:

.virtualScroll({totalCount:this.messageList.length})

它负责开启虚拟滚动。

第二个关键点是:

.cachedCount(3)

它挂在List上,负责列表可视区域外的预加载数量。


四、Repeat.template(..., { cachedCount })不是预加载上下几条

再看另一种写法:

.template('confirm_trip',(ri:RepeatItem<ChatMessage>)=>{ListItem(){ConfirmTripCardComp({card:ri.item.card})}},{cachedCount:2})

这里的cachedCount: 2很容易被误解成“屏幕外缓存 2 条卡片数据”。

其实不是。

它更准确的含义是:

confirm_trip这个模板类型最多缓存 2 个可复用的 UI 节点。

也就是说,它是模板复用层面的缓存池,不是列表可视区域外的预加载数量。

可以这样区分:

写法控制什么可以怎么理解
Repeat.virtualScroll()是否开启虚拟滚动不一次性创建全部 item
List.cachedCount(3)可视区域外预加载 item屏幕附近多准备一些列表项
Repeat.template(..., { cachedCount: 2 })某个模板的复用节点缓存池某类模板最多保留几个可复用节点

所以,如果你的目标是“用户滚动时,上下附近提前加载 3 条”,应该写在List上:

List(){// Repeat ...}.cachedCount(3)

而不是只写:

.template('xxx',builder,{cachedCount:3})

五、多类型聊天消息里的完整写法

如果你的聊天列表里既有普通文本消息,又有确认打车卡片,可以这样组织:

interfaceTripCard{title:stringstartLocation:stringendLocation:stringdistance:stringduration:stringpriceRange:string}interfaceChatMessage{id:stringtype:stringcontent:stringcard?:TripCard}@ComponentV2struct ChatListComp{@Parammessages:ChatMessage[]build(){List(){Repeat<ChatMessage>(this.messages).virtualScroll({totalCount:this.messages.length}).key((item:ChatMessage)=>item.id).templateId((item:ChatMessage)=>{if(item.type==='confirm_trip'){return'confirm_trip'}return'text'}).each((ri:RepeatItem<ChatMessage>)=>{ListItem(){Text(ri.item.content).fontSize(15).fontColor('#222222').padding(12)}}).template('confirm_trip',(ri:RepeatItem<ChatMessage>)=>{ListItem(){if(ri.item.card){ConfirmTripCardComp({card:ri.item.card})}}},{cachedCount:2})}.width('100%').height('100%').cachedCount(3)}}

这段代码里有三层含义。

第一层:

.virtualScroll({totalCount:this.messages.length})

开启虚拟滚动。

第二层:

.templateId((item:ChatMessage)=>{if(item.type==='confirm_trip'){return'confirm_trip'}return'text'})

根据消息类型选择模板。普通文本走默认模板,确认打车消息走confirm_trip模板。

第三层:

.cachedCount(3)

挂在List上,表示可视区域外额外预加载附近列表项。

而这里:

.template('confirm_trip',...,{cachedCount:2})

只是confirm_trip模板自己的节点复用缓存池,不是上下预加载 2 条。


六、结合聊天列表再理解一次

以聊天列表为例,假设当前屏幕能看到 6 条消息,列表总共有 100 条消息。

如果不做虚拟滚动,可能会倾向于一次性创建大量 UI 节点,数据量大时会影响性能。

使用Repeat.virtualScroll()后,系统会根据列表当前显示区域,按需创建附近的 UI 节点。

再加上:

List().cachedCount(3)

可以让滚动容器在可视区域外提前准备一些 item。

大致可以这样理解:

当前屏幕可见:约 6 条 屏幕附近预加载:由 List.cachedCount(3) 控制 离屏幕很远的数据:不会提前创建 UI 节点 滚动过程中:复用已经创建过的节点

不过要注意,实际运行时不一定永远严格等于“上面 3 条 + 下面 3 条”。

因为缓存分布还会受下面这些因素影响:

  • 当前是否在列表顶部
  • 当前是否在列表底部
  • 用户滚动方向
  • item 高度是否一致
  • 外层容器布局
  • 系统内部复用策略

所以在表达时不要说死:

一定是上面 3 条,下面 3 条。

更稳的说法是:

设置List.cachedCount(3)后,列表会在可视区域外额外预加载附近 item,减少滚动时的白屏和卡顿。


七、开发时最容易写错的地方

1. 只写了virtualScroll,以为已经设置了缓存数量

错误理解:

Repeat<Message>(this.messageList).virtualScroll({totalCount:this.messageList.length})

然后以为它已经自动设置了“上下缓存 3 条”。

实际上,virtualScroll只是开启虚拟滚动,不是设置上下缓存数量。

如果要控制可视区域外预加载数量,还要看外层滚动容器:

List(){// Repeat ...}.cachedCount(3)

2. 把template cachedCount当成列表预加载数量

错误理解:

.template('confirm_trip',builder,{cachedCount:3})

以为这代表“确认打车卡片在屏幕外预加载 3 条”。

实际上它表示的是:

confirm_trip模板类型最多缓存 3 个可复用节点。

3. 没有写稳定的 key

列表数据经常新增、删除、更新时,建议给Repeat配置稳定的key

.key((item:ChatMessage)=>item.id)

这样系统在更新 UI 时更容易识别每条消息,减少不必要的重建。

聊天列表里不要用数组下标当核心身份标识,尤其是消息可能插入、删除、刷新时。


八、最终记忆口诀

最后用几句话记住就够了:

ForEach:小列表普通循环。 LazyForEach:老的大列表懒加载方案,写法偏重。 Repeat:新的统一循环渲染方案。 Repeat.virtualScroll:开启虚拟滚动。 List.cachedCount:控制可视区域外预加载 item。 Repeat.template cachedCount:控制某个模板类型的节点复用缓存池。

如果是 HarmonyOS 6.0 新项目里的聊天列表,我会优先这样写:

List(){Repeat<ChatMessage>(this.messages).virtualScroll({totalCount:this.messages.length}).key((item:ChatMessage)=>item.id).templateId((item:ChatMessage)=>item.type).each((ri:RepeatItem<ChatMessage>)=>{ListItem(){TextMessageItem({message:ri.item})}}).template('confirm_trip',(ri:RepeatItem<ChatMessage>)=>{ListItem(){ConfirmTripCardComp({card:ri.item.card})}},{cachedCount:2})}.cachedCount(3)

这套写法的优势是:

  • 列表不会全量渲染所有消息
  • 可以根据type分发不同消息模板
  • 滚动时可以预加载附近 item
  • 复杂卡片模板可以复用节点
  • 很适合 AI 聊天、订单卡片、商品流、通知流这类多类型列表场景

九、一句话总结

Repeat负责循环渲染和虚拟滚动,List.cachedCount负责可视区域外预加载,Repeat.template cachedCount负责模板节点复用。想让聊天列表“当前屏幕 6 条、上下附近预加载 3 条”,核心写法就是:

List(){Repeat<Message>(this.messageList).virtualScroll({totalCount:this.messageList.length}).key((item:Message)=>item.id).each((ri:RepeatItem<Message>)=>{ListItem(){MessageItem({message:ri.item})}})}.cachedCount(3)
http://www.zskr.cn/news/1497686.html

相关文章:

  • 2026五常大米谁家好吃?产区好米选购实用解析 - 最新行业资讯
  • 老域名是什么?为什么SEO都喜欢老域名
  • 光伏电站LoRa数据传输远程控制系统方案
  • 软件测试简历项目经验如何编写?
  • 大功率UPS电流检测技术白皮书:2000A以上量程的传感器选型指南
  • 实测!山东拓兴MGE合金板性能揭秘,硬度耐磨突出但耐腐蚀性
  • Effective C++ 条款09:绝不在构造和析构过程中调用 virtual 函数
  • 在威尼斯遇到注单未同步一直提不了现解决的方法?
  • 打造Harness最佳实践,华为云智果AgentArts企业级智能体平台破解智能体规模化落地难题
  • 2026年,武汉口碑好的全屋定制工厂究竟有哪些?带你一探究竟!
  • KK键盘 v4.0.2-快捷连发+聊天气泡+斗图,输入体验直接拉满
  • 如何在Windows电脑上告别笨重模拟器?APK安装器让你3分钟搞定安卓应用安装
  • 爽姐的装修日常
  • FRPP 管道:玻纤增强聚丙烯防腐管道的性能革新与工业应用 - 苏一塑业13914572689
  • 收藏!AI时代程序员必看:如何升级技能,避免被淘汰?
  • 进程异常退出,定位原因技巧
  • 核货宝加拿大版订货系统:助力华商简化订货流程,降低成本
  • 用 codex逆向拆解 20 张爆款电商主图:手把手教你建立高点击率视觉工作流
  • 2026 桂林厨卫屋面地下室漏水瓷砖空鼓测评:吉修匠 99.8 分五星榜首 - 吉修匠
  • 多水质工况实测 投入式液位计源头厂家TOP10 污水净水通用仪表 - 仪表人叶工
  • 北京26年甄选名猫猫狗狗宠物店权威排行榜店铺推荐,靠谱宠物店联系方式推荐 - 谊识预商贸
  • 计算机毕业设计之智能推荐系统在电商平台中的个性化设计与实现
  • 2026年上海美本申请规划成功案例:完整方案资料解读 - 虚拟星辰
  • 一体式超声波液位计多少钱?2026年十大品牌价格全解析与选型避坑指南 - 仪表品牌排行榜
  • 广东育家心理学研究院为什么要为休学孩子做“坤和静界·春藤计划”
  • 上海智慧食堂厂家排行:基于资质与落地案例的客观盘点 - 互联网科技品牌测评
  • 网络经纪人助手口碑全维度评测:合规性与服务力验证 - 奔跑123
  • 2026 年网站建设公司排行,综合实力盘点
  • 气膜为什么成为工业主流建筑?玖晟气膜拆解五大核心优势
  • 大模型一体机生产厂家:技术资质与落地能力双维度解析 - 奔跑123