2026/10/16 2:33:20

仓颉ArkUI @Reusable宏:自定义组件复用机制与高性能列表实践指南

仓颉ArkUI @Reusable宏:自定义组件复用机制与高性能列表实践指南 文档知识库教程OpenHarmony【免费下载链接】CangjieCorpus本仓库为仓颉编程语言语料库聚焦 仓颉编程语言 核心知识体系整合官方开发指南、API 文档、典型代码示例及语法规范等多维度资源经结构化处理与专业标注构建为支持 RAG检索增强生成技术 的高质量知识基座适配智能编码助手、学习辅助工具等应用开发需求。 欢迎开发者基于本语料库构建仓颉语言智能应用推动鸿蒙开发生态智能化升级项目地址https://gitcode.com/Cangjie/CangjieCorpus点击查看免费下载本指南聚焦鸿蒙 ArkUI仓颉声明式开发范式中的Reusable宏讲解如何让自定义组件在从组件树移除后进入复用缓存并在下次创建时直接复用节点以节约创建开销。你将掌握Reusable的使用限制、aboutToReuse/aboutToRecycle生命周期回调、ReuseParams参数传递以及动态布局更新、LazyForEach长列表滚动、if条件渲染三类实战场景的完整仓颉代码方案。概述什么是组件复用Reusable宏装饰任意自定义组件时表示该自定义组件可以复用。它适用于自定义组件需与[Component](https://link.gitcode.com/i/b6ecaaa1f985fc21016d6d376667d78d)结合使用。其核心机制为标记为Reusable的自定义组件从组件树上被移除时组件和其对应的页面对象都会被放入复用缓存中后续创建新自定义组件节点时会复用缓存区中的节点从而节约组件重新创建的时间。在 ArkUI 中Reusable被定位为自定义组件的一种能力装饰宏与Component、Entry等并列具体用法在 创建自定义组件 文档中有如下声明Reusable Component class MyComponent { }也就是说一个普通自定义组件只要在其Component上方追加Reusable便具备了被复用缓存的能力。限制条件使用Reusable前需要明确以下限制否则可能引发编译报错或运行时生命周期异常。暂时不支持混合工程当前版本中Reusable暂时不支持混合工程即仓颉与 ArkTS 混编的工程。仅用于自定义组件Reusable仅用于自定义组件不能用于Builder定义的构建函数。以下示例中若将Reusable与Builder同时使用编译会直接报错package ohos_app_cangjie_entry import kit.ArkUI.* import ohos.arkui.state_management.* import ohos.arkui.state_macro_manage.* // Builder加上Reusable编译报错不适用于builder // Reusable Builder func MyBuilder() { Crash() } Component public class Crash { public func build() { Column() { Text(Crash) .fontSize(12) .lineHeight(8) .fontColor(Color.Blue) } .width(100.percent) .height(100.percent) .justifyContent(FlexAlign.Center) } } Entry Component public class EntryView { public func build(): Unit { Column() { MyBuilder() } } }Reusable 不支持嵌套使用Reusable宏不支持嵌套使用原因在于嵌套使用会带来内存增加和不方便维护的问题。官方说明中特别指出不支持嵌套使用只是标记会多增加一个缓存池各自的复用缓存池存在相同树状结构复用效率低引发复用内存增加嵌套使用形成各自独立的复用缓存池之后生命周期的传递存在问题资源和变量管理无法共享并不方便维护容易引发问题示例中PlayButton形成的复用缓存池并不能在PlayButton02的复用缓存池使用但PlayButton02自己形成复用缓存相互可以使用在PlayButton隐藏时已经触发PlayButton02的aboutToRecycle但是在PlayButton02单独显示时却无法执行aboutToReuse组件复用的生命周期方法存在无法成对调用问题综上不建议嵌套使用。以下示例展示了父子嵌套复用组件导致生命周期回调无法成对出现的问题运行日志Hilog.info可以观察到aboutToRecycle与aboutToReuse的错配package ohos_app_cangjie_entry import kit.ArkUI.* import ohos.arkui.state_macro_manage.* import kit.PerformanceAnalysisKit.Hilog Entry Component public class EntryView { State var isPlaying: Bool false State var isPlaying01: Bool true State var isPlaying02: Bool false public func build() { Column() { if (this.isPlaying02) { // 初始态是显示的按钮 Text(Default shown childbutton).fontSize(14) PlayButton02(isPlaying02: isPlaying02) } Text().fontSize(14) if (this.isPlaying01) { // 初始态是隐藏的按钮 Text(Default hidden childbutton).fontSize(14) PlayButton02(isPlaying02: isPlaying01) } Text().fontSize(14) // 父子嵌套 if (this.isPlaying) { Text(parent child 嵌套).fontSize(14) PlayButton(buttonPlaying: isPlaying) } Text().fontSize(14) // 父子嵌套控制 Text(Parentchildis ${if(isPlaying) {} else {not}} playing).fontSize(14) Button(Parentchildcontroll${isPlaying}).margin(14).onClick({ e isPlaying !isPlaying }) Text().fontSize(14) // 默认隐藏按钮控制 Text(Hiddenchildis ${if(isPlaying01) {} else {not}} playing).fontSize(14) Button(Buttonhiddenchildcontrol${isPlaying01}).margin(14).onClick({ e isPlaying01 !isPlaying01 }) Text().fontSize(14) // 默认显示按钮控制 Text(shownchidis ${if(isPlaying02) {} else {not}} playing).fontSize(14) Button(Buttonshownchidcontrol${isPlaying02}).margin(14).onClick({ e isPlaying02 !isPlaying02 }) } } } // 复用1 Reusable Component class PlayButton { Link var buttonPlaying: Bool func build() { Column() { // 复用 PlayButton02(isPlaying02: buttonPlaying) Button(if(buttonPlaying) {parent_pause} else {parent_play}).margin(12).onClick({ e buttonPlaying !buttonPlaying }) } } } // 复用2 不建议嵌套使用 Reusable Component class PlayButton02 { Link var isPlaying02: Bool protected override func aboutToRecycle(): Unit { Hilog.info(0, cangjie, aboutToRecyclePlayButton02) } protected override func aboutToReuse(param: ReuseParams) { Hilog.info(0, cangjie, aboutToReusePlayButton02) } func build() { Column() { Button(commonbutton).margin(12) } } }使用场景Reusable主要面向以下三类高频创建 / 销毁组件的性能敏感场景列表滚动当应用需要展示大量数据的列表并且用户进行滚动操作时频繁创建和销毁列表项的视图可能导致卡顿和性能问题。在这种情况下使用列表组件的组件复用机制可以重用已经创建的列表项视图提高滚动的流畅度。动态布局更新如果应用中的界面需要频繁地进行布局更新例如根据用户的操作或数据变化动态改变视图结构和样式重复创建和销毁视图可能导致频繁的布局计算影响帧率。在这种情况下使用组件复用可以避免不必要的视图创建和布局计算提高性能。频繁创建和销毁数据项的视图场景下使用组件复用可以重用已创建的视图只更新数据的内容减少视图的创建和销毁能有效提高性能。其中“列表滚动”场景通常与[LazyForEach数据懒加载](https://link.gitcode.com/i/46f837b967faedba752fe769cef94ee4)配套使用。LazyForEach 文档明确要求“为了高性能渲染……LazyForEach必须和Reusable装饰器一起使用才能触发节点复用”并将Reusable装饰在 LazyForEach 列表的组件上作为使用规则。使用场景举例下面通过三个可运行的完整示例演示Reusable在不同场景中的具体落地方式。三个示例均通过aboutToReuse生命周期回调从ReuseParams中取出构造参数并刷新状态是组件复用的标准范式。动态布局更新示例代码将Child自定义组件标记为复用组件通过Button点击更新Child触发Child复用。核心要素如下Reusable自定义组件被Reusable宏修饰即表示其具备组件复用的能力aboutToReuse当一个可复用的自定义组件从复用缓存中重新加入到节点树时触发aboutToReuse生命周期回调并将组件的构造参数传递给aboutToReuse。package ohos_app_cangjie_entry import kit.ArkUI.* import ohos.arkui.state_macro_manage.* import kit.PerformanceAnalysisKit.Hilog public class Message { public var value: String public init(val: String) { value val } } Entry Component public class EntryView { State var switch: Bool true public func build() { Column() { Button(Hello) .fontSize(30) .fontWeight(FontWeight.Bold) .onClick({evt switch !switch }) if (switch) { Child(message: Message(Child)) } }.height(100.percent).width(100.percent) } } Reusable Component class Child { State var message: Message Message(about to reuse) protected override func aboutToReuse(params: ReuseParams) { if (let Some(value) - params.getMessage(message)) { message value as Message ?? Message(None) Hilog.info(0, cangjie, Recycle Child) } } func build() { Column() { Text(this.message.value) }.borderWidth(1).height(100) } }运行流程点击“Hello”按钮后switch在true/false间翻转if分支反复创建和销毁Child。由于Child被Reusable标记销毁时组件进入复用缓存再次创建时直接从缓存取出仅通过aboutToReuse更新message数据即可完成展示。列表滚动配合LazyForEach使用示例代码将CardView自定义组件标记为复用组件List上下滑动触发CardView复用。关键要点Reusable自定义组件被Reusable装饰器修饰即表示其具备组件复用的能力变量item被State修饰才能更新非State修饰变量存在无法更新问题。package ohos_app_cangjie_entry import kit.ArkUI.* import ohos.arkui.state_management.* import ohos.arkui.state_macro_manage.* import std.collection.ArrayList import kit.PerformanceAnalysisKit.Hilog class MyDataSource : IDataSourceInt64 { public MyDataSource(let data_: ArrayListInt64) {} public var listenerOp: OptionDataChangeListener None public func totalCount(): Int64 { return data_.size } public func getData(index: Int64): Int64 { return data_[index] } public func pushData(val: Int64): Unit { data_.add(val) } public func registerDataChangeListener(listener: DataChangeListener): Unit { listenerOp listener } public func unregisterDataChangeListener(listener: DataChangeListener): Unit { listenerOp None } } Entry Component public class EntryView { let data: MyDataSource MyDataSource(ArrayListInt64([])) protected override func aboutToAppear() { for (i in 0..1000) { data.pushData(i) } } public func build(): Unit { Column() { List() { LazyForEach( data, itemGeneratorFunc: { item: Int64, idx: Int64 ListItem() { CardView(item: ${item}) } } ) } } } } // 复用组件 Reusable Component class CardView { State var item: String protected override func aboutToReuse(params: ReuseParams) { if (let Some(value) - params.getString(item)) { item value Hilog.info(0, cangjie, Recycle Child) } } func build() { Column() { Text(item) }.borderWidth(1).height(100) } }该示例向数据源注入了 1000 条数据LazyForEach按可视区域懒加载ListItem。滑动过程中滑出屏幕的CardView被放入复用缓存滑入屏幕的新列表项直接复用缓存节点aboutToReuse通过params.getString(item)拿到新数据并刷新避免重复创建组件实例。if使用场景示例代码将OneMoment自定义组件标记为复用组件List上下滑动触发OneMoment复用。该示例模拟了朋友圈信息流场景数据项同时包含文本与可选图片资源?AppResource在aboutToReuse中整体替换FriendMoment对象package ohos_app_cangjie_entry import kit.ArkUI.* import ohos.arkui.state_management.* import ohos.arkui.state_macro_manage.* import std.collection.ArrayList import kit.PerformanceAnalysisKit.Hilog import kit.LocalizationKit.* import ohos.resource.__GenerateResource__ class MyDataSource : IDataSourceFriendMoment { public MyDataSource(let data_: ArrayListFriendMoment) {} public var listenerOp: OptionDataChangeListener None public func totalCount(): Int64 { return data_.size } public func getData(index: Int64): FriendMoment { return data_[index] } public func pushData(val: FriendMoment): Unit { data_.add(val) } public func registerDataChangeListener(listener: DataChangeListener): Unit { listenerOp listener } public func unregisterDataChangeListener(listener: DataChangeListener): Unit { listenerOp None } } public class FriendMoment { public var text: String public var title: String public var image: ?AppResource None public init(text: String, title: String, image: ?AppResource) { this.text text this.title title this.image image } } Reusable Component public class OneMoment { State var moment: FriendMoment FriendMoment(, , r(app.media.startIcon)) protected override func aboutToReuse(params: ReuseParams) { if (let Some(value) - params.getFriendMoment(moment)) { let pVal value this.moment pVal Hilog.info(0, cangjie, aboutToReuseOnMoment复用了 ${pVal.text}) } } public func build() { Column() { Text(moment.text) if (moment.image.isSome()) { Flex(wrap: FlexWrap.Wrap) { Image((moment.image) ?? r(app.media.background)).height(50).width(50) Image((moment.image) ?? r(app.media.background)).height(50).width(50) Image((moment.image) ?? r(app.media.background)).height(50).width(50) Image((moment.image) ?? r(app.media.background)).height(50).width(50) } } } } } Entry Component public class EntryView { let data: MyDataSource MyDataSource(ArrayListFriendMoment([])) protected override func aboutToAppear() { for (i in 0..20) { let title ${i1}testif data.pushData(FriendMoment(${i}, title, r(app.media.startIcon))) } for (i in 0..50) { let title ${i1}testif data.pushData(FriendMoment(${i}, title, OptionAppResource.None)) } } public func build(): Unit { Column() { List() { LazyForEach( data, itemGeneratorFunc: { item: FriendMoment, idx: Int64 ListItem() { OneMoment(moment: item) } } ) } } } }该示例特别之处在于FriendMoment.image是可空类型?AppResource前 20 条数据携带图片、后 50 条数据不带图片。OneMoment.build()内部通过if (moment.image.isSome())决定是否渲染图片区域展示了复用组件在不同数据形态之间切换时State状态随aboutToReuse更新后驱动 UI 正确刷新的能力。源码级原理解析生命周期回调与ReuseParamsReusable的复用得逞于两个生命周期回调与一个参数容器类型其 API 定义见 自定义组件的生命周期 与 通用类型 文档。aboutToReuse 生命周期回调protected open func aboutToReuse(_: ReuseParams): Unit功能当一个可复用的自定义组件从复用缓存中重新加入到节点树时触发aboutToReuse生命周期回调并将组件的构造参数传递给aboutToReuse。参数说明参数名类型必填默认值说明_ReuseParams是-自定义组件的构造参数。aboutToRecycle 生命周期回调protected open func aboutToRecycle(): Unit功能组件的生命周期回调在可复用组件从组件树上被加入到复用缓存之前调用。它相当于“进入缓存”的入口钩子与aboutToReuse“从缓存取出”构成一对生命周期方法。正因如此前一节嵌套复用示例中“aboutToRecycle与aboutToReuse无法成对调用”才会引发资源与变量管理问题。ReuseParams 参数容器aboutToReuse的入参ReuseParams定义于 通用类型public class ReuseParams { public init(arr: Array(String, Any)) }init(arr: Array(String, Any))创建一个ReuseParams对象存放组件构造参数的元组数组通常情况下开发者不会调用getT(key: String): ?T通过key获取对应的构造参数返回值类型为?T。这也是所有示例中params.getMessage(message)、params.getString(item)、params.getFriendMoment(moment)调用所对应的底层实现。从源码结构看复用机制的完整调用链为组件被移除 → 触发aboutToRecycle→ 组件及其页面对象进入复用缓存 → 新节点创建请求到达 → 从缓存取出组件 → 触发aboutToReuse(ReuseParams)→ 在回调中通过params.getT(key)取回构造参数并更新State状态 →build()按新状态渲染。整个过程绕过了自定义组件的“创建实例 初始化成员变量 aboutToAppear”流程这正是复用能够节约创建时间的原因所在。使用建议与注意事项综合以上原理与示例在实际工程中使用Reusable时建议遵循以下规则只对叶子级、无嵌套的组件启用复用避免Reusable组件内部再嵌套Reusable组件防止复用缓存池树状结构重叠、生命周期回调无法成对调用。复用组件的数据成员务必用State修饰aboutToReuse中更新的数据需要驱动 UI 刷新非状态变量无法触发重渲染导致界面显示旧数据。配合LazyForEach使用以获得最佳收益长列表滚动是复用收益最明显的场景LazyForEach按需创建 Reusable节点复用可显著降低滑动卡顿相关限制如键值唯一性、DataChangeListener通知机制参见 LazyForEach数据懒加载。在aboutToReuse中完成数据刷新与埋点该回调是复用组件与新数据对接的唯一入口同时可在此处通过Hilog输出日志辅助排查复用是否生效。关注与页面生命周期的配合复用组件同样遵循自定义组件生命周期规则关于aboutToAppear、onDidBuild、aboutToDisappear等回调的完整流程可参考 页面和自定义组件生命周期以便在复用之外正确管理组件的创建、渲染与销毁时机。赞分享文档知识库教程OpenHarmony【免费下载链接】CangjieCorpus本仓库为仓颉编程语言语料库聚焦 仓颉编程语言 核心知识体系整合官方开发指南、API 文档、典型代码示例及语法规范等多维度资源经结构化处理与专业标注构建为支持 RAG检索增强生成技术 的高质量知识基座适配智能编码助手、学习辅助工具等应用开发需求。 欢迎开发者基于本语料库构建仓颉语言智能应用推动鸿蒙开发生态智能化升级项目地址https://gitcode.com/Cangjie/CangjieCorpus点击查看免费下载相关推荐仓颉ArkUI中的Builder宏自定义构建函数的UI复用机制与参数传递实战指南仓颉ArkUI中的Builder宏自定义构建函数的UI复用机制与参数传递实战指南 Builder宏是仓颉ArkUI提供的一种轻量级UI元素复用机制它允许文档知识库教程OpenHarmony仓颉 ArkUI BuilderParam 宏详解为自定义组件注入 Builder 构建内容仓颉 ArkUI BuilderParam 宏详解为自定义组件注入 Builder 构建内容 BuilderParam 是仓颉 ArkUI 中用于承接文档知识库教程OpenHarmony仓颉 ArkUI 状态管理之 State 宏组件内状态变量的定义、观察与刷新机制仓颉 ArkUI 状态管理之 State 宏组件内状态变量的定义、观察与刷新机制 在仓颉Cangjie语言的声明式 ArkUI 编程框架中UI 是程序文档知识库教程OpenHarmony上一篇AO3镜像站终极访问指南3步解锁全球同人作品宝库下一篇XUnity Auto Translator打破语言障碍畅玩全球Unity游戏创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考