HarmonyOS(AIP12 Beta5版)鸿蒙开发:选择条件渲染和显隐控制

news2024/11/14 15:03:03

开发者可以通过条件渲染或显隐控制两种方式来实现组件在显示和隐藏间的切换。本文从两者原理机制的区别出发,对二者适用场景分别进行说明,实现相应适用场景的示例并给出性能对比数据。

原理机制

条件渲染

if/else条件渲染是ArkUI应用开发框架提供的渲染控制的能力之一。条件渲染可根据应用的不同状态,渲染对应分支下的UI描述。条件渲染的作用机制如下:

  • 页面初始构建时,会评估条件语句,构建适用分支的组件,若缺少适用分支,则不构建任何内容。
  • 应用状态变化时,会重新评估条件语句,删除不适用分支的组件,构建适用分支的组件,若缺少适用分支,则不构建任何内容。

显隐控制

显隐控制visibility是ArkUI应用开发框架提供的组件通用属性之一。开发者可以通过设定组件属性visibility不同的属性值,进而控制组件的显隐状态。visibility属性值及其描述如下:

名称描述
Visible组件状态为可见
Hidden组件状态为不可见,但参与布局、进行占位
None组件状态为不可见,不参与布局、不进行占位

机制区别

具体针对实现组件显示和隐藏间切换的场景,条件渲染和显隐控制的作用机制区别总结如下:

机制描述条件渲染显隐控制
页面初始构建时,若组件隐藏,组件是否会被创建
若组件由显示变为隐藏时,组件是否会被销毁、从组件树取下
若组件隐藏时,是否占位可以配置

适用场景

通过条件渲染或显隐控制,实现组件的显示和隐藏间的切换,两者的适用场景分别如下:

条件渲染的适用场景:

  • 在应用冷启动阶段,应用加载绘制首页时,如果组件初始不需要显示,建议使用条件渲染替代显隐控制,以减少渲染时间,加快启动速度。
  • 如果组件不会较频繁地在显示和隐藏间切换,或者大部分时间不需要显示,建议使用条件渲染替代显隐控制,以减少界面复杂度、减少嵌套层次,提升性能。
  • 如果被控制的组件所占内存庞大,开发者优先考虑内存时,建议使用条件渲染替代显隐控制,以即时销毁不需要显示的组件,节省内存。
  • 如果组件子树结构比较复杂,且反复切换条件渲染的控制分支,建议使用条件渲染配合组件复用机制,提升应用性能。
  • 如果切换项仅涉及部分组件的情况,且反复切换条件渲染的控制分支,建议使用条件渲染配合容器限制,精准控制组件更新的范围,提升应用性能。

显隐控制的适用场景:

  • 如果组件频繁地在显示和隐藏间切换时,建议使用显隐控制替代条件渲染,以避免组件的频繁创建与销毁,提升性能。
  • 如果组件隐藏后,在页面布局中,需要保持占位,建议适用显隐控制。

显隐控制

针对显示和隐藏间频繁切换的场景,下面示例通过按钮点击,实现1000张图片显示与隐藏,来简单复现该场景,并进行正反例性能数据的对比。

反例

使用条件循环实现显示和隐藏间的切换。

@Entry
@Component
struct WorseUseIf {
  @State isVisible: boolean = true;
  private data: number[] = [];

  aboutToAppear() {
    for (let i: number = 0; i < 1000; i++) {
      this.data.push(i);
    }
  }

  build() {
    Column() {
      Button('Switch visible and hidden').onClick(() => {
        this.isVisible = !this.isVisible;
      }).width('100%')
      Stack() {
        if (this.isVisible) { // 使用条件渲染切换,会频繁创建与销毁组件
          Column() {
            ForEach(this.data, (item: number) => {
              Image($r('app.media.icon')).width('25%').height('12.5%')
            }, (item: number) => item.toString())
          }
        }
      }
    }
  }
}

正例

使用显隐控制实现显示和隐藏间的切换。

@Entry
@Component
struct BetterUseVisibility {
  @State isVisible: boolean = true;
  private data: number[] = [];

  aboutToAppear() {
    for (let i: number = 0; i < 1000; i++) {
      this.data.push(i);
    }
  }

  build() {
    Column() {
      Button('Switch visible and hidden').onClick(() => {
        this.isVisible = !this.isVisible;
      }).width('100%')
      Scroll() {
        Column() {
          ForEach(this.data, (item: number) => {
            Image($r('app.media.icon')).width('25%').height('12.5%')
          }, (item: number) => item.toString())
        }
      }.visibility(this.isVisible ? Visibility.Visible : Visibility.None) // 使用显隐控制切换,不会频繁创建与销毁组件
    }
  }
}

