🌟 引言:组件化思维的价值

在现代鸿蒙应用开发中,组件化不仅是技术实现手段,更是架构设计的核心思想。通过将复杂界面拆分为独立、可复用的自定义组件,开发者能够构建出高内聚、低耦合的应用程序架构。ArkUI的组件系统让每个UI单元都能拥有独立的状态管理和生命周期,大幅提升代码的可维护性和团队协作效率。

一、自定义组件基础:从概念到实现

自定义组件是基于ArkUI声明式语法构建的独立UI单元,它封装了特定的布局、样式和交互行为,可以在应用的不同部分多次复用。

1. 组件基本结构与装饰器

@Component
struct UserCard {
  @State userName: string = '默认用户'
  @State isSelected: boolean = false

  build() {
    Column() {
      Image($r('app.media.avatar'))
        .width(60)
        .height(60)
        .borderRadius(30)
      
      Text(this.userName)
        .fontSize(16)
        .fontColor(this.isSelected ? '#007DFF' : '#182431')
    }
    .padding(10)
    .backgroundColor('#FFFFFF')
    .onClick(() => {
      this.isSelected = !this.isSelected
    })
  }
}

关键装饰器解析:

  • @Component:将struct转换为可复用的UI组件
  • @State:管理组件内部状态,状态变化自动触发UI更新
  • @Entry:标识页面入口组件(每个页面唯一)

2. 组件的导出与导入

为了实现真正的复用,组件需要支持跨文件引用:

// UserCard.ets
@Component
export struct UserCard {  // 使用export关键字导出
  @Prop userName: string
  
  build() {
    // 组件实现
  }
}

// Index.ets  
import { UserCard } from './UserCard'  // 导入自定义组件

@Entry
@Component
struct MainPage {
  build() {
    Column() {
      UserCard({ userName: '张三' })
      UserCard({ userName: '李四' })
    }
  }
}
二、组件数据通信:多种场景下的信息流转

组件通信是组件化架构的核心,鸿蒙提供了多种数据传递机制适应不同场景。

1. 父传子:@Prop属性的单向流动

@Prop允许父组件向子组件传递数据,建立单向绑定关系:

@Component
struct ProductItem {
  @Prop productName: string = '默认商品'  // 父组件传递的属性
  @Prop price: number = 0
  @Prop isOnSale: boolean = false

  build() {
    Row() {
      Column() {
        Text(this.productName)
          .fontSize(16)
        Text(`¥${this.price}`)
          .fontColor(this.isOnSale ? '#FF3B30' : '#000000')
          .decoration({ type: this.isOnSale ? TextDecorationType.LineThrough : TextDecorationType.None })
      }
      if (this.isOnSale) {
        Text('特价')
          .fontColor('#FF3B30')
          .border({ width: 1, color: '#FF3B30' })
      }
    }
  }
}

// 父组件使用
ProductItem({ productName: '鸿蒙开发板', price: 299, isOnSale: true })

2. 子传父:@Event事件回调机制

子组件通过事件通知父组件状态变化,实现逆向通信:

// 子组件定义
@Component
struct SearchBar {
  @State searchText: string = ''
  @Event onSearch: (text: string) => void  // 定义事件回调

  build() {
    Row() {
      TextInput({ placeholder: '请输入关键词', text: this.searchText })
        .onChange((value: string) => {
          this.searchText = value
        })
      
      Button('搜索')
        .onClick(() => {
          this.onSearch(this.searchText)  // 触发事件
        })
    }
  }
}

// 父组件使用
@Entry
@Component
struct ProductPage {
  build() {
    Column() {
      SearchBar({
        onSearch: (text: string) => {
          // 处理搜索逻辑
          this.performSearch(text)
        }
      })
    }
  }

  performSearch(keyword: string): void {
    console.info(`搜索关键词: ${keyword}`)
    // 实际搜索实现
  }
}

3. 双向同步:@Link的状态共享

@Link建立父子组件间的双向数据绑定,任何一方的修改都会同步到另一方:

@Component
struct ToggleSwitch {
  @Link isOn: boolean  // 双向绑定

  build() {
    Toggle({ type: ToggleType.Switch, isOn: this.isOn })
      .onChange((value: boolean) => {
        this.isOn = value  // 修改同步到父组件
      })
  }
}

@Entry
@Component
struct SettingsPage {
  @State autoLogin: boolean = true  // 父组件状态

  build() {
    Column() {
      Text(`自动登录状态: ${this.autoLogin ? '开启' : '关闭'}`)
      ToggleSwitch({ isOn: $autoLogin })  // 使用$符号建立双向绑定
    }
  }
}
三、组件组合与嵌套:构建复杂界面

通过组件的合理组合,可以构建出功能丰富且结构清晰的用户界面。

1. 容器组件的组合策略

// 基础信息展示组件
@Component
struct UserInfo {
  @Prop user: UserModel
  
  build() {
    Row() {
      Image(this.user.avatar)
        .width(40)
        .height(40)
      Column() {
        Text(this.user.name)
        Text(this.user.title)
          .fontColor('#666666')
      }
    }
  }
}

