【鸿蒙实战开发】合理选择条件渲染和显隐控制

30 篇文章 0 订阅
30 篇文章 0 订阅

前言

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

原理机制

条件渲染

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

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

显隐控制

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

在这里插入图片描述

机制区别

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

在这里插入图片描述

适用场景

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

条件渲染的适用场景:

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

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

显隐控制

针对显示和隐藏间频繁切换的场景,下面示例通过按钮点击,实现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。
在这里插入图片描述

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

在这里插入图片描述

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

条件渲染

针对应用冷启动,加载绘制首页时,如果组件初始不需要显示的场景,下面示例通过初始时,隐藏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数据。具体操作,可以参考应用性能分析工具CPU Profiler的使用指导。

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

在这里插入图片描述

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

在这里插入图片描述

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

条件渲染和容器限制

针对反复切换条件渲染的控制分支,但切换项仅涉及页面中少部分组件的场景,下面示例通过Column父组件下1000个Text组件,与1个受条件渲染控制的Text组件的组合来说明该场景,并对1个受条件渲染控制的Text组件的外面是否加上容器组件做包裹,做两种情况的正反例性能数据的对比。

反例

没有使用容器限制条件渲染组件的刷新范围,导致条件变化会触发创建和销毁该组件,影响该容器内所有组件都会刷新。

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

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

  build() {
    Column() {
      Stack() {
        Scroll() {
          Column() { // 刷新范围会扩展到这一层
            if (this.isVisible) { // 条件变化会触发创建和销毁该组件,影响到容器的布局,该容器内所有组件都会刷新
              Text('New item').fontSize(20)
            }
            ForEach(this.data, (item: number) => {
              Text(`Item value: ${item}`).fontSize(20).width('100%').textAlign(TextAlign.Center)
            }, (item: number) => item.toString())
          }
        }
      }.height('90%')

      Button('Switch Hidden and Show').onClick(() => {
        this.isVisible = !(this.isVisible);
      })
    }
  }
}

正例

使用容器限制条件渲染组件的刷新范围。

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

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

  build() {
    Column() {
      Stack() {
        Scroll() {
          Column() {
            Stack() { // 在条件渲染外套一层容器,限制刷新范围
              if (this.isVisible) {
                Text('New item').fontSize(20)
              }
            }

            ForEach(this.data, (item: number) => {
              Text(`Item value: ${item}`).fontSize(20).width('100%').textAlign(TextAlign.Center)
            }, (item: number) => item.toString())
          }
        }
      }.height('90%')

      Button('Switch Hidden and Show').onClick(() => {
        this.isVisible = !(this.isVisible);
      })
    }
  }
}

效果对比

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

容器内有Text组件被if条件包含,if条件结果变更会触发创建和销毁该组件,此时影响到父组件Column容器的布局,该容器内所有组件都会刷新,包括模块ForEach,因此导致主线程UI刷新耗时过长。

以下为未使用容器限制条件渲染组件的刷新范围的方式,Column组件被标记脏区,ForEach耗时13ms。
在这里插入图片描述

基于上例,容器内有Text组件被if条件包含,if条件结果变更会触发创建和销毁该组件,此时对于这种受状态变量控制的组件,在if外套一层Stack容器,只局部刷新if条件包含的组件。因此减少了主线程UI刷新耗时。

以下为使用容器限制条件渲染组件的刷新范围的方式,Column组件没有被标记脏区,没有ForEach耗时。

在这里插入图片描述

可见,如果切换项仅涉及部分组件的情况,且反复切换条件渲染的控制分支,使用条件渲染配合容器限制,精准控制组件更新的范围,可以提升应用性能。

条件渲染和组件复用

针对反复切换条件渲染的控制分支,且控制分支中的每种分支内,组件子树结构都比较复杂的场景,当有可以复用的组件情况时,可以用组件复用配合条件渲染的方式提升性能。下面示例通过定义一个自定义复杂子组件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。

在这里插入图片描述

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

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

写在最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有必要的。

这份鸿蒙(HarmonyOS NEXT)文档包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

希望这一份鸿蒙学习文档能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习文档

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

在这里插入图片描述