效果对比

正反例相同的操作步骤:通过点击按钮,将初始状态为显示的循环渲染组件切换为隐藏状态,再次点击按钮,将隐藏状态切换为显示状态。两次切换间的时间间隔长度,需保证页面渲染完成。

此时组件从显示切换到隐藏状态,由于条件渲染会触发一次销毁组件,再从隐藏切换到显示,二次触发创建组件,此时用条件渲染实现切换的方式, 核心函数forEach耗时1s。

img

基于上例,由于显隐控制会将组件缓存到组件树,从缓存中取状态值修改,再从隐藏切换到显示,继续从缓存中取状态值修改,没有触发创建销毁组件,此时用显隐控制实现切换的方式,核心函数forEach耗时2ms。

img

可见,如果组件频繁地在显示和隐藏间切换时,使用显隐控制替代条件渲染,避免组件的频繁创建与销毁,可以提高性能。

条件渲染

针对应用冷启动,加载绘制首页时,如果组件初始不需要显示的场景,下面示例通过初始时,隐藏1000个Text组件,来简单复现该场景,并进行正反例性能数据的对比。

反例

对于首页初始时,不需要显示的组件,通过显隐控制进行隐藏。

@Entry
@Component
struct WorseUseVisibility {
  @State isVisible: boolean = false; // 启动时,组件是隐藏状态
  private data: number[] = [];

  aboutToAppear() {
    for (let i: number = 0; i < 1000; i++) {
      this.data.push(i);
    }
  }

  build() {
    Column() {
      Button('Show the Hidden on start').onClick(() => {
        this.isVisible = !this.isVisible;
      }).width('100%')
      Stack() {
        Image($r('app.media.icon')).objectFit(ImageFit.Contain).width('50%').height('50%')
        Scroll() {
          Column() {
            ForEach(this.data, (item: number) => {
              Text(`Item value: ${item}`).fontSize(20).width('100%').textAlign(TextAlign.Center)
            }, (item: number) => item.toString())
          }
        }.visibility(this.isVisible ? Visibility.Visible : Visibility.None) // 使用显隐控制,启动时即使组件处于隐藏状态,也会创建
      }
    }
  }
}

正例

对于首页初始时,不需要显示的组件,通过条件渲染进行隐藏。

@Entry
@Component
struct BetterUseIf {
  @State isVisible: boolean = false; // 启动时,组件是隐藏状态
  private data: number[] = [];

  aboutToAppear() {
    for (let i: number = 0; i < 1000; i++) {
      this.data.push(i);
    }
  }

  build() {
    Column() {
      Button('Show the Hidden on start').onClick(() => {
        this.isVisible = !this.isVisible;
      }).width('100%')
      Stack() {
        Image($r('app.media.icon')).objectFit(ImageFit.Contain).width('50%').height('50%')
        if (this.isVisible) { // 使用条件渲染,启动时组件处于隐藏状态,不会创建
          Scroll() {
            Column() {
              ForEach(this.data, (item: number) => {
                Text(`Item value: ${item}`).fontSize(20).width('100%').textAlign(TextAlign.Center)
              }, (item: number) => item.toString())
            }
          }
        }
      }
    }
  }
}

效果对比

正反例相同的操作步骤:通过hdc命令方式,采集应用主线程冷启动的CPU Profiler数据。

当应用加载绘制首页,大量组件初始不需要显示的冷启动场景时,如果组件初始不需要显示,此时使用显隐控制,启动时即使组件为隐藏状态也会创建组件。在UIAbility 启动阶段,以下为使用显隐控制的方式,渲染初始页面initialRenderView耗时401.1ms。

img

基于上例,如果组件初始不需要显示,此时使用条件渲染由于不满足渲染条件,启动时组件不会创建。在UIAbility 启动阶段,以下为使用条件渲染的方式,渲染初始页面initialRenderView耗时12.6ms。

img

可见,如果在应用冷启动阶段,应用加载绘制首页时,如果组件初始不需要显示,使用条件渲染替代显隐控制,可以减少渲染时间,加快启动速度。

条件渲染和组件复用

针对反复切换条件渲染的控制分支,且控制分支中的每种分支内,组件子树结构都比较复杂的场景,当有可以复用的组件情况时,可以用组件复用配合条件渲染的方式提升性能。下面示例通过定义一个自定义复杂子组件MockComplexSubBranch配合条件渲染,来展示两种场景的性能效果对比,并对该组件复用与否做正反例性能数据的对比。

