HarmonyOS—UI开发性能提升的推荐方法

注:本文转载自HarmonyOS官网文档

开发者若使用低性能的代码实现功能场景可能不会影响应用的正常运行,但却会对应用的性能造成负面影响。本章节列举出了一些可提升性能的场景供开发者参考,以避免应用实现上带来的性能劣化。

使用数据懒加载

开发者在使用长列表时,如果直接采用循环渲染方式,如下所示,会一次性加载所有的列表元素,一方面会导致页面启动时间过长,影响用户体验,另一方面也会增加服务器的压力和流量,加重系统负

@Entry
@Component
struct MyComponent {
  @State arr: number[] = Array.from(Array(100), (v,k) =>k);  //构造0-99的数组build() {List() {ForEach(this.arr, (item: number) => {ListItem() {Text(`item value: ${item}`)}}, (item: number) => item.toString())}}
}

上述代码会在页面加载时将100个列表元素全部加载,这并非我们需要的,我们希望从数据源中按需迭代加载数据并创建相应组件,因此需要使用数据懒加载,如下所示:

class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = []public totalCount(): number {return 0}public getData(index: number): any {return undefined}registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener')this.listeners.push(listener)}}unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {
      console.info('remove listener')this.listeners.splice(pos, 1)}}notifyDataReload(): void {this.listeners.forEach(listener => {
      listener.onDataReloaded()})}notifyDataAdd(index: number): void {this.listeners.forEach(listener => {
      listener.onDataAdd(index)})}notifyDataChange(index: number): void {this.listeners.forEach(listener => {
      listener.onDataChange(index)})}notifyDataDelete(index: number): void {this.listeners.forEach(listener => {
      listener.onDataDelete(index)})}notifyDataMove(from: number, to: number): void {this.listeners.forEach(listener => {
      listener.onDataMove(from, to)})}
}class MyDataSource extends BasicDataSource {private dataArray: string[] = ['item value: 0', 'item value: 1', 'item value: 2']public totalCount(): number {return this.dataArray.length}public getData(index: number): any {return this.dataArray[index]}public addData(index: number, data: string): void {this.dataArray.splice(index, 0, data)this.notifyDataAdd(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()build() {List() {LazyForEach(this.data, (item: string) => {ListItem() {Row() {Text(item).fontSize(20).margin({ left: 10 })}}.onClick(() => {this.data.pushData('item value: ' + this.data.totalCount())})}, item => item)}}
}

上述代码在页面加载时仅初始化加载三个列表元素,之后每点击一次列表元素,将增加一个列表元素。

设置List组件的宽高

在使用Scroll容器组件嵌套List组件加载长列表时,若不指定List的宽高尺寸,则默认全部加载。

说明

Scroll嵌套List时:

  • List没有设置宽高,会布局List的所有子组件。
  • List设置宽高,会布局List显示区域内的子组件。
  • List使用ForEach加载子组件时,无论是否设置List的宽高,都会加载所有子组件。
  • List使用LazyForEach加载子组件时,没有设置List的宽高,会加载所有子组件,设置了List的宽高,会加载List显示区域内的子组件。

class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = []public totalCount(): number {return 0}public getData(index: number): any {return undefined}registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener')this.listeners.push(listener)}}unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {
      console.info('remove listener')this.listeners.splice(pos, 1)}}notifyDataReload(): void {this.listeners.forEach(listener => {
      listener.onDataReloaded()})}notifyDataAdd(index: number): void {this.listeners.forEach(listener => {
      listener.onDataAdd(index)})}notifyDataChange(index: number): void {this.listeners.forEach(listener => {
      listener.onDataChange(index)})}notifyDataDelete(index: number): void {this.listeners.forEach(listener => {
      listener.onDataDelete(index)})}notifyDataMove(from: number, to: number): void {this.listeners.forEach(listener => {
      listener.onDataMove(from, to)})}
}class MyDataSource extends BasicDataSource {private dataArray: Array<string> = new Array(100).fill('test')public totalCount(): number {return this.dataArray.length}public getData(index: number): any {return this.dataArray[index]}public addData(index: number, data: string): void {this.dataArray.splice(index, 0, data)this.notifyDataAdd(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()build() {Scroll() {List() {LazyForEach(this.data, (item: string, index: number) => {ListItem() {Row() {Text('item value: ' + item + (index + 1)).fontSize(20).margin(10)}}})}}}
}

因此,此场景下建议设置List子组件的宽高。

class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = []public totalCount(): number {return 0}public getData(index: number): any {return undefined}registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener')this.listeners.push(listener)}}unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {
      console.info('remove listener')this.listeners.splice(pos, 1)}}notifyDataReload(): void {this.listeners.forEach(listener => {
      listener.onDataReloaded()})}notifyDataAdd(index: number): void {this.listeners.forEach(listener => {
      listener.onDataAdd(index)})}notifyDataChange(index: number): void {this.listeners.forEach(listener => {
      listener.onDataChange(index)})}notifyDataDelete(index: number): void {this.listeners.forEach(listener => {
      listener.onDataDelete(index)})}notifyDataMove(from: number, to: number): void {this.listeners.forEach(listener => {
      listener.onDataMove(from, to)})}
}class MyDataSource extends BasicDataSource {private dataArray: Array<string> = new Array(100).fill('test')public totalCount(): number {return this.dataArray.length}public getData(index: number): any {return this.dataArray[index]}public addData(index: number, data: string): void {this.dataArray.splice(index, 0, data)this.notifyDataAdd(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()build() {Scroll() {List() {LazyForEach(this.data, (item: string, index: number) => {ListItem() {Text('item value: ' + item + (index + 1)).fontSize(20).margin(10)}.width('100%')})}.width('100%').height(500)}.backgroundColor(Color.Pink)}
}

使用条件渲染替代显隐控制

如下所示,开发者在使用visibility通用属性控制组件的显隐状态时,仍存在组件的重新创建过程,造成性能上的损耗。

@Entry
@Component
struct MyComponent {
  @State isVisible: Visibility = Visibility.Visible;  build() {
    Column() {
      Button("显隐切换")
        .onClick(() => {
          if (this.isVisible == Visibility.Visible) {
            this.isVisible = Visibility.None
          } else {
            this.isVisible = Visibility.Visible
          }
        })
      Row().visibility(this.isVisible)
        .width(300).height(300).backgroundColor(Color.Pink)
    }.width('100%')
  }
}

要避免这一问题,可使用if条件渲染代替visibility属性变换,如下所示:

@Entry
@Component
struct MyComponent {
  @State isVisible: boolean = true;build() {Column() {Button("显隐切换").onClick(() => {this.isVisible = !this.isVisible})if (this.isVisible) {Row().width(300).height(300).backgroundColor(Color.Pink)}}.width('100%')}
}

使用Column/Row替代Flex

由于Flex容器组件默认情况下存在shrink导致二次布局,这会在一定程度上造成页面渲染上的性能劣化。

@Entry
@Component
struct MyComponent {
  build() {
    Flex({ direction: FlexDirection.Column }) {
      Flex().width(300).height(200).backgroundColor(Color.Pink)
      Flex().width(300).height(200).backgroundColor(Color.Yellow)
      Flex().width(300).height(200).backgroundColor(Color.Grey)
    }
  }
}

上述代码可将Flex替换为Column、Row,在保证实现的页面布局效果相同的前提下避免Flex二次布局带来的负面影响。

@Entry
@Component
struct MyComponent {
  build() {
    Column() {
      Row().width(300).height(200).backgroundColor(Color.Pink)
      Row().width(300).height(200).backgroundColor(Color.Yellow)
      Row().width(300).height(200).backgroundColor(Color.Grey)
    }
  }
}

减少应用滑动白块

应用通过增大List/Grid控件的cachedCount参数,调整UI的加载范围。cachedCount表示屏幕外List/Grid预加载item的个数。

如果需要请求网络图片,可以在item滑动到屏幕显示之前,提前下载好内容,从而减少滑动白块。

如下是使用cachedCount参数的例子:

@Entry
@Component
struct MyComponent {
  private source: MyDataSource = new MyDataSource();  build() {
    List() {
      LazyForEach(this.source, item => {
        ListItem() {
          Text("Hello" + item)
            .fontSize(50)
            .onAppear(() => {
              console.log("appear:" + item)
            })
        }
      })
    }.cachedCount(3) // 扩大数值appear日志范围会变大
  }
}class MyDataSource implements IDataSource {
  data: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];  public totalCount(): number {
    return this.data.length
  }  public getData(index: number): any {
    return this.data[index]
  }  registerDataChangeListener(listener: DataChangeListener): void {
  }  unregisterDataChangeListener(listener: DataChangeListener): void {
  }
}

使用说明:

cachedCount的增加会增大UI的cpu、内存开销。使用时需要根据实际情况,综合性能和用户体验进行调整。

点击关注阅读原文,了解更多精彩资讯

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/120912.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【OpenCV入门】第五部分——图像运算

文章结构 掩模图像的加法运算图像的位运算按位与运算按位或运算按位取反运算按位异或运算图像位运算的运用 合并图像加权和覆盖 掩模 当计算机处理图像时&#xff0c;有些内容需要处理&#xff0c;有些内容不需要处理。能够覆盖原始图像&#xff0c;仅暴露原始图像“感兴趣区域…

基本介绍——数据挖掘

1.数据挖掘的定义 数据挖掘是采用数学的、统计的、人工智能和神经网络等领域的科学方法&#xff0c;如记忆推理、聚类分析、关联分析、决策树、神经网络、基因算法等技术&#xff0c;从大量数据中挖掘出隐含的、先前未知的、对决策有潜在价值的关系、模式和趋势&#xff0c;并…

串的定义,实现和模式匹配

串的相关概念及操作 串的定义 串&#xff1a;是由零个或多个字符组成的有限序列。 空串&#xff1a;不包含任何字符的串称为空串。 子串&#xff1a;串中任意个连续的字符组成的子序列称为该串的子串。 空格串&#xff1a;由一个或多个空格组成的串称为空格串&#xff08;空格…

冯诺依曼体系结构/什么是OS?

一、体系结构图 示意图 控制器可以控制其它4个硬件&#xff0c;四个硬件直接可以进行数据传输。 5大硬件 但是这些个体需要用“线”连接。 为什么要有存储器&#xff1f; 如果没有&#xff0c;实际速度则为输入、输出设备的速度。 加上后&#xff0c;变为内存的速度。&#…

SquirrelMail实现Web方式收发邮件_xionglling的博客-CSDN博客

SquirrelMail实现Web方式收发邮件_xionglling的博客-CSDN博客小松鼠实现Web邮件服务SquirrelMail 是一个用PHP开发的Web邮件系统。它内置纯PHP支持的IMAP和SMTP协议&#xff0c;所有页面都遵循 HTML 4.0标准(没有使用任何 JavaScript 代码)&#xff0c;以便最大限度兼容各种多浏…

【程序员必知必会3】你还不懂ClickHouse和Hive的区别?!

ClickHouse和Hive究竟哪些区别 ClickHouse和Hive都是用于大数据处理和分析的分布式存储和计算系统&#xff0c;但它们之间存在一些区别&#xff1a; 架构&#xff1a;ClickHouse采用列式存储和向量化执行引擎&#xff0c;可以实现亚秒级别的数据查询。而Hive采用基于Hadoop的数…

亚马逊云科技通过生成式AI,帮助清华RIOS加速计算和分析的处理效率

近日&#xff0c;硬件创建平台Efabless宣布了其第一届“生成式AI开源芯片设计挑战赛”&#xff08;AI Generated Open-Source Silicon Design Challenge&#xff09;的评选结果。来自清华大学的RISC-V国际开源实验室&#xff08;RIOS Lab&#xff09;团队基于亚马逊云科技云上科…

JLink和ST-Link接口引脚介绍

STM32F1系列&#xff0c;STM8S系列&#xff0c;PY32F003系列都用过好久了&#xff0c;但是对JLink和ST-Link下载器认识&#xff0c;还是很肤浅的。有时候&#xff0c;需要自己接线&#xff0c;却不知道引脚定义&#xff0c;特整理如下&#xff1a; 1、ST-Link ST-Link适合对象…

解决IDEA中java的system.properties乱码问题

在拉了别人的代码到本地后发现system.properties中中文注释都变成了乱码&#xff0c;故记录下解决步骤&#xff0c;供参考&#xff0c;我的系统是mac系统 1、在IDEA中打开设置&#xff0c;IDEA--->Preferences 2、点击Editor-->File Encodings 3、图中三处选择UTF-8&…

智能配电室运维云平台

智能配电室运维云平台依托电易云-智慧电力物联网&#xff0c;是通过物联网技术实现配电设备智能化管理和运维的云服务系统。该平台可以实时监测配电设备的运行状态、能耗情况、故障报警等信息&#xff0c;并通过云计算、大数据等技术进行分析和处理&#xff0c;提供精准的数据支…

传输层—UDP原理详解

目录 前言 1.netstat 2.pidof 3.UDP协议格式 4.UDP的特点 5.面向数据报 6.UDP的缓冲区 7.UDP使用注意事项 8.基于UDP的应用层协议 总结 前言 在之前的文章中为大家介绍了关于网络协议栈第一层就是应用层&#xff0c;包含套接字的使用&#xff0c;在应用层编码实现服务…

nc前端合计行

nc前端合计行 1.无表体和单表体的合计行加法 只要卡片下 如果是只有表头要合计行就只留ShowTotalLine&#xff1b;如果是只有表体要合计行就只留ShowTotalLineTabcodes 2.多表体的合计行加法 表头卡片下和列表下都要 3.档案的合计行加法 重写一下列表模板

cin、cin.getline()、getline()的用法【C++】

一、cin>> 用法1&#xff1a;输入一个数字或字符 #include <iostream> using namespace std; int main () {int a,b;cin>>a>>b;cout<<ab<<endl;return 0; } 用法2&#xff1a;接收一个字符串&#xff0c;遇“空格”、“TAB”、“回车”…

select多选回显问题 (取巧~)

要实现的效果&#xff1a; 实际上select选择框&#xff0c;我想要的是数组对象&#xff0c;但是后端返回来的是个字符串。 以下是解决方法&#xff1a; 以上是一种简单的解决方法~ 也可以自己处理数据或者让后端直接改成想要的格式。

无涯教程-JavaScript - ISOWEEKNUM函数

描述 ISOWEEKNUM函数返回给定日期的年份的ISO周编号。 语法 ISOWEEKNUM (date)争论 Argument描述Required/OptionalDateDate is the date-time code used by Excel for date and time calculation.Required Notes Microsoft Excel将日期存储为连续数字,因此可以在计算中使…

【会议征稿】第五届土木工程、环境资源与能源材料国际学术会议(CCESEM 2023)

第五届土木工程、环境资源与能源材料国际学术会议&#xff08;CCESEM 2023&#xff09; 第五届土木工程、环境资源与能源材料国际学术会议&#xff08;CCESEM 2023&#xff09;&#xff0c;定于2023年10月27日至29日在厦门举行。会议主要围绕“土木工程”、“环境资源”、“能…

15种下载文件的方法文件下载方法汇总超大文件下载

15种下载文件的方法&文件下载方法汇总&超大文件下载 15种下载文件的方法Pentesters经常将文件上传到受感染的盒子以帮助进行权限提升&#xff0c;或者保持在计算机上的存在。本博客将介绍将文件从您的计算机移动到受感染系统的15种不同方法。对于那些在盒子上存在且需要…

Linux xargs命令继续学习

之前学习过Linux xargs&#xff0c;对此非常的不熟悉&#xff0c;下面继续学习一下&#xff1b; xargs 可以将管道或标准输入&#xff08;stdin&#xff09;数据转换成命令行参数&#xff0c;也能够从文件的输出中读取数据&#xff1b; xargs也可以给命令传递参数&#xff1b;…

redis核心数据结构

redis下载地址&#xff1a;Download | Redis linux进入redis目录首先使用make命令进行c的编译&#xff0c;修改redis.conf文件&#xff1a; daemonize yes #后台启动 protected-mode no #关闭保护模式&#xff0c;开启的 # 需要注释掉bind #bind 127.0.0.1&#xff08;bind…

【已解决】Java 后端使用数组流 Array.stream() 将数组格式的 Cookie 转换成字符串格式

&#x1f389;工作中遇到这样一个场景&#xff1a;远程调用某个接口&#xff0c;该接口需要用户的 Cookie 信息进行权限认证&#xff0c;认证通过之后才可以打通并返回数据。 在后端拿到 httpServletRequest 后&#xff0c;调用 getCookies() 方法&#xff0c;返回的是一个 Coo…