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

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、内存开销。使用时需要根据实际情况,综合性能和用户体验进行调整。

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

相关文章:

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

注&#xff1a;本文转载自HarmonyOS官网文档 开发者若使用低性能的代码实现功能场景可能不会影响应用的正常运行&#xff0c;但却会对应用的性能造成负面影响。本章节列举出了一些可提升性能的场景供开发者参考&#xff0c;以避免应用实现上带来的性能劣化。 使用数据懒加载 开…...

英文科技论文写作与发表-常见英语写作困扰(第3章)

1.常见英语写作困扰 英语写作应该是越精炼越好。写完一个句子&#xff0c;建议尝试删除某个或某些单词&#xff0c;如果删除后句子意义基本不变&#xff0c;就应该删除。 1.1 所有格 使用所有格可以省去至少一个单词&#xff08;of&#xff09;,如&#xff1a;Kangs book T…...

video标签自动播放音视频并绘制波形图

html中的<video>标签可以用来播放常见的音视频格式&#xff0c;支持的格式包括:MP3、Ogg、WAV、AAC、MP4、WebM、AVI等&#xff0c;当然支持的格式也和浏览器和操作系统有关。这里以一个可以自动播放音视频并绘制波形图的页面为例说明一下<video>标签的用法。 vid…...

Netty—EventLoop

文章目录 一、EventLoopGroup 是什么&#xff1f;&#x1f914;️二、NioEventLoop 有哪些重要组成部分&#xff1f;&#x1f50d;三、NioEventLoop 的 thread 在何时启动&#xff1f;三、 run() 方法中线程在干嘛&#xff1f; 一、EventLoopGroup 是什么&#xff1f;&#x1f…...

[极客大挑战 2019]FinalSQL(bypass盲注)

这里是数字型注入&#xff0c;选择一个序号 fuzz ?id1这里过滤了很多东西 使用fuzzSQL字典&#xff0c;这是我自己定义编写的一个fuzz字典&#xff0c;内容较少 select from information . tables whereand " or | & union columns updatexml extractvalue databa…...

如何实现小程序与h5页面间的跳转

接到新需求&#xff0c;要在小程序页面内点击按钮实现跳转h5&#xff0c;一开始没接触过&#xff0c;还挺头疼的&#xff0c;但真正做起来&#xff0c;也就那么一回事啦&#xff0c;废话少说&#xff0c;直接上 1. 配置域名 先登录小程序开发平台&#xff0c;将页面需要跳转的…...

企业架构LNMP学习笔记9

nginx配置文件定义php-fpm服务&#xff1a; 编写测试文件&#xff1a; vim /usr/local/nginx/html/index.php 内容&#xff1a; <?phpphpinfo(); 在nginx的配置文件中配置&#xff1a; 修改配置文件&#xff0c;告知nginx如果收到.php结尾的请求&#xff0c;交由给php-…...

华为OD机试 - 二维伞的雨滴效应(Java JS Python)

题目描述 普通的伞在二维平面世界中,左右两侧均有一条边,而两侧伞边最下面各有一个伞坠子,雨滴落到伞面,逐步流到伞坠处,会将伞坠的信息携带并落到地面,随着日积月累,地面会呈现伞坠的信息。 1、为了模拟伞状雨滴效应,用二叉树来模拟二维平面伞(如下图所示),现在输…...

【HttpRunnerManager】搭建接口自动化测试平台操作流程

一、需要准备的知识点 1. linux: 安装 python3、nginx 安装和配置、mysql 安装和配置 2. python: django 配置、uwsgi 配置 二、我搭建的环境 1. Centos7 &#xff08;配置 rabbitmq、mysql 、Supervisord&#xff09; 2. python 3.6.8 &#xff08;配置 django、uwsgi&am…...

【C++】STL-常用算法-常用查找算法

0.前言 1.find #include <iostream> using namespace std;// 常用查找算法 find #include<vector> #include<algorithm>//查找 内置数据类型 void test01() {vector<int>v;for (int i 0; i < 10; i){v.push_back(i);}//查找 容器中 是否有 5 这个元…...

vue3 webpack打包流程及安装 (1)

npm run build 也可以打包 如果没有特殊需求 可以使用 效果其实是差不多的 --------------------------------------------------------------------------------------------------------------------------------- webpack网址 &#xff1a; 起步 | webpack 中文文档 (docsc…...

【C++】内联函数 ① ( 内联函数引入 | 内联函数语法 )

文章目录 一、内联函数引入1、内联函数引入2、代码示例 - 宏代码片段 与 内联函数 二、内联函数语法1、内联函数语法说明2、代码示例 - 内联函数基本语法 一、内联函数引入 1、内联函数引入 " 内联函数 " 是 C 语言中的一种特殊函数 , 其目的是为了提高程序的执行效率…...

