自学 HarmonyOS ArkTS 自定义组件:打造可复用的 UI 架构 原创

李森同学
发布于 2025-3-27 10:40
浏览
0收藏

作为一名 HarmonyOS 开发者,我在构建复杂 UI 时深刻体会到:重复的代码逻辑、臃肿的页面结构、难以维护的状态管理,这些问题都可以通过自定义组件迎刃而解。在官方文档的基础上,我结合实际开发经验,整理出这套系统化的学习路径。本文不仅包含官方示例的重构(所有代码均经过参数 / 结构调整),更融入了状态管理最佳实践、性能优化技巧和真实项目中的踩坑总结,助你构建可扩展的 UI 架构。

一、自定义组件:构建 UI 的原子化思维

核心特性(官方定义重构)

  • 可组合性:封装系统组件(如 Row+Text)为复合组件
  • 可复用性:跨页面 / 跨项目的组件实例化(如 ButtonGroup)
  • 数据驱动:@State 变量触发 UI 自动刷新(区别于命令式编程)

基础语法(代码重构示例)

// 自定义按钮组件(修改参数名+样式)
@Component
struct CustomButton {
  @State btnText: string = '点击我' // 状态变量
  @State btnColor: Color = Color.RoyalBlue // 初始样式

  // 自定义点击事件(新增业务逻辑)
  handleClick() {
    this.btnText = `已点击${++this.clickCount}次`
    console.info(`按钮状态更新:${this.btnText}`) // 合法的日志位置
  }

  build() {
    Button(this.btnText)
      .fontSize(16)
      .padding(12)
      .backgroundColor(this.btnColor)
      .onClick(() => this.handleClick()) // 绑定自定义逻辑
  }
}

// 父组件调用(多实例复用)
@Entry
@Component
struct AppComponent {
  build() {
    Column {
      CustomButton() // 默认实例
        .margin(10)
        .width(150)
        
      CustomButton({ btnText: '提交', btnColor: Color.Green }) // 定制实例
        .margin(10)
        .width(200)
    }
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.

二、组件元编程:装饰器的魔法世界

核心装饰器对比(表格重构)

装饰器

作用场景

版本支持

扩展参数

@Component

基础组件封装

API 7+

freezeWhenInactive(API 11+)

@Entry

页面入口组件

API 7+

routeName/storage(API 10+)

@Reusable

可复用组件(跨模块)

API 10+

-

高级用法:组件冻结优化(新增示例)

// 长列表组件(开启冻结优化)
@Component({ freezeWhenInactive: true }) 
struct ListItem {
  @State itemData: any

  build() {
    Row {
      Text(itemData.title).fontSize(14)
      Text(itemData.date).fontSize(12).opacity(0.7)
    }.height(60)
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

三、build () 函数:UI 描述的黄金法则

合法操作清单(流程图解)

build() 函数 → [ 根节点唯一 ] → [ 容器组件(@Entry)/任意组件(@Component) ]
          ↳ 禁止:本地变量/switch/表达式/直接状态修改
          ↳ 允许:if条件/@Builder方法/TS函数返回值
  • 1.
  • 2.
  • 3.

反模式与解决方案(真实案例)

错误示例:(循环渲染陷阱)

@State count: number = 0
build() {
  Text(`计数:${this.count++}`) // ❌ 每次渲染改变状态
    .onClick(() => this.count += 2)
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.


正确实现:(状态变更分离)

@State count: number = 0
handleCount() {
  this.count++ // ✅ 仅在事件处理中变更
}

build() {
  Text(`计数:${this.count}`)
    .onClick(this.handleCount)
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

四、父子组件通信:数据流的双向通道

实战模式:

  1. 属性传递(父→子):

// 子组件
@Component
struct ChildComponent {
  propTitle: string // 父组件传入的属性
  build() { Text(propTitle) }
}

// 父组件
ChildComponent({ propTitle: '来自父组件的标题' })
  .fontSize(18)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.


  1. 事件回调(子→父):

// 子组件
@Component
struct ButtonGroup {
  onSubmit: () => void // 回调函数定义

  build() {
    Button('提交')
      .onClick(() => this.onSubmit()) // 触发回调
  }
}

// 父组件
@State formData: any = {}
handleSubmit() {
  console.info('提交数据:', formData)
}

ButtonGroup({ onSubmit: this.handleSubmit })
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

五、状态管理:性能优化的关键

状态变更最佳实践:

  1. 最小化更新(API 9 + 特性):

@State user: { name: string, age: number } = { name: 'Alice', age: 25 }

build() {
  Row {
    Text(user.name).fontSize(16) // 仅name变更时更新
    Text(user.age.toString()).fontSize(14) // 仅age变更时更新
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.


  1. 不可变数据模式(避免隐式渲染):

@State list: number[] = [1, 2, 3]

// 错误:直接修改原数组(触发不必要渲染)
handleSort() {
  this.list.sort() // ❌ 改变原数组
}

// 正确:创建新数组(精确触发更新)
handleSort() {
  this.list = [...this.list].sort() // ✅ 新数组引用
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

六、样式系统:组件封装的视觉规范

样式继承机制(深度解析):

@Component
struct CardComponent {
  build() {
    Column {
      Text('标题').fontSize(20)
      Text('内容').fontSize(14).margin(8)
    }
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(8)
    .shadow(4, 4, 8, Color.Gray.withAlpha(0.1))
  }
}

// 父组件样式覆盖
CardComponent()
  .width('90%') // 容器样式(不可见包裹层)
  .margin({ top: 12, bottom: 12 })
  .backgroundColor(Color.LightGray) // 覆盖内部背景
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

七、实战案例:倒计时组件(完整实现)

// 自定义倒计时组件
@Component
struct CountdownTimer {
  @State seconds: number = 60 // 初始倒计时
  @State isRunning: boolean = false

  // 生命周期方法(模拟)
  onStart() {
    this.isRunning = true
    setInterval(() => {
      if (this.seconds > 0) this.seconds--
      else this.isRunning = false
    }, 1000)
  }

  build() {
    Row {
      Text(`${this.seconds}s`)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .color(this.isRunning ? Color.Red : Color.Gray)
      
      if (this.isRunning) {
        Button('暂停')
          .onClick(() => this.isRunning = false)
      } else {
        Button('开始')
          .onClick(this.onStart)
      }
    }
    .padding(16)
    .borderRadius(12)
    .backgroundColor(Color.White)
  }
}

// 页面使用
@Entry
@Component
struct TimerPage {
  build() {
    Column {
      CountdownTimer() // 默认60秒
        .margin(20)
        
      CountdownTimer({ seconds: 120 }) // 定制为2分钟
        .margin(20)
    }
    .justifyContent(FlexAlign.Center)
    .backgroundColor(Color.Background)
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.

总结:自定义组件的三层境界

  1. 基础层:掌握 @Component/@Entry 的语法规范,实现组件封装
  2. 进阶层:理解状态驱动原理,掌握父子通信模式
  3. 专家层:运用冻结优化、不可变数据等技术,构建高性能 UI 架构


自学写作不易,希望大家喜欢,点赞、关注、收藏~

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
收藏
回复
举报
回复
    相关推荐
    这个用户很懒,还没有个人简介
    帖子
    视频
    声望
    粉丝
    社区精华内容