鸿蒙Next之自定义Component

305 阅读3分钟

一、什么是自定义组件,为什么要自定义组件

通过组合系统组件或抽离公共通用组件的方式,以满足特定需求。这一过程涉及组件的自定义,旨在实现相同UI界面和业务逻辑的复用,从而有效减少重复开发的工作量。

二、自定义组件用的装饰器

对于自定义组件,我们需要简单的了解一下装饰器 HarmonyOS NEXT 中常用的状态管理装饰器总结一下常用的装饰器 这是之前整理的装饰器的文章,欢迎大家阅读。

以下是自定义组件要用到的装饰器:

@Entry:将结构体标记为页面组件,代表一个完整的页面。

@Component:将结构体标记为可复用的组件。

@Preview:让组件能够在开发过程中进行预览。

@State:用于定义组件内部的响应式状态变量需给初始值

@Prop:实现父组件到子组件的数据单向传递。可以给初始值也可以不给

@Link:达成父组件与子组件之间的数据双向传递。不能给初始值

@Builder:用于创建自定义构建函数,对重复的 UI 元素进行抽象。

@BuilderParam:引用自定义构建函数,为组件添加特定功能。

三、创建子组件

使用struct定义的组件内部,通常有一个build方法。这个方法是组件构建的核心部分,它返回组件的 UI 结构。使用@Component将一个struct(或其他符合要求的结构)标记为一个组件。这使得开发框架能够识别这个结构是一个可以在用户界面中进行渲染和交互的单元。

数据传递-父传子:

使用@Proy装饰器实现数据的父组件传递给子组件

ChildComponent子组件:
@Component
export default struct ChildComponent {
  // 接收参数 
  @Prop num: number = 0
  build() {
    Column() {
      Button(`子组件${this.num}`)
        .onClick(() => {
        
        })
    }
  }
}
  • 在这里,@Component装饰器应用于struct ChildComponent,告诉鸿蒙开发框架这个struct定义了一个可以在界面中使用的组件。如果没有这个装饰器,开发框架将不会把ChildComponent当作一个组件来处理,无法在界面构建过程中正确渲染它。
  • 当一个struct@Component装饰后,它能够利用组件相关的各种功能。这包括但不限于数据绑定、事件处理和生命周期管理。
  • 数据绑定方面,组件可以通过@Prop等装饰器来接收外部数据,并且在数据变化时自动更新 UI 显示。
父组件:
import ChildComponent from '../components/ChildComponent'

@Entry
@Component
struct Index {
  @State num:number = 9

  build() {
    RelativeContainer() {
        Column(){
          ChildComponent({num:this.num})
        }
        .alignItems(HorizontalAlign.Center)
        .justifyContent(FlexAlign.Center)
     
    }
    .height('100%')
    .width('100%')
  }
}
Screenshot_2024-11-14T204517.png

使用@Prop就可以实现数据父传子,将父组件的num数据传递给子组件使用

数据传递-子传父:

使用回调函数实现数据传递(子组件向父组件)

子组件:
@Component
export default struct ChildComponent {
  // 接收参数 
  @Prop
  num: number = 0;
  callBack: (v: string) => void = () => {
  };
  build() {
    Column() {
      Button(`子组件${this.num}`)
        .onClick(() => {
            this.callBack("传递给父组件")
        })
    }
  }
}
父组件:
import ChildComponent from '../components/ChildComponent'

@Entry
@Component
struct Index {
  @State num:number = 9
  @State childValue:string = '';
  callBack=(child:string)=>{
    console.log(child)
    this.childValue = child
  }

  build() {
    RelativeContainer() {
        Column(){
          Text(`子传父数据:${this.childValue}`)
          ChildComponent({num:this.num,callBack:this.callBack})
        }
        .alignItems(HorizontalAlign.Center)
        .justifyContent(FlexAlign.Center)
        .height('100%')
        .width('100%')
     
    }
    .height('100%')
    .width('100%')
  }
}

点击子组件的按钮,数据通过callBack函数回调给父组件,实现子传父的场景,将子组件的childValue数据传递给父组件

实现子父组件的双向绑定:

使用@Link装饰器,实现数据的双向绑定

子组件:
@Component
export default struct ChildComponent {
  // 接收参数 
  @Link num: number;
  
  //定义子组件的累加函数
  add=()=>{
      this.num+=2
  }
  
  build() {
    Column() {
      Button(`子组件${this.num}`)
        .onClick(() => {
            this.add()
        })
      Text(`子组件数据v:${this.num}`)
    }
  }
}
父组件:
import ChildComponent from '../components/ChildComponent'

@Entry
@Component
struct Index {
  @State num:number = 9
  
  add = ()=>{
    this.num +=3
  }


  build() {
    RelativeContainer() {
        Column(){
          
          ChildComponent({num:this.num})
         
          Button('父组件数据累加')
           .onClick(this.add)

        }
        .alignItems(HorizontalAlign.Center)
        .justifyContent(FlexAlign.Center)
        .height('100%')
        .width('100%')
     
    }
    .height('100%')
    .width('100%')
  }
}
Screenshot_2024-11-15T105218.png Screenshot_2024-11-15T105223.png Screenshot_2024-11-15T105231.png
  • 第一张图是页面初始化时的数据展示

  • 第二张图是点击父组件的累加按钮数据进行+3子组件数据跟随变化

  • 第三张图是点击子组件的累加按钮数据进行+2父组件数据跟随变化

这就实现了数据的双向绑定。

自定义组件添加扩展:

定义自定义组件可能会存在不同场景,抽离出的公共组件会有部分差异,那么我们就可以把这个差异点交于调用自定义组件时去处理。就类似前端里的插槽功能。这个功能我们可以使用@BuilderParam装饰器

子组件:
@Component
export default struct ChildComponent {
  @BuilderParam contentBuilder: () => void;

  build() {
    Column() {
      Text("自定义组件")
      // 扩展内容
      this.contentBuilder()
    }
    .width('300')
    .height('300')
    .backgroundColor(Color.Gray)
  }
}
父组件:
import ChildComponent from '../components/ChildComponent'

@Entry
@Component
struct Index {
  @State num:number = 9
  
  add = ()=>{
    this.num +=3
  }


  build() {
    RelativeContainer() {
        Column(){
            Text(`父组件`)
            ChildComponent3(){
              Text("子组件 扩展")
            }
        }
        .alignItems(HorizontalAlign.Center)
        .justifyContent(FlexAlign.Center)
        .height('100%')
        .width('100%')
     
    }
    .height('100%')
    .width('100%')
  }
}
Screenshot_2024-11-15T110631.png

这样就可以实现存在细微差异的公共组件,在调用处进行处理。

以上就是鸿蒙Next中的自定义组件,欢迎查阅指正~