鸿蒙学习实战之路-ArkTS 条件渲染_ifelse 使用指南

害,最近好多刚开始学鸿蒙开发的朋友问我:“西兰花啊,我以前写前端页面的时候,可以用 if else 控制页面内容,在鸿蒙里是不是也可以?还是要写很多复杂的代码?” 害,这问题可问对人了!

今天这篇,我就手把手带你从零开始搞定 ArkTS 条件渲染,保证你 5 分钟就能学会~

这是个啥?

ArkUI 通过自定义组件的 build() 函数和 @Builder 装饰器中的声明式 UI 描述语句构建相应的 UI。在声明式描述语句中开发者除了使用系统组件外,还可以使用渲染控制语句来辅助 UI 的构建,这些渲染控制语句包括控制组件是否显示的条件渲染语句,基于数组数据快速生成组件的循环渲染语句,针对大数据量场景的数据懒加载语句,针对混合模式开发的组件渲染语句。

简单来说,条件渲染就是根据条件动态显示不同的 UI 内容,这就像 Vue 里的 v-if 指令,或者 React 里的三元表达式一样~

这玩意儿能干什么?

条件渲染能帮你实现很多实用场景:

  • 登录状态控制:登录后显示用户信息,未登录显示登录按钮
  • 数据加载状态:加载中显示 Loading,成功后显示数据,失败显示错误信息
  • 权限控制:根据用户权限显示不同的功能按钮
  • 响应式界面:根据屏幕尺寸或设备类型显示不同布局
  • 购物车状态:有商品时显示购物车,空购物车时显示提示

🥦 西兰花小贴士
条件渲染非常适合做状态管理,比如 loading、success、error 三种状态的切换,比用 visibilitydisplay 属性更灵活~


使用规则你得知道

在开始写代码之前,这些规则你得先了解:

规则类别 具体说明
语法支持 支持 ifelseelse if 完整条件语句
条件变量 可使用状态变量(值改变时实时渲染 UI),可使用常规变量(值改变不会触发 UI 更新)
容器限制 允许在容器组件内使用,条件渲染对组件父子关系是"透明"的
构建函数规则 每个分支内部的构建函数必须创建一个或多个组件,空构建函数会产生语法错误