反例

没有使用组件复用实现条件渲染控制分支中的复杂子组件。

@Entry
@Component
struct IfWithoutReusable {
  @State isAlignStyleStart: boolean = true;

  build() {
    Column() {
      Button('Change FlexAlign').onClick(() => {
        this.isAlignStyleStart = !this.isAlignStyleStart;
      })
      Stack() {
        if (this.isAlignStyleStart) {
          MockComplexSubBranch({ alignStyle: FlexAlign.Start }) // 未使用组件复用机制实现的MockComplexSubBranch
        } else {
          MockComplexSubBranch({ alignStyle: FlexAlign.End })
        }
      }
    }
  }
}

其中MockComplexSubBranch是由3个Flex容器组件分别弹性布局200个Text组件构造而成,用以模拟组件复杂的子树结构,代码如下:

@Component
export struct MockComplexSubBranch {
  @State alignStyle: FlexAlign = FlexAlign.Center;

  build() {
    Column() {
      Column({ space: 5 }) {
        Text('ComplexSubBranch not reusable').fontSize(9).fontColor(0xCCCCCC).width('90%')
        AlignContentFlex({ alignStyle: this.alignStyle })
        AlignContentFlex({ alignStyle: this.alignStyle })
        AlignContentFlex({ alignStyle: this.alignStyle })
      }
    }
  }
}

@Component
struct AlignContentFlex {
  @Link alignStyle: FlexAlign;
  private data: number[] = [];

  aboutToAppear() {
    for (let i: number = 0; i < 200; i++) {
      this.data.push(i);
    }
  }

  build() {
    Flex({ wrap: FlexWrap.Wrap, alignContent: this.alignStyle }) {
      ForEach(this.data, (item: number) => {
        Text(`${item % 10}`).width('5%').height(20).backgroundColor(item % 2 === 0 ? 0xF5DEB3 : 0xD2B48C)
      }, (item: number) => item.toString())
    }.size({ width: '100%', height: 240 }).padding(10).backgroundColor(0xAFEEEE)
  }
}

正例

使用组件复用实现条件渲染控制分支中的复杂子组件。

@Entry
@Component
struct IfWithReusable {
  @State isAlignStyleStart: boolean = true;

  build() {
    Column() {
      Button('Change FlexAlign').onClick(() => {
        this.isAlignStyleStart = !this.isAlignStyleStart;
      })
      Stack() {
        if (this.isAlignStyleStart) {
          MockComplexSubBranch({ alignStyle: FlexAlign.Start }) // 使用组件复用机制实现的MockComplexSubBranch
        } else {
          MockComplexSubBranch({ alignStyle: FlexAlign.End })
        }
      }
    }
  }
}

其中MockComplexSubBranch实现如下方所示,AlignContentFlex 代码一致,此处不再赘述。

@Component
@Reusable // 添加Reusable装饰器,声明组件具备可复用的能力
export struct MockComplexSubBranch {
  @State alignStyle: FlexAlign = FlexAlign.Center;

  aboutToReuse(params: ESObject) { // 从缓存复用组件前,更新组件的状态变量
    this.alignStyle = params.alignStyle;
  }

  build() {
    Column() {
      Column({ space: 5 }) {
        Text('ComplexSubBranch reusable').fontSize(9).fontColor(0xCCCCCC).width('90%')
        AlignContentFlex({ alignStyle: this.alignStyle })
        AlignContentFlex({ alignStyle: this.alignStyle })
        AlignContentFlex({ alignStyle: this.alignStyle })
      }
    }
  }
}

效果对比

正反例相同的操作步骤:通过点击按钮,Text组件会在Flex容器主轴上,由首端对齐转换为尾端对齐,再次点击按钮,由尾端对齐转换为首端对齐。两次切换间的时间间隔长度,需保证页面渲染完成。

此时由于按钮反复切换了条件渲染分支,且每一分支中的MockComplexSubBranch组件子树结构都比较复杂,会造成大量的组件销毁创建过程,以下为不使用组件复用实现条件渲染控制分支中的子组件的方式,应用Index主页面渲染耗时180ms。

img

基于上例,考虑到将控制分支中的复杂组件子树结构在父组件中进行组件复用,此时从组件树缓存中拿出子组件,避免大量的组件销毁创建过程,以下为使用组件复用实现条件渲染控制分支中的子组件的方式,应用Index主页面渲染耗时14ms。