// 操作按钮组组件
@Component
struct ActionButtons {
  @Event onFollow: () => void
  @Event onMessage: () => void
  
  build() {
    Row() {
      Button('关注')
        .onClick(() => this.onFollow())
      Button('发消息')
        .onClick(() => this.onMessage())
    }
  }
}

// 组合成用户卡片组件
@Component
struct UserProfileCard {
  @Prop user: UserModel
  @Event onFollow: () => void
  
  build() {
    Column() {
      UserInfo({ user: this.user })
      ActionButtons({
        onFollow: this.onFollow.bind(this),
        onMessage: () => this.sendMessage()
      })
    }
  }
  
  sendMessage(): void {
    // 发送消息逻辑
  }
}

2. 插槽机制:@Builder的灵活运用

@Builder提供了一种更灵活的组件内容定制方式:

@Component
struct CardContainer {
  @BuilderParam header: () => void
  @BuilderParam content: () => void
  @BuilderParam footer: () => void

  build() {
    Column() {
      // 头部插槽
      if (this.header) {
        this.header()
      }
      
      // 内容插槽  
      Column() {
        if (this.content) {
          this.content()
        }
      }
      .layoutWeight(1)

      // 底部插槽
      if (this.footer) {
        this.footer()
      }
    }
  }
}

// 使用构建器定制内容
CardContainer() {
  .header(() => {
    Text('卡片标题')
      .fontSize(18)
      .fontWeight(FontWeight.Bold)
  })
  .content(() => {
    Text('这是卡片的主要内容区域...')
  })
  .footer(() => {
    Button('确认操作')
  }
}
四、组件生命周期管理

自定义组件拥有完整的生命周期回调,允许开发者在特定时机执行逻辑。

1. 生命周期阶段详解

@Component
struct LifecycleDemo {
  @State data: string = ''
  
  // 组件即将出现时触发
  aboutToAppear(): void {
    console.info('组件即将显示')
    this.loadData()
  }
  
  // 组件即将消失时触发  
  aboutToDisappear(): void {
    console.info('组件即将销毁')
    this.cleanup()
  }
  
  // 组件布局完成时触发
  onPageShow(): void {
    console.info('页面已显示')
  }
  
  // 组件隐藏时触发
  onPageHide(): void {
    console.info('页面已隐藏')
  }

  build() {
    Text(this.data)
  }
  
  private loadData(): void {
    // 数据加载逻辑
  }
  
  private cleanup(): void {
    // 资源清理逻辑
  }
}

2. 生命周期最佳实践

  • 数据加载:在aboutToAppear中执行,避免在build方法中阻塞渲染
  • 资源释放:在aboutToDisappear中清理定时器、监听器等资源
  • 状态保存:在onPageHide中保存用户操作状态
五、高级组件模式

1. 条件渲染控制

@Component
struct SmartContainer {
  @Prop condition: boolean
  @BuilderParam content: () => void
  @BuilderParam fallback: () => void = undefined

  build() {
    Column() {
      if (this.condition) {
        this.content()
      } else if (this.fallback) {
        this.fallback()
      } else {
        Text('暂无内容')
          .fontColor('#999999')
      }
    }
  }
}

2. 列表项组件优化

@Reusable  // 启用组件复用
@Component
struct OptimizedListItem {
  @Prop item: ListItemModel
  @Prop onItemClick: (item: ListItemModel) => void

  build() {
    Row() {
      Image(this.item.cover)
        .width(80)
        .height(80)
      Column() {
        Text(this.item.title)
          .fontSize(16)
        Text(this.item.description)
          .maxLines(2)
      }
    }
    .onClick(() => {
      this.onItemClick?.(this.item)
    })
  }
}
六、组件设计最佳实践

1. 单一职责原则

每个组件应该只负责一个特定的功能模块。过于复杂的组件应该进行拆分。

2. 接口设计规范

  • 属性命名:使用驼峰命名法,明确表达意图
  • 事件定义:使用on前缀,如onValueChange
  • 默认值:为可选属性提供合理的默认值
  • 类型校验:使用TypeScript确保类型安全

3. 性能优化策略

  • 使用@Reusable装饰器优化组件复用
  • 避免在build方法中创建新对象或函数
  • 合理使用@StorageLink进行状态持久化
  • 复杂计算使用@Watch监听器优化重渲染
💎 总结

自定义组件是鸿蒙应用架构的基石。通过掌握组件通信、生命周期管理和组合模式,开发者可以构建出高度可复用、易维护的UI组件体系。良好的组件设计不仅提升开发效率,也为后续的功能扩展和维护奠定坚实基础。

本系列下一篇文章将探讨《动效与交互:属性动画、转场动画与手势处理》,深入分析如何为鸿蒙应用添加流畅的动画效果和直观的交互体验。

进一步学习建议:在实际项目中,建议从简单的展示组件开始,逐步添加交互逻辑和状态管理。官方文档中的自定义组件详解提供了完整的API参考和最佳实践示例。

需要参加鸿蒙认证的请点击 鸿蒙认证链接

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