🥦 西兰花警告
注意了!条件分支里的构建函数不能是空的,必须至少创建一个组件。我之前写了个 if (show) { } 结果编译报错,整了半天才发现…(┓( ´∀` )┏)


跟着我做,3 种用法搞定

1. 基础 if 条件渲染

最简单的情况,就是根据一个变量显示不同的文本或组件:

@Entry
@Component
struct BasicIfRendering {
  @State count: number = 0;

  build() {
    Column() {
      Text(`当前计数: ${this.count}`)
        .fontSize(18)

      // 条件渲染核心代码
      if (this.count > 0) {
        Text(`计数为正数`)
          .fontColor(Color.Green)
          .fontSize(16)
      } else if (this.count < 0) {
        Text(`计数为负数`)
          .fontColor(Color.Red)
          .fontSize(16)
      } else {
        Text(`计数为零`)
          .fontColor(Color.Gray)
          .fontSize(16)
      }

      Button('增加计数')
        .onClick(() => this.count++)
        .margin(5)

      Button('减少计数')
        .onClick(() => this.count--)
        .margin(5)
    }
    .width('100%')
    .padding(20)
    .justifyContent(FlexAlign.Center)
  }
}

工作原理

  • 初始渲染时,if 语句执行构建函数并添加相应组件
  • count 状态变量变化时,条件语句重新评估
  • 分支变化时,旧组件被移除,新组件被创建并添加到父组件

这就像 Vue 的 v-if 一样,条件为真时创建组件,为假时销毁组件~

2. 状态管理问题与解决方案

这里有个坑需要注意!当条件切换时,子组件的状态会被重置:

// 子组件定义
@Component
struct CounterView {
  @State counter: number = 0;
  label: string = 'unknown';

  build() {
    Column({ space: 10 }) {
      Text(this.label)
        .fontSize(16)

      Button(`计数器: ${this.counter} +1`)
        .onClick(() => this.counter++)
        .width('100%')
    }
    .margin(10)
    .padding(15)
    .border({ width: 1, color: Color.Gray })
    .borderRadius(5)
  }
}

// 父组件使用条件渲染
@Entry
@Component
struct IfElseStateManagement {
  @State toggle: boolean = true;

  build() {
    Column() {
      // 条件渲染子组件
      if (this.toggle) {
        CounterView({ label: '正向计数器' })
      } else {
        CounterView({ label: '反向计数器' })
      }

      Button(`切换状态 (当前: ${this.toggle})`)
        .onClick(() => this.toggle = !this.toggle)
        .margin(10)
    }
    .width('100%')
    .padding(20)
    .justifyContent(FlexAlign.Center)
  }
}

问题所在

  • toggle 状态变化时,旧的 CounterView 实例会被删除,创建新的实例
  • 新旧 CounterView 是同一自定义组件的不同实例,状态不会共享
  • 所以每次切换都会重置计数

🥦 西兰花警告
这是条件渲染的一个常见坑!很多人以为子组件状态会保留,结果发现切换后数据丢失了…解决方法就是状态提升~

解决方案 - 状态提升

@Component
struct CounterView {
  @Link counter: number; // 使用@Link引用父组件状态
  label: string = 'unknown';

  build() {
    Column({ space: 10 }) {
      Text(this.label)
        .fontSize(16)

      Button(`计数器: ${this.counter} +1`)
        .onClick(() => this.counter++)
        .width('100%')
    }
    .margin(10)
    .padding(15)
    .border({ width: 1, color: Color.Gray })
    .borderRadius(5)
  }
}

@Entry
@Component
struct StatePreservationExample {
  @State toggle: boolean = true;
  @State counter: number = 0; // 状态提升至父组件

  build() {
    Column() {
      if (this.toggle) {
        CounterView({
          label: '正向计数器',
          counter: $counter // 传递状态引用
        })
      } else {
        CounterView({
          label: '反向计数器',
          counter: $counter // 共享同一状态
        })
      }

      Button(`切换状态 (当前: ${this.toggle})`)
        .onClick(() => this.toggle = !this.toggle)
        .margin(10)
    }
    .width('100%')
    .padding(20)
    .justifyContent(FlexAlign.Center)
  }
}

这样就能在切换条件时保持状态不变啦~

3. 嵌套条件渲染

复杂业务逻辑经常需要嵌套 if 语句:

@Entry
@Component
struct NestedIfExample {
  @State toggleOuter: boolean = false;
  @State toggleInner: boolean = false;

  build() {
    Column({ space: 15 }) {
      Text('条件渲染嵌套示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      // 外层条件
      if (this.toggleOuter) {
        Column({ space: 10 }) {
          Text('外层条件为真')
            .fontSize(18)
            .backgroundColor('#aaffaa')
            .padding(10)

          // 内层条件
          if (this.toggleInner) {
            Text('内层条件为真')
              .fontSize(16)
              .backgroundColor('#00aaaa')
              .padding(8)
          } else {
            Text('内层条件为假')
              .fontSize(16)
              .backgroundColor('#aaaaff')
              .padding(8)
          }
        }
      } else {
        Column({ space: 10 }) {
          Text('外层条件为假')
            .fontSize(18)
            .backgroundColor('#ffaaaa')
            .padding(10)

          // 内层条件
          if (this.toggleInner) {
            Text('内层条件为真')
              .fontSize(16)
              .backgroundColor('#00aaaa')
              .padding(8)
          } else {
            Text('内层条件为假')
              .fontSize(16)
              .backgroundColor('#aaaaff')
              .padding(8)
          }
        }
      }

      Row({ space: 10 }) {
        Button('切换外层条件')
          .onClick(() => this.toggleOuter = !this.toggleOuter)

        Button('切换内层条件')
          .onClick(() => this.toggleInner = !this.toggleInner)
      }
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .justifyContent(FlexAlign.Center)
  }
}

🥦 西兰花小贴士
嵌套条件渲染虽然强大,但别嵌套太深!超过 3 层就考虑把逻辑拆分到函数或计算属性里,这样代码更清晰~


常见坑与解决方案

1. 状态丢失问题

问题:条件分支切换时,子组件状态被重置
原因:分支切换会创建新的组件实例,原实例及其状态被销毁
解决方案

  • 将状态提升至父组件
  • 使用 @Link@Provide/@Consume 在组件间共享状态
  • 对于复杂状态,考虑使用状态管理库

2. 性能优化

问题:频繁切换条件导致性能问题
解决方案

  • 减少条件渲染层级,避免过度嵌套
  • 对于简单显示/隐藏需求,考虑使用 Visibility 属性
  • 避免在条件渲染中创建复杂组件树

3. 容器组件限制

问题:在特定容器中使用条件渲染报错
原因:某些容器组件对子组件类型有严格限制
解决方案

  • 确保条件渲染语句内创建的组件符合容器要求
  • 例如:Grid 容器内只能使用 GridItem 组件

🥦 西兰花警告
记住!Grid 容器内的条件渲染语句只能使用 GridItem 组件,如果你用了其他组件,编译时会报错…别问我怎么知道的 T_T


更新机制你得懂

ifelse if 后的条件语句中使用的状态变量值改变时,条件渲染会按以下步骤更新:

  1. 条件评估:重新计算 if 和 else if 的条件表达式
  2. 分支判断
    • 若分支未变化,不执行后续步骤
    • 若分支变化,则执行步骤 3 和 4
  3. 移除旧组件:删除此前构建的所有子组件
  4. 创建新组件:执行新分支的构造函数,添加生成的子组件到父容器中

注意:条件表达式中可包含 Typescript 表达式,但构造函数中的表达式不得更改应用程序状态


最佳实践

  1. 状态管理

    • 避免在条件分支中创建复杂状态
    • 共享状态应放在条件分支外部的父组件中
  2. 代码组织

    • 保持条件分支代码简洁,复杂逻辑应提取为独立函数或组件
    • 条件表达式应简单明了,复杂条件可封装为计算属性
  3. 性能考量

    • 避免不必要的条件判断
    • 对于大型列表,考虑使用 LazyForEach 结合条件渲染
  4. 可读性

    • 对复杂条件渲染添加注释
    • 保持分支内组件结构一致,便于维护

下一步

👉 预告:《ArkTS 循环渲染_forEach 使用指南》

📚 推荐资料:

我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