img

可见,针对反复切换条件渲染的控制分支的情况,且控制分支中的组件子树结构比较复杂,使用组件复用机制,可以提升应用性能。

最后

小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为资料太多,太杂,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有必要的。 

为了确保高效学习,建议规划清晰的学习路线,涵盖以下关键阶段:

希望这一份鸿蒙学习文档能够给大家带来帮助~


 鸿蒙(HarmonyOS NEXT)最新学习路线

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案

路线图适合人群:

IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术

2.视频教程+学习PDF文档

(鸿蒙语法ArkTS、TypeScript、ArkUI教程……)

 纯血版鸿蒙全套学习文档(面试、文档、全套视频等)

                   

鸿蒙APP开发必备

​​

总结

参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软考通过率真的很低吗?

一、软考通过率多少&#xff1f; 首先要说的是&#xff0c;软考办并没有公布过全国考试通过率。但我们可以根据官方公布的报名人数和合格人数做一个预估。 浙江软考办官方公布&#xff0c;浙江2022年下半年软考合格人数为4780人(其中初级779人、中级2392人、高级1609人)。 以…

【附解决方法】由于找不到vcruntime140_1.dll 无法继续执行代码如何处理

准备使用photoshop &#xff0c;结果弹出这个 提示“由于找不到 VCRUNTIME140_1.dll&#xff0c;无法继续执行代码。重新安装程序可能会解决此问题。”&#xff0c;这一般是什么原因导致了这个问题&#xff0c;我们要如何解决? 原因&#xff1a; vcruntime140_1.dll文件即动…

【推荐】9款适合中小企业的知识库管理系统

一、什么是知识库管理系统 知识库管理系统&#xff08;Knowledge Base Management System, KBMS&#xff09;&#xff0c;又称数字资产管理系统&#xff08;Digital Asset Management System&#xff09;&#xff0c;是专门用于管理企业知识文档、图纸、视频、音频等信息内容的…

中级测试工程师面试题

很多软件测试工程师在面试的时候都会遇到考官给的各种各样的面试题&#xff0c;这也反应了测试工程师对企业的重要性&#xff0c;面试通常分为以下几个方面&#xff0c;由于篇幅有限&#xff0c;在这里就只给大家分享一些比较常见的问题。 一、自我介绍 这里我不分享如何自我介…

FPGA——VGA协议

VGA协议 VGA简介接口及引脚定义显示原理显示电路原理VGA协议电压标准数字信号转化标准模拟信号方案数字信号时序标准 模块设计 VGA简介 VGA&#xff0c;英文全称“Video Graphics Array”&#xff0c;译为视频图形阵列&#xff0c;是一种使用模拟信号进行视频传输的标准协议&a…

观测云广告全国登陆,携手华为云引领企业迈向数智化新纪元

随着数字化转型的浪潮席卷全球&#xff0c;企业如何紧跟时代步伐&#xff0c;实现转型升级&#xff0c;已成为业界热议的焦点。华为云828 B2B企业节&#xff0c;作为业界瞩目的年度盛事&#xff0c;已于8月27日在贵阳盛大开幕。与此同时&#xff0c;观测云与华为云强强联手&…

基于 web教学管理系统设计与实现

3 总体设计 3.1 系统软件体系结构 系统采用B/S结构&#xff0c;统一管理数据库和Web服务器。在这种结构下&#xff0c;用户界面完全通过WWW浏览器实现&#xff0c;一部分事务逻辑在前端实现&#xff0c;但是主要事务逻辑在服务器端实现&#xff0c;形成所谓3-tier结构,第一…

沈阳网站建设手机能看的网站

在当今信息化的时代&#xff0c;网站已经成为企业展示形象、推广产品和服务的重要工具。尤其是在中国的沈阳&#xff0c;随着智能手机的普及&#xff0c;越来越多的用户选择通过移动设备浏览网站。因此&#xff0c;建设一个能够在手机上良好展示的网站显得尤为重要。本文将探讨…

vivado中定点类型Binary point的含义

vivado中&#xff0c;ILA或仿真波形显示的定点数&#xff0c;可以设置为有符号或无符号数&#xff0c;其中小数点位置通过Binary point设置&#xff0c;这个设置的数值&#xff0c;表示小数点后的二进制位数 参考&#xff1a; https://people-ece.vse.gmu.edu/coursewebpages/E…

【pandas2】表格数据的行列操作、查询指定的数据内容、数据类型处理、 缺失值处理和透视表、分组与聚合、数据的纵向合并(扩展数据)、数据的横向合并(连接表)

