首页 / 资讯中心 / 文章详情

仓颉版 ArkUI LazyForEach 数据懒加载指南:IDataSource 接口、DataChangeListener 与长列表按需渲染实践

仓颉版 ArkUI LazyForEach 数据懒加载指南:IDataSource 接口、DataChangeListener 与长列表按需渲染实践 ★ FEATURED ARTICLE
文档知识库教程OpenHarmony【免费下载链接】CangjieCorpus本仓库为仓颉编程语言语料库聚焦 仓颉编程语言 核心知识体系整合官方开发指南、API 文档、典型代码示例及语法规范等多维度资源经结构化处理与专业标注构建为支持 RAG检索增强生成技术 的高质量知识基座适配智能编码助手、学习辅助工具等应用开发需求。 欢迎开发者基于本语料库构建仓颉语言智能应用推动鸿蒙开发生态智能化升级项目地址https://gitcode.com/Cangjie/CangjieCorpus点击查看免费下载本文以 ArkUI 仓颉 API 参考文档 cj-state-rendering-lazyforeach.md 为主体系统讲解 LazyForEach 懒加载渲染组件的数据源接口IDataSourceT、数据变化监听器DataChangeListener、组件构造函数LazyForEachT的完整签名与使用规则并结合仓库中的配套使用指南 cj-rendering-control-lazyforeach.md 深入剖析键值生成规则、组件创建与销毁流程、九大使用限制及常见性能陷阱。读完本文你将能够在仓颉工程中正确实现自定义数据源、通过监听器通知完成增删改查与批量变更并写出流畅、低内存占用的长列表页面。LazyForEach 是什么大量子组件场景下的按需渲染方案LazyForEach 是 ArkUI方舟 UI 框架面向仓颉语言提供的数据懒加载组件。与一次性渲染全部数据的 ForEach 不同LazyForEach 从开发者提供的数据源中按需迭代数据并在每次迭代过程中创建相应的组件。当 LazyForEach 被放在滚动容器中时框架会根据可视区域动态创建组件组件滑入可视区域时被创建滑出可视区域时被销毁回收从而显著降低内存占用。在大量子组件的场景下LazyForEach 与缓存列表项cachedCount、动态预加载、组件复用等方法配合使用可以进一步提升滑动帧率并降低应用内存占用——这正是官方参考文档开篇强调的核心定位。从仓库中的列表开发指南 cj-layout-development-create-list.md 可以看到官方对使用场景的明确建议循环渲染ForEach适用于短列表当构建具有大量列表项的长列表时直接采用循环渲染会一次性加载所有列表元素导致页面启动时间过长此时推荐使用 LazyForEach 实现按需迭代加载数据以提升列表性能。在 ArkUI 仓颉 API 中LazyForEach 的使用依赖三部分协作一个由开发者实现的数据源IDataSourceT接口一个由框架回调的数据变化监听器DataChangeListener组件构造入口LazyForEachT通过itemGenerator生成子组件、通过keyGenerator生成键值。三者共同构成了数据驱动渲染、监听驱动刷新的完整闭环。导入模块使用 LazyForEach 及本文涉及的所有类型前需要导入 ArkUI 框架模块import kit.ArkUI.*参考文档位于 cj-state-rendering-lazyforeach.md其中kit.ArkUI.*提供了IDataSource、DataChangeListener、LazyForEach等全部声明。示例代码中还会用到标准集合库与状态管理宏import ohos.arkui.state_macro_manage.* import std.collection.*interface IDataSourceT开发者必须实现的数据源接口LazyForEach 不直接接收数组而是接收一个数据源对象。数据源必须实现IDataSourceT泛型接口接口定义见 cj-state-rendering-lazyforeach.mdpublic interface IDataSourceT { func totalCount(): Int64 func getData(Int64): T func registerDataChangeListener(DataChangeListener): Unit func unregisterDataChangeListener(DataChangeListener): Unit }该接口的系统能力为SystemCapability.ArkUI.ArkUI.Full起始版本为 22。四个方法的分工如下方法签名功能调用方totalCountfunc totalCount(): Int64获得数据总数框架侧渲染前确定迭代范围getDatafunc getData(index: Int64): T获取索引值 index 对应的数据框架侧按需取数registerDataChangeListenerfunc registerDataChangeListener(listener: DataChangeListener): Unit注册数据改变的监听器框架侧挂载时unregisterDataChangeListenerfunc unregisterDataChangeListener(listener: DataChangeListener): Unit注销数据改变的监听器框架侧卸载时func getData(Int64)func getData(index: Int64): T获取索引值 index 对应的数据返回类型为泛型 T。参数参数名类型必填默认值说明indexInt64是-数据对应的索引值。返回值索引值 index 对应的数据类型 T。func registerDataChangeListener(DataChangeListener)func registerDataChangeListener(listener: DataChangeListener): Unit注册数据改变的监听器。LazyForEach 在挂载时会调用该方法将框架内部的DataChangeListener实例交给数据源保存后续数据源通过该监听器向组件通报数据变化。参数参数名类型必填默认值说明listenerDataChangeListener是-数据变化监听器。func unregisterDataChangeListener(DataChangeListener)func unregisterDataChangeListener(listener: DataChangeListener): Unit注销数据改变的监听器。LazyForEach 卸载时会调用该方法数据源应释放对监听器的引用避免悬挂引用。参数参数名类型必填默认值说明listenerDataChangeListener是-数据变化监听器。func totalCount()func totalCount(): Int64获得数据总数。返回值数据总数Int64。LazyForEach 首次渲染时通过该方法确定迭代范围滚动过程中也需要它判断边界例如加载更多场景。class DataChangeListener数据变化监听器DataChangeListener是框架回调给数据源的监听器类定义见 cj-state-rendering-lazyforeach.mdpublic class DataChangeListener {}系统能力SystemCapability.ArkUI.ArkUI.Full起始版本22。它本身是一个空类真正发挥作用的是其上的五个通知方法分别对数据源的不同变更类型通知方法签名触发时机组件行为onDataAddonDataAdd(index: IntNative): Unit添加数据完成后在 index 位置新建子组件onDataChangeonDataChange(index: IntNative): Unit改变数据完成后在 index 位置重建子组件onDataDeleteonDataDelete(index: IntNative): Unit删除数据完成后删除 index 位置的子组件并刷新展示内容onDataMoveonDataMove(from: IntNative, to: IntNative): Unit移动数据完成后交换 from 与 to 位置的子组件onDataReloadedonDataReloaded(): Unit重新加载数据完成后逐项比对键值键值未变复用原子组件键值变化重建子组件func onDataAdd(IntNative)public func onDataAdd(index: IntNative): Unit功能通知组件 index 的位置有数据添加。添加数据完成后调用。参数名类型必填默认值说明indexIntNative是-数据添加位置的索引值。func onDataChange(IntNative)public func onDataChange(index: IntNative): Unit功能通知组件 index 的位置有数据变化。改变数据完成后调用。参数名类型必填默认值说明indexIntNative是-数据变化位置的索引值。func onDataDelete(IntNative)public func onDataDelete(index: IntNative): Unit功能通知组件删除 index 位置的数据并刷新 LazyForEach 的展示内容。删除数据完成后调用。参数名类型必填默认值说明indexIntNative是-数据删除位置的索引值。func onDataMove(IntNative, IntNative)public func onDataMove(from: IntNative, to: IntNative): Unit功能通知组件数据有移动将 from 和 to 位置的数据进行交换。数据移动起始位置与数据移动目标位置交换完成后调用。参数名类型必填默认值说明fromIntNative是-数据移动起始位置。toIntNative是-数据移动目标位置。func onDataReloaded()public func onDataReloaded(): Unit功能通知组件重新加载所有数据。键值没有变化的数据项会使用原先的子组件键值发生变化的会重建子组件。重新加载数据完成后调用。这是最重的刷新手段适用于批量修改无法用上述细粒度回调表达的场景但需注意其性能代价与潜在屏闪问题详见后文常见问题。class LazyForEachT组件构造入口LazyForEachT泛型类用于创建 LazyForEach 组件定义见 cj-state-rendering-lazyforeach.mdpublic class LazyForEachT { public init(dataSource: IDataSourceT, itemGenerator!: ItemGeneratorFuncT, keyGenerator!: ?KeyGeneratorFuncT None) }系统能力SystemCapability.ArkUI.ArkUI.Full起始版本22。init(IDataSourceT, ItemGeneratorFuncT, ?KeyGeneratorFuncT)public init(dataSource: IDataSourceT, itemGenerator!: ItemGeneratorFuncT, keyGenerator!: ?KeyGeneratorFuncT None)功能创建 LazyForEach 组件。三个参数中dataSource与itemGenerator为必填keyGenerator可省略。参数名类型必填默认值说明dataSourceIDataSourceT是-LazyForEach 数据源需要开发者实现相关接口。itemGeneratorItemGeneratorFuncT是-命名参数。子组件生成函数为数组中的每一个数据项创建一个子组件。lambda 函数的第一个泛型参数为数据类型第二个参数为当前列表项的索引值。keyGenerator?KeyGeneratorFuncT否None命名参数。匿名函数用于键值生成为给定数组项生成唯一且稳定的键值。两个函数类型均定义在 cj-common-types.mdpublic type ItemGeneratorFuncT (T, Int64) - Unit public type KeyGeneratorFuncT (T, Int64) - StringItemGeneratorFuncT(T, Int64) - Unit第一个参数是数据项本身第二个参数是该项当前索引。注意该函数要求每次迭代必须且只创建一个根子组件见下文使用限制。KeyGeneratorFuncT(T, Int64) - String根据数据项与索引返回一个字符串键值。键值用于标识组件是 LazyForEach 判断复用还是重建的依据。完整示例Student 列表的 LazyForEach 实战以下完整示例来自参考文档 cj-state-rendering-lazyforeach.md演示了数据源实现、监听器注册/注销以及通过按钮触发数据变更后刷新 UI 的完整链路package ohos_app_cangjie_entry import kit.ArkUI.* import ohos.arkui.state_macro_manage.* import std.collection.* public class Student { public Student( let name: String, let id: Int64 ) {} } class StudentDataSource : IDataSourceStudent { public StudentDataSource(let data_: ArrayListStudent) {} public var listenerOp: OptionDataChangeListener None public func totalCount(): Int64 { return data_.size } public func getData(index: Int64): Student { return data_[index] } public func registerDataChangeListener(listener: DataChangeListener): Unit { listenerOp listener } public func unregisterDataChangeListener(listener: DataChangeListener): Unit { listenerOp None } public func notifyChange(): Unit { let listener: DataChangeListener listenerOp.getOrThrow() listener.onDataReloaded() } } func getDS(): StudentDataSource { let data: ArrayListStudent ArrayListStudent() for (i in 0..10) { data.add(Student(name ${i}, i * i)) } let dataSourceStu: StudentDataSource StudentDataSource(data) return dataSourceStu } let dataSourceStu: StudentDataSource getDS() var changeID: Int64 0 Entry Component public class EntryView { public func build(): Unit { Column(space: 30) { Column { LazyForEach(dataSourceStu, itemGeneratorFunc: {stu: Student, idx: Int64 Column { Text(stu.name) } }) } .height(220.0) Text(click to notifyChange).onClick({ evt if (changeID dataSourceStu.data_.size) { dataSourceStu.data_.remove(at: changeID) dataSourceStu.data_.add(Student(xiaoming, 10086), at: changeID) dataSourceStu.notifyChange() changeID 1 } }) } } }逐段拆解这个示例数据模型Student类持有name与id两个不可变成员作为列表数据项。数据源实现StudentDataSource : IDataSourceStudent用ArrayListStudent承载数据用OptionDataChangeListener保存框架注册的监听器。totalCount()返回data_.sizegetData(index)返回data_[index]注册/注销监听器时分别赋值与置空listenerOp。通知入口notifyChange()从listenerOp取出监听器并调用onDataReloaded()触发整表重载。数据准备getDS()构造 10 条Student数据name 0到name 9id 为i * i。渲染LazyForEach(dataSourceStu, itemGeneratorFunc: {stu, idx Column { Text(stu.name) } })为每个Student生成一个ColumnText。本例未传keyGenerator走默认键值生成逻辑。变更驱动点击Text(click to notifyChange)后先移除changeID位置的数据再在该位置插入Student(xiaoming, 10086)最后调用notifyChange()触发onDataReloaded()LazyForEach 依据键值比对决定复用或重建子组件。示例中notifyChange()使用的是全量重载策略实际工程中应根据数据变更粒度选择onDataAdd/onDataChange/onDataDelete/onDataMove等更精细的通知避免不必要的整表重建详见下文通知机制。键值生成规则LazyForEach 复用的核心依据在 LazyForEach 循环渲染过程中系统会为每个 item 生成一个唯一且持久的键值用于标识对应的组件。当键值变化时ArkUI 框架将视为该数组元素已被替换或修改并基于新的键值创建一个新组件。keyGenerator参数允许开发者自定义键值生成规则。若开发者未定义keyGenerator框架使用默认键值生成函数{data: T, idx: Int64 return ${viewID} - ${idx} - ${uniqueKey_}}其中viewID在编译器转换过程中生成同一个 LazyForEach 组件内的viewID是一致的。这意味着默认键值会耦合索引与内部唯一计数数据删除/插入导致索引变化时键值也会随之变化从而引发组件重建。键值规则直接影响两个关键行为首次渲染为数据源每个数组项生成唯一键值并创建对应组件非首次渲染当数据源变化后框架将新旧数据项的键值逐一比对——键值相同则复用原有子组件含其内部状态键值不同则销毁并重建。因此keyGenerator应尽量基于数据项本身的稳定标识如业务 ID生成键值而不是依赖会随增删而漂移的索引。键值生成器必须针对每个数据生成唯一的值如果键值相同将导致键值相同的 UI 组件渲染出现问题框架行为不可预测详见使用限制与常见问题。使用限制九条必须遵守的约束配套使用指南 cj-rendering-control-lazyforeach.md 列出了 LazyForEach 的完整使用限制以下九条直接影响代码能否正确运行必须在容器组件内使用。仅有 List、Grid、Swiper 组件支持数据懒加载可配置cachedCount属性即只加载可视部分及其前后少量数据用于缓冲其他容器仍然是一次性加载所有数据。也就是说只有放入这三个滚动容器LazyForEach 的懒加载才真正生效。LazyForEach 依赖键值判断是否刷新子组件。若键值不发生改变则无法触发 LazyForEach 刷新对应的子组件。容器组件内只能包含一个 LazyForEach。以 List 为例同时包含 ListItem、ForEach、LazyForEach 的情形不推荐同时包含多个 LazyForEach 也不推荐。每次迭代必须创建且只允许创建一个子组件itemGenerator生成函数中必须且只能有一个根组件。生成的子组件必须是允许包含在父容器组件中的子组件例如 List 内应生成 ListItem、Grid 内应生成 GridItem。允许 LazyForEach 包含在 if/else 条件渲染语句中也允许 LazyForEach 内部出现 if/else 条件渲染语句。键值生成器必须针对每个数据生成唯一的值键值相同会导致 UI 组件渲染出现问题。必须通过 DataChangeListener 对象更新数据对第一个参数dataSource重新赋值会异常dataSource使用状态变量时状态变量改变不会触发 LazyForEach 的 UI 刷新。这是与 ForEach 最大的差异——LazyForEach 的刷新完全依赖监听器回调。高性能更新的两条细则通过onDataChange更新 UI 时需要生成不同于原来的键值来触发组件刷新LazyForEach 必须和Reusable装饰器一起使用才能触发节点复用。使用方法见 cj-macro-reusable.md将Reusable装饰在 LazyForEach 列表的子组件上。此外cachedCount是 List 提供的预加载数量参数见 cj-scroll-swipe-list.md懒加载场景下只预加载 List 显示区域外cachedCount的内容非懒加载场景会全部加载设置后显示区域外上下各会预加载并布局cachedCount行 ListItem。该参数在列表滚动体验与内存占用之间提供权衡。数据变更通知的工程化封装BasicDataSource参考文档示例中的StudentDataSource只持有一个监听器实际项目中一个数据源可能被多个组件监听。配套指南附件给出了通用封装 BasicDataSourcepublic open class BasicDataSourceT : IDataSourceT { public BasicDataSource(let data_: ArrayListT) {} public var listenerOp: ArrayListDataChangeListener ArrayListDataChangeListener() public func totalCount(): Int { return data_.size } public func getData(index: Int): T { return data_[index] } // 该方法为框架侧调用为LazyForEach组件向其数据源处添加listener监听 public func onRegisterDataChangeListener(listener: DataChangeListener): Unit { for (listeneritem in listenerOp) { if (refEq(listeneritem, listener)) { return } } listenerOp.add(listener) } // 该方法为框架侧调用为对应的LazyForEach组件在数据源处去除listener监听 public func onUnregisterDataChangeListener(listener: DataChangeListener): Unit { var index 0 while (index listenerOp.size) { let listeneritem listenerOp[index] if (refEq(listeneritem, listener)) { listenerOp.remove(at: index) } else { index } } } // 通知LazyForEach组件需要重载所有子组件 public func notifyDataReload(): Unit { for (listeneritem in listenerOp) { listeneritem.onDataReloaded() } } // 通知LazyForEach组件在index对应索引处数据有变化需要重建该子组件 public func notifyDataChange(index: Int64): Unit { for (listeneritem in listenerOp) { listeneritem.onDataChange(index) } } // 通知LazyForEach组件需要在index对应索引处添加子组件 public func notifyDataAdd(index: Int64): Unit { for (listeneritem in listenerOp) { listeneritem.onDataAdd(index) } } // 通知LazyForEach组件需要在index对应索引处删除该子组件 public func notifyDataDelete(index: Int64): Unit { for (listeneritem in listenerOp) { listeneritem.onDataDelete(index) } } // 通知LazyForEach组件将from索引和to索引处的子组件进行交换 public func notifyDataMove(from: Int64, to: Int64): Unit { for (listeneritem in listenerOp) { listeneritem.onDataMove(from, to) } } public func notifyDatasetChange(operations: ArrayListDataOperation): Unit { for (listeneritem in listenerOp) { listeneritem.onDatasetChange(operations) } } }从源码结构可以看出这个封装的工程价值监听器集合管理用ArrayListDataChangeListener维护多个监听器注册时通过refEq引用判等去重注销时按引用移除——避免重复注册导致的通知风暴。六个通知方法notifyDataReload、notifyDataChange、notifyDataAdd、notifyDataDelete、notifyDataMove、notifyDatasetChange分别对应对应回调向所有已注册监听器广播变更。子类只需组合业务逻辑业务子类如MyDataSource在完成ArrayList的实际增删改后调用对应的notifyXxx即可驱动 LazyForEach 精准刷新与渲染逻辑解耦。基于该封装各变更场景的标准写法是业务操作数据层动作通知方法LazyForEach 行为添加数据data.add(str)notifyDataAdd(size - 1)在末尾新建子组件删除数据data.remove(at: index)notifyDataDelete(index)删除对应子组件交换数据交换data[from]/data[to]notifyDataMove(from, to)调换 from/to 子组件位置改变单个数据data[index] strnotifyDataChange(index)重建该索引子组件改变多个数据批量修改后notifyDataReload()全量键值比对后重建精准批量修改组合操作后notifyDatasetChange(operations)按 operations 精准增删移常见问题与规避方案问题一删除数据后删错——渲染结果非预期现象在 List 中使用 LazyForEach 渲染数据点击某一子组件删除对应数据多次点击后发现删除的并不一定是单击的那个子组件。原因删除一个子组件后位于其后的数据项索引均应减 1但后续子组件的itemGenerator中拿到的 index 仍是最初分配的旧索引索引与数据错位导致删除结果与预期不符问题复现与修复完整代码见 cj-rendering-control-lazyforeach.md。修复方案删除数据后调用notifyDataReload()重建后续数据项以刷新 index 索引// 点击删除子组件 this.dataSource.deleteData(index) // 重置所有子组件的index索引 this.dataSource.reloadData()注意reloadData要生效必须保证被删除项之后的数据项能生成新的 key。使用item index.toString()可保证仅重建被删除项之后的数据项若改用item DateTime.now().toString()之类的时间戳键值则所有数据项都会重建——效果相同但性能略差。问题二List 中 onScrollIndex 内调用 onDataReloaded 产生屏幕闪烁现象在 List 的onScrollIndex回调中当滚动到底部时追加数据并调用onDataReloaded()屏幕出现闪烁。原因onDataReloaded()是全量重载语义在滚动高频回调过程中触发整表重建会造成明显的视觉闪烁与性能开销。修复方案改用notifyDatasetChangeonDatasetChange进行精准批量追加仅通知 LazyForEach 在指定位置插入新数据public func operateData(): Unit { let totalCount this.data.size let batch 5 for (i in totalCount..totalCountbatch) { this.data.add(Hello ${i}) } this.notifyDatasetChange(ArrayListDataOperation([ DataAddOperation(Int32(totalCount - 1), count: Int32(batch), key: , keys: []) ])) }指南明确指出用onDatasetChange代替onDataReloaded不仅可以修复闪屏问题还能提升加载性能。问题三onDatasetChange 批量操作的注意事项onDatasetChange(operations: ArrayListDataOperation)用于一次性表达多个数据操作DataMoveOperation、DataExchangeOperation、DataAddOperation、DataDeleteOperation等使用时必须遵守以下规则详见 cj-rendering-control-lazyforeach.mdonDatasetChange与其它操作数据的接口不能混用传入的每个 operation 的 index均从修改前的原数组内寻找因此 operations 中的 index 与操作后 Datasource 中的 index 不总是一一对应且不能是负数调用一次onDatasetChange一个 index 对应的数据只能被操作一次若被操作多次LazyForEach 仅使第一个操作生效部分操作可以由开发者传入键值此时 LazyForEach 不会重复调用 keyGenerator 获取键值需要开发者保证传入键值的正确性若本次操作集合中有 RELOAD 操作则其余操作全不生效。与 ForEach 的差异及状态管理配合LazyForEach 与 ForEach 的定位差异ForEach见 cj-state-rendering-foreach.md基于CollectionExT数组类型数据进行循环渲染一次性创建全部子组件适合短列表。LazyForEach 则按需迭代适合长列表。两者在使用方式上有本质区别ForEach 直接接收数组CollectionExT数组变化时依赖状态管理机制触发刷新LazyForEach 接收IDataSourceT数据源必须通过 DataChangeListener 通知刷新对dataSource重新赋值或修改状态变量均不能触发 UI 刷新。改变数据子属性Observed 与 Link 深度观测仅靠 LazyForEach 的键值刷新机制item 变化时若想更新子组件需要销毁并重建整个子组件在子组件结构复杂时靠改变键值刷新渲染性能较低。框架因此提供了Observed与Link机制进行深度观测可做到仅刷新使用了该属性的组件示例见 cj-rendering-control-lazyforeach.mdObserved public class StringData { Publish var message: String } Component class ChildComponent { Link var data: StringData func build() { Row() { Text(this.data.message).fontSize(50) } } }当点击子组件改变item.message时重渲染依赖的是ChildComponent的Link成员变量对其子属性的监听——此时框架只会刷新Text(this.data.message)不会重建整个 ListItem 子组件。开发者可根据业务特点在改键值重建与深度观测局部刷新两种方式间取舍。数组类状态管理ObservedArrayList若数据源本身需要随状态管理自动感知变化可参考组件级状态管理文档 cj-state-rendering-componentstatemanagement.md 中的ObservedArrayList它是实现CollectionExT的状态管理数组类型当其中数组发生变化修改某项值、删除或添加一项时触发 UI 更新可作为 LazyForEach 数据源内部数组的增强替代。总结LazyForEach 的完整实践要点数据源是核心实现IDataSourceT的四个方法totalCount/getData/registerDataChangeListener/unregisterDataChangeListener并在业务变更后通过DataChangeListener的五个回调onDataAdd/onDataChange/onDataDelete/onDataMove/onDataReloaded通知刷新。键值决定复用keyGenerator应生成唯一且稳定的键值键值不变则不刷新键值相同则渲染出错。容器决定懒加载只有 List / Grid / Swiper 配合cachedCount才真正按需加载一个容器内只放一个 LazyForEachitemGenerator只创建一个根组件。更新不走状态变量dataSource重新赋值或状态变量变化都不会触发刷新必须走监听器通知追求性能时用onDatasetChange精准批量更新避免在onScrollIndex等高频路径调用onDataReloaded。组合使用更优LazyForEach cachedCount预加载 Reusable组件复用 Observed/Link深度观测是官方推荐的大列表高性能组合。参考文档入口cj-state-rendering-lazyforeach.md配套使用指南cj-rendering-control-lazyforeach.md。赞分享文档知识库教程OpenHarmony【免费下载链接】CangjieCorpus本仓库为仓颉编程语言语料库聚焦 仓颉编程语言 核心知识体系整合官方开发指南、API 文档、典型代码示例及语法规范等多维度资源经结构化处理与专业标注构建为支持 RAG检索增强生成技术 的高质量知识基座适配智能编码助手、学习辅助工具等应用开发需求。 欢迎开发者基于本语料库构建仓颉语言智能应用推动鸿蒙开发生态智能化升级项目地址https://gitcode.com/Cangjie/CangjieCorpus点击查看免费下载相关推荐LazyForEach 数据懒加载仓颉 ArkUI 大数据量列表渲染优化实战指南LazyForEach 数据懒加载仓颉 ArkUI 大数据量列表渲染优化实战指南 LazyForEach 是仓颉 ArkUI 框架中面向大数据量场景的核心渲染文档知识库教程OpenHarmony仓颉 ArkUI List 列表开发实战从静态列表到分组、懒加载与滚动交互仓颉 ArkUI List 列表开发实战从静态列表到分组、懒加载与滚动交互 本文是面向 OpenHarmony 应用开发的仓颉CangjieArkUI 布文档知识库教程OpenHarmony旧电脑提速完整指南换SSD、加内存、调系统5步让老机器跑满速旧电脑提速完整指南换SSD、加内存、调系统5步让老机器跑满速 想让用了八年的老电脑提速先换SSD、再加内存最后用系统瘦身和电源调校把性能榨干。开源项目桌面应用操作系统固件上一篇PaddleNLP 多语言段落重排序模型 amberoad/bert-multilingual-passage-reranking-msmarco模型文件下载与实战使用指南下一篇VeighNavnpy开源量化交易框架贡献指南从创建 PR 到通过代码质量检查创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站