【鸿蒙应用开发】性能优化

devtools/2025/2/28 2:20:29/

渲染方面

Repeat:可复用的循环渲染

Repeat 一般会用于取代 ForEach,相较后者具有更强的渲染性能,Repeat 具有两种工作模式:

non-virtualScroll 模式

在初始化页面时就加载列表中的全部子组件。

相比于ForEach,具有一定的性能优化,一方面针对数据的更新性能进行了优化,其次是生成函数中的索引管理转移到了框架层。

virtualScroll 模式

需要开启 virtualScroll 开关,它属于动态加载机制,通过可视区域 + 预加载区域来加载子组件,当你的容器滑动或者数组元素改变时,它会重新计算加载返回,帮助你管理节点的创建和销毁。

适合场景

懒加载长数据列表,希望通过组件复用来优化性能表现的场景。

示例代码
// 在List容器组件中使用Repeat virtualScroll模式
@Entry
@ComponentV2 // 推荐使用V2装饰器
struct RepeatExample {@Local dataArr: Array<string> = []; // 数据源aboutToAppear(): void {for (let i = 0; i < 50; i++) {this.dataArr.push(`data_${i}`); // 为数组添加一些数据}}build() {Column() {List() {Repeat<string>(this.dataArr).each((ri: RepeatItem<string>) => { // 默认模板ListItem() {Text('each_A_' + ri.item).fontSize(30).fontColor(Color.Red) // 文本颜色为红色}}).key((item: string, index: number): string => item) // 键值生成函数.virtualScroll({ totalCount: this.dataArr.length }) // 打开virtualScroll模式,totalCount 为期望加载的数据长度}.cachedCount(2) // 容器组件的预加载区域大小.height('70%').border({ width: 1 }) // 边框}}
}

Repeat:可复用的循环渲染https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-new-rendering-control-repeat-V5

LazyForEach:数据懒加载

按需加载迭代数据,框架根据滚动容器的可视范围来按需创建组件,当组件滑出可视区域外时,框架会销毁部分组件来降低内存占用。

首次渲染

首次渲染时,根据键值生成规则为数据源的每个数组元素生成唯一键值,并创建组件。