1 表格数据的行列操作 2 查询指定的数据内容 3 数据类型处理 4 缺失值处理 5 透视表 6 分组与聚合 7 统计NBA夺冠次数 8 数据的纵向合并(扩展数据) 9 数据的横向合并(连接表) 1 表格数据的行列操作 # 增 df[info] 这些车都很好 df[desc] df[Sec_price] * df[Km(W)] df.inser…

深度解读SGM41511电源管理芯片I2C通讯协议REG0B寄存器解释

REG0B 是 SGM41511 的第十二个寄存器 也是最后一个寄存器&#xff0c;地址为 0x0B。这个寄存器包含了只读&#xff08;R&#xff09;和可读写&#xff08;R/W&#xff09;的位。上电复位值&#xff08;PORV&#xff09;为 000101xx&#xff0c;其中 x 表示不确定的初始状态。这…

实时数仓,站上产业潮头

在这场新的数据驱动战场里&#xff0c;谁能更好的对数据进行智能、准确、迅速、高性价比的体系化处理&#xff0c;谁能以更低的成本、更高效的能力构建底层的PaaS、IaaS组件&#xff0c;谁就能在如今的市场竞争中构建更具竞争力的业务模型&#xff0c;成为新的弄潮儿。 对Byt…

C#开发中ImageComboBox控件数据源实时变换

在C#开发中&#xff0c;我们如何将控件的数据源实时变换&#xff0c;当然我们可以在窗口实例化的时候指定固定的数据源&#xff0c;但是这样对于用户来说数据源永远固定&#xff0c;并不利于我们对于用户的数据存储&#xff0c;优化用户的操作&#xff0c;遇到这种问题&#xf…

模拟登录页,华为账号一键登录

一、介绍 基于鸿蒙Next模拟账号一键登录&#xff0c;免去账号注册环节二、场景需求 1. 用户场景 新用户&#xff1a; 需要快速注册并登录&#xff0c;以体验华为的服务。 老用户&#xff1a; 希望快速登录&#xff0c;不用每次输入用户名和密码。 2. 界面设计 Logo和标题&#…

RK方案有时一开机要设置GPIO口点平

有时候RK方案&#xff0c;需要一开机就设置GPIO口电平&#xff0c;需要在uboot阶段&#xff0c;board.c #define GPIO_BANK0 0 #define GPIO_BANK1 32 #define GPIO_BANK2 64 #define GPIO_BANK3 …

基于RK3568智慧交通-雷达视频融合一体机,支持鸿蒙

智慧交通-雷达视频融合一体机 随着5G网络与智慧交通车路协同系统在全国各点的落地&#xff0c;作为提升交通安全的前沿技术方案也愈发受到重视。 在交通信控领域&#xff0c;以往的感知技术、无论是地磁、线圈还是摄像头&#xff0c;功能都仅仅局限于数清经过了多少车辆&…

TypeScript类型检查错误 error TS2339

错误产生 上一篇博客写了一个调用摄像头的 demo &#xff0c;用了 vue3 vite &#xff0c;使用了 TypeScript &#xff0c;代码大致如下&#xff1a; <script setup lang"ts"> import { onMounted, ref } from vue; import WelcomeItem from ./WelcomeItem.…

视频监控管理老鼠检测算法应用方案厨房老鼠检测算法源码展示

在当今的现代厨房环境中&#xff0c;维持卫生的重要性不仅仅体现在健康层面&#xff0c;更与整个家庭的生活质量息息相关。随着城市化进程不断加快&#xff0c;城市中的居住空间变得更加拥挤&#xff0c;这导致了一系列家庭管理问题的出现&#xff0c;其中厨房老鼠问题尤为突出…

复杂 RAG 系统的检索规划

文章介绍了REAPER&#xff08;Reasoning based Retrieval Planning for Complex RAG Systems&#xff09;&#xff0c;这是一种基于大型语言模型&#xff08;LLM&#xff09;的规划器&#xff0c;用于在复杂的对话系统中生成检索计划。REAPER旨在解决在大规模异构数据存储中进行…

linux中下载nginx

Nginx是一款高性能的开源Web服务器软件。它可以作为一个HTTP服务器进行网站的发布&#xff0c;也可以作为反向代理服务器进行负载均衡。Nginx以其出色的性能、稳定性和低资源消耗而受到广泛的使用。它的特点包括支持并发连接数高、内存消耗低、事件驱动等&#xff0c;适用于大部…