鸿蒙装饰器篇二-自定义函数组件、样式抽离、原生组件扩展

403 阅读3分钟

装饰器@Builder

也是用来定义组件,功能类似@Component的基础使用,使用更轻便灵活,但功能比较单一,不支持状态装饰器(上一篇内容),换句话说就是不支持和父组件有交互。使用有以下特性

  • 有两种用法:全局的,组件内部的。
  • 有两种传参方式:按值传递参数,按引用传递参数。
两种类型

作用域的区别,全局使用和组件内部使用

// 全局自定义函数组件
@Builder outBuilder(){
    Row() {
        Text(`outBuilder`)
    }
}

@Entry
@Component
struct Index {
    // 组件内部函数组件
    @Builder inBuilder(){
        Row() {
            Text(`inBuilder`)
        }
    }
    build() {
        Row(){
            outBuilder()
            this.inBuilder()
        }
    }
}
两种传参方式

可以参考数据结构中的基础类型和引用类型。

  • 按值传递:值不再响应传参的变化。
  • 按引用传递:值会响应传参的变化。
// 按值传递参数
@Builder cusBuilder1(p1: string){
    Row() {
        Text(`Builder${p1}`) // 不会响应父组件msg的值的改变
    }
}
// 按引用传递参数
@Builder cusBuilder2($$: {p2: string}){
    Row() {
        Text(`Builder${$$.p2}`) // 会响应父组件msg的值的改变
    }
}

@Entry
@Component
struct Index {
    @State msg: string = '1'
    build() {
        Row(){
            cusBuilder1(this.msg)
            cusBuilder2({p2: this.msg})
            Button(`按钮`)
              .onClick(() => {
                this.msg = '2'
              })
        }
    }
}

有没有react中函数组件和Class组件的赶脚。

扩展用法

内部函数组件猛的一看有点鸡肋,但因为是在组件内部是可以直接调用组件内部的变量。这样既可以抽离出一部分复杂的东西,又不用考虑变量的传值

@Entry
@Component
struct Index {
    @State msg: string = '1'
    // 组件内部函数组件
    @Builder inBuilder(){
        Row() {
            Text(`inBuilder${this.msg}`)
        }
    }
    build() {
        Row(){
            this.inBuilder()
            Button(`按钮`)
              .onClick(() => {
                this.msg = '2'
              })
        }
    }
}

装饰器@Styles

@Style装饰器是提炼公共样式进行复用的装饰器。之前说过样式都是绑定在组件上链式调用,复杂的功能就会很臃肿,且可能会有一些重复的样式。@Style就是用来解决这些问题的。使用有以下特性

  • 有全局和组件内部两种类型,且组件内部定义的优先级高于全局
  • 不支持传参,但组件内部定义的可以调用组件变量
// 定义在全局的@Styles封装的样式
@Styles function globalFancy  () {
  .width(150)
  .height(100)
  .backgroundColor(Color.Pink)
}

@Entry
@Component
struct FancyUse {
  @State heightValue: number = 100
  // 定义在组件内的@Styles封装的样式
  @Styles fancy() {
    .width(200)
    .height(this.heightValue)
    .backgroundColor(Color.Yellow)
    .onClick(() => {
      this.heightValue = 200
    })
  }

  build() {
    Column({ space: 10 }) {
      Text('FancyA')
        .globalFancy()  // 使用全局的@Styles封装的样式
        .fancy()        // 使用组件内的@Styles封装的样式
        .fontSize(30)
    }
  }
}

装饰器@Extend

@Extend是用于扩展原生组件样式的装饰器。测试使用下来的感觉就是弥补了@Style全局模式不能传参的缺点。有以下特性

  • 是对鸿蒙原生组件进行扩展,指定绑定在扩展的对应原生组件上
  • 仅支持全局定义
  • 传值的方式传变量也是可以响应式的
@Extend(Text) function fancy (fontSize: number) {
  .fontColor(Color.Red)
  .fontSize(fontSize)
}

@Entry
@Component
struct FancyUse {
  @State fontSizeValue: number = 20
  build() {
    Row({ space: 10 }) {
      Text('Fancy')
        .fancy(this.fontSizeValue)
        .onClick(() => {
          this.fontSizeValue = 30 // UI可以响应更新
        })
    }
  }
}

补充:在后续测试中发现很多属性不属于通用元素属性,在@Styles中是无法提取的。比如fontColor、fontSize只有在类似Text这种元素组件上可用,如果在@Styles声明就会报错