/** BasicDataSource代码见文档末尾附件: string类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: string[] = [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public pushData(data: string): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}@Entry
@Component
struct MyComponent {private data: MyDataSource = new MyDataSource();aboutToAppear() {for (let i = 0; i <= 20; i++) {this.data.pushData(`Hello ${i}`)}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string) => {ListItem() {Row() {Text(item).fontSize(50).onAppear(() => {console.info("appear:" + item)})}.margin({ left: 10, right: 10 })}}, (item: string) => item)}.cachedCount(5)}
}
非首次渲染

当数据发生变化,再次引发渲染时,应正确处理和调用对应的接口(通知LazyForEach更新),以下是当数据源内新增数据时的处理。

/** BasicDataSource代码见文档末尾附件: string类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: string[] = [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public pushData(data: string): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}@Entry
@Component
struct MyComponent {private data: MyDataSource = new MyDataSource();aboutToAppear() {for (let i = 0; i <= 20; i++) {this.data.pushData(`Hello ${i}`)}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string) => {ListItem() {Row() {Text(item).fontSize(50).onAppear(() => {console.info("appear:" + item)})}.margin({ left: 10, right: 10 })}.onClick(() => {// 点击追加子组件this.data.pushData(`Hello ${this.data.totalCount()}`);})}, (item: string) => item)}.cachedCount(5)}
}

当我们点击组件时,调用数据源的 pushData 方法,该方法内的实现中包含了 notifyDataAdd 方法,这会通知 LazyForEach 有数据增加,LazyForEach 随即新建子组件。

适合场景

按需加载数据的场景,优化容器组件内存使用量的场景。

LazyForEach:数据懒加载https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-rendering-control-lazyforeach-V5

多线程

使用多线程来优化密集计算和高延迟任务。

可参考我的上一篇文章:【鸿蒙应用开发】多线程并发(Stage模型)

taskpool 

使用任务池来提交任务,在其内部维护了统一的线程管理(负载均衡,动态调度等),任务多会自动扩容,长时间没任务会缩容,减少占用。

taskpool 提供了众多 API 帮助你构建更为强大的 任务调度,支持:

  • 设置任务优先级
  • 设置延迟执行时间
  • 设置周期性执行任务
  • 取消任务池任务
  • 任务组
  • 长时任务
  • 串行队列

其实还有很多不一一列举,总之就是非常强大。

戳下面的链接看全部的API:

taskpool 的API参考https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-taskpool-V5#state10

worker 

worker 也是一种并发多线程机制,你可以通过分离宿主线程和工作线程来加快处理效率,相比 taskpool,worker 个人认为更加的轻量,它使用简单,非常适合长运行时间,如:文件下载,文件读写等耗时任务。

虽说其 API 设计简单,但在实际使用上也暴露出功能匮乏,在一些需要进行任务取消,调度逻辑复杂的场景,它表现不太良好,不过我们本篇文章仅讨论它作为性能优化的一个方向。

Worker简介https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/worker-introduction-V5

适用场景

在多线程方面我们讨论了两种并发机制,它们各有千秋,在适用场景上当然有所不同。

如果你希望进行文件下载,文件读写等耗时任务,那么 worker 在其使用简单、高效API方面更具优势。

如果你希望定制任务调度逻辑,存在任务取消,任务调度复杂等情况时(比如你正在展示图片瀑布流,图片并行加载,若滑动超出范围则取消加载),taskpool 可能更适合。

官方对 worker 和 taskpool 也进行了比较,并对他们的适用场景做了详细对比,(总的来说官方更希望你尽可能用 taskpool, 它占用更小,API 更加强大,适用面更广。)

TaskPool和Worker的对比 (TaskPool和Worker)https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/taskpool-vs-worker-V5

状态管理

不使用状态变量强行更新非状态变量关联组件

以下是它的例子:

@Entry
@Component
struct MyComponent {@State needsUpdate: boolean = true;realStateArr: Array<number> = [4, 1, 3, 2]; // 未使用状态变量装饰器realState: Color = Color.Yellow;updateUIArr(param: Array<number>): Array<number> {const triggerAGet = this.needsUpdate;return param;}updateUI(param: Color): Color {const triggerAGet = this.needsUpdate;return param;}build() {Column({ space: 20 }) {ForEach(this.updateUIArr(this.realStateArr),(item: Array<number>) => {Text(`${item}`)})Text("add item").onClick(() => {// 改变realStateArr不会触发UI视图更新this.realStateArr.push(this.realStateArr[this.realStateArr.length-1] + 1);// 触发UI视图更新this.needsUpdate = !this.needsUpdate;})Text("chg color").onClick(() => {// 改变realState不会触发UI视图更新this.realState = this.realState == Color.Yellow ? Color.Red : Color.Yellow;// 触发UI视图更新this.needsUpdate = !this.needsUpdate;})}.backgroundColor(this.updateUI(this.realState)).width(200).height(500)}
}

以上是官方的代码示例,总的来说,你别用一个受状态管理的变量去强行改变非状态管理的变量的组件。很拗口对么? 看懂上面的代码你就能明白我在说什么哈哈。

官方的更多优化建议(状态管理相关)

上面这个例子其实也是我从官方找到的,官方梳理了很多状态管理中的优秀实践,戳:

状态管理优秀实践https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-state-management-best-practices-V5面试的时候,如果你把状态管理方面的性能优化按照上面的优秀实践说,那简直是核弹级别的回答。

未完待续

疯狂码字中。。。。

 如果觉得有用,点个赞支持一下呗(能关注一下就更好啦)。


http://www.ppmy.cn/devtools/163225.html

相关文章

Unity3D 战斗系统架构与设计详解

引言 战斗系统是许多游戏的核心玩法之一&#xff0c;尤其是在动作游戏、角色扮演游戏&#xff08;RPG&#xff09;和策略游戏中。Unity3D 作为一款强大的游戏引擎&#xff0c;提供了丰富的工具和 API 来帮助开发者实现复杂的战斗系统。本文将详细探讨 Unity3D 中战斗系统的架构…

JavaScript 验证 API

JavaScript 验证 API 引言 在网页开发中,数据验证是确保用户输入数据准确性和完整性的关键步骤。JavaScript 作为网页开发的主要脚本语言,提供了丰富的验证 API,使得开发者能够轻松实现各种验证需求。本文将详细介绍 JavaScript 验证 API 的使用方法、特点和注意事项。 一…

【行业解决方案篇十四】【DeepSeek法律科技:合同条款解析引擎】

开篇:当AI成为"法律CT机" 你可能不知道,某上市公司法务部去年审了2185份合同,其中73%的时间花在找条款间的"埋伏笔"。现在DeepSeek的合同解析系统,能让这些戴着金丝眼镜的法律顾问们用CT扫描般的精度看透每份合同。今天要讲的这个系统,不只是关键词匹…

微信小程序 - 自定义实现分页功能

概述 在微信小程序项目中&#xff0c;没有现成的分页器组件&#xff0c;所以需要自定义实现分页功能 自定义实现分页功能 1、index.json {"usingComponents": {"van-button": "vant/weapp/button/index"} }这里使用 Vant Weapp 中的 van-butt…

聚焦低空经济,峰飞航空飞行汽车开启未来出行新篇章

曾经只存在于科幻电影中的“飞行汽车”&#xff0c;如今正以eVTOL&#xff08;电动垂直起降飞行器&#xff09;的形式加速落地&#xff0c;成为全球科技竞争的新焦点。作为低空经济的核心载体之一&#xff0c;eVTOL不仅承载着缓解地面交通压力的使命&#xff0c;更被视为推动城…

git上传gitee仓库---简单方便

安装完git以后 在资源管理器中右键&#xff1a; 选择Open Git Bash here 接着gitclone&#xff0c;从gitee上面复制链接: https://gitee.com/hekai666/python-deeplearning.git 粘贴过来&#xff1a; 回车&#xff1a; 然后在本地就会多出来一个文件&#xff1a; 打开文件夹以…

Unity打包APK报错 using a newer Android Gradle plugin to use compileSdk = 35

Unity打包APK报错 using a newer Android Gradle plugin to use compileSdk 35 三个报错信息如下 第一个 WARNING:We recommend using a newer Android Gradle plugin to use compileSdk 35This Android Gradle plugin (7.1.2) was tested up to compileSdk 32This warning…

ffmpeg常用方法(一)

FFmpeg是一个非常强大的开源项目&#xff0c;提供了一套可以用来录制、转换数字音频、视频&#xff0c;并能将其转换成不同格式的工具和库。它是命令行工具&#xff0c;意味着它没有图形用户界面&#xff0c;但它能够被嵌入到其他应用程序中。它支持多种操作系统&#xff0c;包…