聊聊springboot的ConfigurationProperties的绑定

序 本文主要研究一下springboot的ConfigurationProperties的绑定 ConfigurationPropertiesBindingPostProcessor org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java /*** {link BeanPostProcessor} to bind {link PropertySo…...

Mysql和Oracle的语法区别?

Mysql和Oracle是两种不同的关系型数据库。 MySQL通常在中小型应用程序、Web应用程序和小型企业中广泛使用&#xff0c;因为它易于学习和部署&#xff0c;而且成本较低。 Oracle数据库通常用于大型企业和复杂的企业级应用程序&#xff0c;因为它提供了高度可扩展性、高可用性…...

F - LIS on Tree

F - LIS on Tree (atcoder.jp) 问题描述&#xff1a;树上LIS。 普通LIS。O(n * n)。 void solve() {int n; cin>>n;vector<int> f(n 1),a(n1);for(int i 1; i < n; i) {cin>>a[i];f[i] 1;for(int j 1; j < i; j) {if(a[i] > a[j]) f[i] max…...

2023 年全国大学生数学建模B题目-多波束测线问题

B题目感觉属于平面几何和立体几何的问题&#xff0c;本质上需要推导几何变换情况&#xff0c;B题目属于有标准答案型&#xff0c;没太大的把握不建议选择&#xff0c;可发挥型不大。 第一问 比较简单&#xff0c;就一个2维平面的问题&#xff0c;但有点没理解&#xff0c;这个…...

qt creater11 翻译国际化教程教程:

先出效果图。 闲聊几句&#xff1a;qt这个翻译很方便&#xff0c;能直接导出项目里所有文字。 具体步骤如下&#xff1a; 在Qt中&#xff0c;我们可以使用QTranslator类来实现多语言切换。以下是一般步骤&#xff1a; 1. 在你的源代码中&#xff0c;所有需要翻译的字符串都…...

【AWS实验 】在 AWS Fargate 上使用 Amazon ECS 部署应用程序

文章目录 实验概览目标实验环境任务 1&#xff1a;连接到实验命令主机任务 2&#xff1a;将应用程序容器化任务 3&#xff1a;构建 Web2048 容器任务 4&#xff1a;创建 Amazon ECR 存储库并推送 Docker 映像任务 5&#xff1a;创建 ECS 集群任务 6&#xff1a;测试应用程序总结…...

matlab几种求解器的选择fsolve-sole-vpasolve

文章目录 fsolvesolvevpasovle总结vpasovle的结果fsovle的结果 fsolve 求数值解 result_xfsolve(my_fun,x0,options)参数: my_fun:待求解函数&#xff0c;作为一个.m文件 x0:初始值&#xff0c;向量&#xff0c;可以仅仅指定其中的几项solve 强大的求解器。在方程组中求解析…...

无限访问 GPT-4,OpenAI 强势推出 ChatGPT 企业版!

继 ChatGPT 收费大降价、推出 App 版等系列动作之后&#xff0c;OpenAI 于今日宣布正式发布面向企业的 AI 助手——ChatGPT Enterprise 版。 与 To C 端的 ChatGPT 版本有所不同的是&#xff0c;该版本可以以更快速度无限制地访问 GPT-4&#xff0c;还可以用来处理更长输入的上…...

MySQL的故事——Schema与数据类型优化

Schema与数据类型优化 一、选择优化的数据类型 更小的通常更好 应该尽量使用可以正确存储数据的最小类型&#xff0c;更小的数据类型通常更快&#xff0c;因为他们占用更少的磁盘&#xff0c;内存和CPU缓存&#xff0c;并且处理时需要的CPU周期更少 简单就好 更简单的数据类型…...

C++编译和链接

编译和链接 一、源代码的组织 头文件&#xff08;.h&#xff09;&#xff1a;#include头文件、函数的声明、结构体的声明、类的声明、模板的声明、内联函数、#define和const定义的常量等。 源文件&#xff08;.cpp&#xff09;&#xff1a;函数的定义、类的定义、模板具体化的…...

【CSDN技术】Markdown编辑器如何使用-csdn博客编写入门

Markdown编辑器如何使用-csdn博客编写入门 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自…...

【docker】运行redis

拉取redis镜像 有多种选择&#xff1a; redis&#xff08;基础版&#xff09;redis/redis-stack&#xff08;包含redis stack server和RedisInsight&#xff09;redis/redis-stack-server&#xff08;仅包含redis stack server&#xff09; docker pull redis docker pull r…...

Paddle训练COCO-stuff数据集学习记录

COCO-stuff数据集 COCO-Stuff数据集对COCO数据集中全部164K图片做了像素级的标注。 80 thing classes, 91 stuff classes and 1 class ‘unlabeled’ 数据集下载 wget --directory-prefixdownloads http://images.cocodataset.org/zips/train2017.zip wget --directory-prefi…...

SpringBoot 框架学习

java 学习笔记指路 基础知识 Python转java补充知识 Java中常见的名词解释 前端 【黑马程序员pink老师前端】HTML 【黑马程序员pink老师前端】JavaScript基础大总结 【黑马程序员pink老师前端】JavaScript函数与作用域 【黑马程序员pink老师前端】JavaScript对象 数据库 【黑马程…...

java - lua - redis 完成商品库存的删减

java调用lua脚本完成对商品库存的管理 主页链接 微风轻吟挽歌的主页 如若有帮助请帮忙点赞 //lua脚本 获取到内存不够的商品StringBuilder sb new StringBuilder();//定义一个数组存储可能缺少库存的值sb.append(" local table {} ");//获取值sb.append(" …...

dbeaver离线安装clickhouse连接驱动

Clickhouse 数据库连接工具——DBeaver 主要介绍了Clickhouse 数据库连接工具——DBeaver相关的知识&#xff0c;希望对你有一定的参考价值。 Clickhouse 数据库连接工具——DBeaver 1.下载 DBeaver 和 连接驱动 https://dbeaver.io/files/dbeaver-ce-latest-x86_64-setup.…...

2024腾讯校招后端面试真题汇总及其解答(二)

11.如果同时有5个任务在10分钟之后提交,或者更多,那么如果是一个个从队列中拿数据,那么前一个任务会影响后续任务执行时间,说一下解决思路 你的问题是一个典型的并发处理问题。如果你的系统是单线程的,那么的确,前一个任务的执行时间会影响后续任务的执行时间。但是,你…...

datagrip 相关数据连接信息无缝迁移

背景 因为公司换电脑了&#xff0c;接触的项目比较多&#xff0c;不同项目&#xff0c;不同环境的数据库连接有好几十个&#xff0c;如果在新电脑上挨个重新连接一遍劳心劳力&#xff0c;所以想看一下能不能直接将之前保存的连接信息直接迁移到新的电脑上面。 为此&#xff0c…...

最好的建站网站/网站运营策划书

linux下使用Qt编译程序的时候&#xff0c;安装了程序自带的链接库之后&#xff0c;仍然上报这个错误&#xff0c;发现系统上报这个错误&#xff1a;/usr/bin/ld: 找不到 -ldhnetsdk经过仔细的定位&#xff0c;终于解决了&#xff0c;这里把思路梳理一下&#xff0c;做个总结。1…...

云端网站建设/湘潭网络推广

在JS中&#xff0c;为我们提供了三个包装类&#xff0c;通过这三个包装类&#xff0c;可以将基本数据类型的数据转换为对象 String() 可以将基本数据类型的字符串转换为String对象 Number() 可以将基本数据类型的数字转换为Number对象 Bolean() 可以将基本数据类型的布尔值转换…...

济南网站建设 泉诺/网站搭建详细教程

参考 C训练 (csdn.net) 目录 04HTTP 常用请求头 用户凭证Cookie Session 习题 06HTTPS 为什么使用HTTPS 2、SSL 3、TLS 4、证书与证书链 习题 07OSI七层模型 习题 08IP基础 1、IP分类 2、小知识 3、 网络类型 习题 09IPv6 10网络拓扑 习题 11域名解析 习…...

怎么做.com的网站/网址注册查询

转载&#xff1a;http://blog.csdn.net/kimmking/article/details/8424319 DOM方式是直接把xml文件全部加载到内存&#xff0c;然后建立dom树&#xff0c;特点&#xff1a;可读可写&#xff0c;支持XPath&#xff0c;但是非常慢&#xff0c;占用内存为xml的10倍数量级&#xff…...

wordpress媒体库空白/网络推广大概需要多少钱

Linuxqq安装及其所引发的问题{权限位是 777 (必须 >0755 且 <0755)}&#xff0c;linuxqq777oghostloghost-virtual-machine:~$ ~/home/loghost/qq$ sudo dpkg -i linuxqq_v1.0.2_beta1_i386.debbash: /home/loghost/home/loghost/qq$:没有那个文件或目录//问题1出现rootl…...

九九建筑网官网登录/搜索引擎优化的概念是什么

Source Insight自定义快捷键 鄙人一直用的是Eclipse来开发&#xff0c;如果只是做java的话&#xff0c;这个就够了&#xff0c;但因时不时要查C/C的代码&#xff0c;这个时候这个工具就有点力不从心了&#xff0c;因为C/C的代码非常多&#xff0c;一般放在服务器上&#xff0c…...