HarmonyOS开发实战:笔友-条件渲染与可见性控制:if/Visibility 对比
前言
在 ArkUI 声明式开发范式中,条件渲染和可见性控制是两种不同的 UI 控制策略。if/else通过条件判断决定是否创建组件,而Visibility通过控制组件的可见性属性决定是否显示。它们各有适用场景,错误的选择可能导致性能问题或状态丢失。
本文将以开源鸿蒙笔友通信应用 xiexin 的SplashPage.ets、Index.ets、ComposePage.ets为蓝本,详细剖析if/else和Visibility的语义、差异、性能对比,以及它们在不同场景下的最佳实践。
提示:本文假设你已经了解 ArkUI 声明式开发的基本概念。如果还不熟悉,建议先阅读前十七篇文章。
一、条件渲染的两种方式
1.1 if/else 条件渲染
if/else通过条件判断决定是否创建组件:
// SplashPage.ets — if/else 切换按钮if(this.currentPage===2){Button('开始写信').fontSize(16).fontColor(AppColors.WHITE).backgroundColor(AppColors.PRIMARY).borderRadius(24).width(200).height(48).onClick(()=>{router.replaceUrl({url:'pages/Index'});})}else{Button('下一步').fontSize(16).fontColor(AppColors.PRIMARY).backgroundColor(AppColors.AMBER_LIGHT).borderRadius(24).width(200).height(48).onClick(()=>{this.currentPage=this.currentPage+1;})}1.2 Visibility 可见性控制
Visibility通过属性控制组件是否可见:
// Visibility 的三种状态Visibility.None// 完全不可见,不占布局空间Visibility.Hidden// 不可见,但保留布局占位Visibility.Visible// 可见// 使用示例Text('隐藏文字').visibility(Visibility.Hidden)二、if/else 与 Visibility 的核心差异
2.1 对比表格
| 维度 | if/else | Visibility |
|---|---|---|
| 组件创建 | 条件为 true 时创建 | 始终创建 |
| 内存占用 | 条件为 false 时不占用 | 始终占用内存 |
| 布局空间 | 条件为 false 时不占布局 | None不占,Hidden占位 |
| 状态保留 | 条件变化时销毁重建,状态丢失 | 状态始终保留 |
| 适用场景 | 不常切换、内容较多 | 频繁切换、内容较少 |
| 性能开销 | 创建/销毁有开销 | 始终存在,无切换开销 |
2.2 选择决策树
组件是否需要频繁切换显示/隐藏? ├── 是 → Visibility │ 组件是否需要保留状态? │ ├── 是 → Visibility.Hidden(保留占位) │ └── 否 → Visibility.None(不占布局) └── 否 → if/else 组件内容是否较多? ├── 是 → if/else(减少内存占用) └── 否 → if/else(默认选择)三、xiexin 中的条件渲染
3.1 SplashPage 的按钮切换
// SplashPage.ets — 使用 if/else 切换按钮if(this.currentPage===2){Button('开始写信').fontSize(16).fontColor(AppColors.WHITE).backgroundColor(AppColors.PRIMARY).borderRadius(24).width(200).height(48).fontWeight(FontWeight.Medium).onClick(()=>{router.replaceUrl({url:'pages/Index'});})}else{Button('下一步').fontSize(16).fontColor(AppColors.PRIMARY).backgroundColor(AppColors.AMBER_LIGHT).borderRadius(24).width(200).height(48).fontWeight(FontWeight.Medium).onClick(()=>{this.currentPage=this.currentPage+1;})}这里使用if/else是正确的选择,因为:
- 切换频率低:用户只会在引导页翻页时切换一次
- 组件内容少:按钮只有 10 行代码,创建/销毁开销小
- 状态不保留:切换按钮时不需要保留上一个按钮的状态
3.2 Index 页面的空状态判断
// Index.ets — 使用 if/else 判断空状态if(this.letters.length===0){EmptyState({title:'还没有信件,去写第一封吧',subtitle:'每一封信都是一段温暖的旅程'})}else{Scroll(){Column({space:12}){ForEach(this.getSortedLetters(),(letter:Letter)=>{this.LetterCard(letter)},(letter:Letter)=>letter.id.toString())}.padding({left:16,right:16,bottom:20})}.layoutWeight(1).scrollBar(BarState.Off)}这里使用if/else是正确的选择,因为:
- 切换频率低:从"空状态"到"有信件"只切换一次
- 内容差异大:空状态和信件列表的 UI 完全不同
- 内存优化:空状态时不会创建 100 个 ListItem 组件
3.3 ComposePage 的面板展开
// ComposePage.ets — 使用 @State 布尔值控制面板展开@StateshowHonorificPanel:boolean=false;@StateshowGreetingPanel:boolean=false;@StateshowClosingPanel:boolean=false;@StateshowWordPanel:boolean=false;@StateshowPaperPanel:boolean=false;@StateshowFormatPanel:boolean=false;// 在 build 中使用 if 条件渲染if(this.showHonorificPanel){this.HonorificPanel()}if(this.showGreetingPanel){this.GreetingPanel()}这里使用if条件渲染,但应该考虑Visibility的优化:
// 优化:使用 Visibility 保留面板展开时的滚动位置HonorificPanel().visibility(this.showHonorificPanel?Visibility.Visible:Visibility.None)四、if/else 的常见陷阱
4.1 状态丢失
// 问题:if/else 切换时,子组件状态丢失@Entry@Componentstruct MyComponent{@StateshowInput:boolean=false;build(){Column(){if(this.showInput){TextInput({placeholder:'输入内容'})}Button('切换').onClick(()=>{this.showInput=!this.showInput;})}}}当showInput从true变为false再变为true时,TextInput组件被销毁重建,用户输入的内容丢失。
4.2 动画中断
// 问题:if/else 切换时,动画被中断if(this.isVisible){Text('Hello').opacity(1).animation({duration:300})}else{// 组件不存在,动画无法播放}4.3 性能问题
// 问题:频繁切换时,if/else 导致大量创建/销毁if(this.isVisible){ForEach(this.largeArray,(item)=>{// 大量组件},(item)=>item.id.toString())}五、Visibility 的常见陷阱
5.1 布局空间问题
// Visibility.Hidden 保留布局空间Column(){Text('标题').visibility(Visibility.Hidden)// 标题不可见,但占位还在Text('内容')}5.2 与动画的配合
// 使用 opacity 实现平滑过渡@StateisVisible:boolean=false;Text('Hello').opacity(this.isVisible?1:0).animation({duration:300,curve:Curve.EaseOut})六、if/else 与 Visibility 的配合使用
6.1 组合策略
// 组合使用:if/else 控制创建,Visibility 控制显示@StateisComponentCreated:boolean=false;@StateisComponentVisible:boolean=false;if(this.isComponentCreated){Text('Hello').visibility(this.isComponentVisible?Visibility.Visible:Visibility.Hidden)}6.2 实际应用
// 场景:弹窗的创建和显示@StateisDialogCreated:boolean=false;@StateisDialogVisible:boolean=false;// 创建弹窗(仅一次)if(this.isDialogCreated){CustomDialog({visible:this.isDialogVisible,onClose:()=>{this.isDialogVisible=false;}})}// 显示/隐藏弹窗(多次切换)Button('显示弹窗').onClick(()=>{this.isDialogCreated=true;this.isDialogVisible=true;})七、xiexin 中的可见性控制优化
7.1 当前实现
xiexin 当前主要使用if/else进行条件渲染:
// SplashPage: if/else 切换按钮if(this.currentPage===2){/* 开始写信按钮 */}else{/* 下一步按钮 */}// Index: if/else 判断空状态if(this.letters.length===0){/* 空状态 */}else{/* 信件列表 */}// ComposePage: if 控制面板展开if(this.showHonorificPanel){/* 称谓面板 */}7.2 优化建议
| 当前位置 | 当前方式 | 建议方式 | 理由 |
|---|---|---|---|
| SplashPage 按钮 | if/else | 保持 if/else | 切换频率低,内容简单 |
| Index 空状态 | if/else | 保持 if/else | 内容差异大,内存优化 |
| ComposePage 面板 | if | 改为 Visibility | 面板切换频繁,需保留状态 |
八、条件渲染的性能对比
8.1 创建/销毁开销
| 对比项 | if/else | Visibility |
|---|---|---|
| 首次创建 | 需要创建 | 需要创建 |
| 条件变化 | 销毁重建 | 不需要创建 |
| 多次切换 | 每次创建/销毁 | 无额外开销 |
8.2 内存占用
| 对比项 | if/else | Visibility |
|---|---|---|
| 条件为 false | 不占用内存 | 占用内存 |
| 条件为 true | 占用内存 | 占用内存 |
| 1000 项列表 | 仅创建 1000 项 | 始终创建 1000 项 |
九、Visibility 与动画的配合
9.1 淡入淡出效果
@Entry@Componentstruct FadeExample{@StateisVisible:boolean=false;build(){Column(){Text('Hello').opacity(this.isVisible?1:0).animation({duration:300,curve:Curve.EaseOut})Button('切换').onClick(()=>{this.isVisible=!this.isVisible;})}}}9.2 滑动展开效果
@Entry@Componentstruct SlideExample{@StateisExpanded:boolean=false;build(){Column(){Button('展开').onClick(()=>{animateTo({duration:300},()=>{this.isExpanded=!this.isExpanded;})})// 使用 if 控制创建,动画控制显示if(this.isExpanded){Text('内容').opacity(1).animation({duration:300})}}}}十、从 xiexin 看条件渲染与可见性控制
xiexin 中条件渲染的使用体现了"场景决定选择"的原则:
- 引导页按钮切换:使用
if/else,因为切换频率低、内容简单 - 空状态判断:使用
if/else,因为内容差异大、内存占用差别大 - 编辑面板展开:使用
if(实际上应该用Visibility),因为切换频繁、需要保留状态
每个场景的选择都基于实际的业务需求,而非盲目使用某一种方式。
总结
本文详细剖析了 HarmonyOS ArkUI 的if/else条件渲染和Visibility可见性控制的语义、差异、性能对比,以及它们在不同场景下的最佳实践。
理解条件渲染与可见性控制的关键是把握"两个原则":创建原则(if/else 决定是否创建)和显示原则(Visibility 决定是否显示)。创建原则适用于"不常切换、内容较多"的场景,显示原则适用于"频繁切换、需要保留状态"的场景。
至此,我们已经完成了 HarmonyOS 应用开发实战系列的第二阶段——ArkUI 声明式 UI 进阶。从下一篇文章开始,我们将进入第三阶段——组件化与通用组件,深入剖析 xiexin 中如何通过组件封装实现 UI 复用。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源
- 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
- HarmonyOS 渲染控制概述:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-rendering-control-overview
- HarmonyOS if/else 条件渲染:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-rendering-control-if
- HarmonyOS ForEach 渲染控制:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-rendering-control-foreach
- HarmonyOS LazyForEach 渲染控制:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-rendering-control-lazyforeach
- HarmonyOS 高性能编程实践:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-high-performance-programming
- HarmonyOS 动画概述:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-animation
- HarmonyOS 状态管理概述:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state-management-overview
- HarmonyOS 自定义组件:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-custom-components