有了路线图,怎么能没有学习文档呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习文档

《鸿蒙 (OpenHarmony)开发入门教学视频》

在这里插入图片描述

《鸿蒙生态应用开发V3.0白皮书》

在这里插入图片描述

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

在这里插入图片描述

《鸿蒙开发基础》

●ArkTS语言
●安装DevEco Studio
●运用你的第一个ArkTS应用
●ArkUI声明式UI开发
.……
在这里插入图片描述

《鸿蒙开发进阶》

●Stage模型入门
●网络管理
●数据管理
●电话服务
●分布式应用开发
●通知与窗口管理
●多媒体技术
●安全技能
●任务管理
●WebGL
●国际化开发
●应用测试
●DFX面向未来设计
●鸿蒙系统移植和裁剪定制
……
在这里插入图片描述

《鸿蒙进阶实战》

●ArkTS实践
●UIAbility应用
●网络案例
……
在这里插入图片描述

获取以上完整鸿蒙HarmonyOS学习文档,请点击→纯血版全套鸿蒙HarmonyOS学习文档

  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用如下方法来控制primitives(原语)的显隐: 1. 在HTML中,可以使用`display`属性控制元素的显示与隐藏。例如,可以使用`display:none`来隐藏元素,使用`display:block`来显示元素。 2. 在JavaScript中,可以使用`element.style.display`属性来控制元素的显示与隐藏。例如,可以使用`element.style.display='none'`来隐藏元素,使用`element.style.display='block'`来显示元素。 3. 在CSS中,可以使用`display`属性来控制元素的显示与隐藏。例如,可以使用`display:none`来隐藏元素,使用`display:block`来显示元素。 4. 在jQuery中,可以使用`hide()`函数来隐藏元素,使用`show()`函数来显示元素。 希望这些方法能帮到你。 ### 回答2: 要控制Primitives的显隐,首先需要明确Primitives是指计算机图形学中的基本几何元素,如点、线、多边形等。在图形学中,通过控制Primitives的显隐可以实现对场景的渲染和展示。 要控制Primitives的显隐,通常需要借助图形学编程语言或图形学软件来实现。下面是一种常见的实现方式: 首先,需要通过编程语言或软件中的接口或函数来创建Primitives对象并设置其属性,包括位置、尺寸、颜色等。然后,根据需要设置Primitives的可见性属性,即显示或隐藏。这通常可以通过设置Primitives对象的可见性标志或选择合适的接口函数实现。 例如,在计算机图形学编程中,可以通过使用OpenGL或DirectX等图形库来控制Primitives的显隐。在OpenGL中,可以通过调用glTranslatef、glRotatef等函数来设置Primitives的位置和变换属性。同时,可以通过 glEnable(GL_DEPTH_TEST)函数来开启深度测试,根据深度值实现Primitives的遮挡和显隐效果。 此外,还可以根据用户的交互来动态地控制Primitives的显隐。例如,在交互式的三维建模软件中,可以通过鼠标点击或拖拽等操作来选择或移动Primitives对象,并通过更新Primitives的可见性属性来实现显隐控制。 总之,控制Primitives的显隐是计算机图形学中的一个重要任务,可以通过编程语言或图形学软件提供的接口或函数来实现。这样就能根据需要设置Primitives的可见性属性,实现对场景的渲染和展示。 ### 回答3: 要控制primitives(基本元素)的显隐性,可以采取几种方法。首先,我们可以使用CSS属性的display来控制元素的可见性。通过将display属性设置为"none",可以使元素完全隐藏,不占用任何空间。而将display属性设置为"block"或"inline",则可以使元素显现出来。 其次,我们还可以使用JavaScript来控制primitives的显隐性。通过获取元素的DOM对象,并使用style属性中的display方法,我们可以通过改变display的值来实现元素的显示与隐藏。 另外,还可以借助jQuery库来控制primitives的显隐性。通过使用该库提供的方法如hide()和show(),我们可以实现元素的隐藏和显示。 通过上述方法,我们可以根据需要来控制primitives的显隐性。无论是使用CSS、JavaScript还是jQuery,我们都可以根据具体的情况来选择最适合的方法,以达到我们想要的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值