鸿蒙开发学习笔记——自定义组件

自定义组件能够复用,提高编程效率,开头不需要加@Entry,表明不是入口组件,不能直接展示,要被入口组件复用才可展示。

比如我们创建的两个页面,它们的头部是差不多的,那我们就可以把头部单独抽取出来做成一个组件,在写页面时复用即可,要注意不要将需要改变的部分写死。

一、例子

我们将页面头部自定义成一个组件,将来在别的页面中也能使用。

1.导入图标资源

要实现这个效果,首先我们需要导入图标资源至entry/src/main/resources/base/media:

2.创建头部组件

1.文件以及目录创建

首先在ets文件下新建一个components文件夹用来存放组件,再在此文件夹下新建一个.ets文档来写头部组件。

这里我新建一个名为CommonComponent来写通用组件。

2.编写头部组件

@Component
// export 组件可导出
export struct Header {

  private title: string

  build(){
    // 标题
    Row(){
      Image($r('app.media.ic_public_arrow_left'))
        .width(50)
      Text(this.title)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      // 空白
      Blank()
      Image($r('app.media.ic_public_refresh'))
        .width(30)
    }
    .width('100%')
    .height(30)
  }
}

3.页面引入该组件

目录结构如下

在Index.ets中导入Header()

import { Header } from '../components/CommonComponent'

使用Header()

// 标题部分
      Header({ title: '商品列表' })
        .margin({ bottom: 20 })

完整代码如下:

// 定义商品类
class Item {
  name: string
  image: ResourceStr
  price: number
  discount: number

  // discount初始化为0
  constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
    this.name = name
    this.image = image
    this.price = price
    this.discount = discount
  }
}


import { Header } from '../components/CommonComponent'

@Entry
@Component
struct Index {
  private items: Array<Item> = [
    new Item('Pocket系列', $r('app.media.Pocket'), 7499, 500),
    new Item('Mate系列', $r('app.media.Mate'), 6499),
    new Item('P系列', $r('app.media.P'), 6988),
    new Item('nova系列', $r('app.media.nova'), 4699),
    new Item('畅想系列', $r('app.media.nav'), 1499),
    new Item('畅想系列', $r('app.media.nav'), 1499)
  ]

  build() {
    Column({ space: 8 }) {

      // 标题部分
      Header({ title: '商品列表' })
        .margin({ bottom: 20 })

      // 定义列表
      List({ space: 8 }) {
        ForEach(
          this.items,
          // 也可以写成item => {},但因为传进来的item类型为any,所以没有提示,声明变量类型后就有提示了
          (item: Item) => {
            ListItem() {
              Row({ space: 10 }) {
                Image(item.image)
                  .width(100)
                Column({ space: 4 }) {
                  // 如果有折扣 渲染只有这里有变化
                  if (item.discount) {
                    Text(item.name)
                      .fontSize(20)
                      .fontWeight(FontWeight.Bold)
                    Text('原价:¥' + item.price)
                      .fontColor('#CCC')
                      .fontSize(14)
                      .decoration({ type: TextDecorationType.LineThrough })
                    Text('折扣价:¥' + (item.price - item.discount))
                      .fontColor('#F36')
                      .fontSize(18)
                    Text('补贴:¥' + item.discount)
                      .fontColor('#F36')
                      .fontSize(18)
                  } else {
                    Text(item.name)
                      .fontSize(20)
                      .fontWeight(FontWeight.Bold)
                    Text('¥' + item.price)
                      .fontColor('#F36')
                      .fontSize(18)
                  }
                }
                .height('100%')
                .alignItems(HorizontalAlign.Start)
              }
              .width('100%')
              .backgroundColor('#FFF')
              .borderRadius(20)
              .height(120)
              .padding(10)
            }
          }
        )

      }
      .width('100%')
      .layoutWeight(1) // 权重配比

    }
    .width('100%')
    .height('100%')
  }
}

效果图:

3.进一步优化

我们发现上面代码可读性还是很差,中间商品展示代码可用自定义组件展示,也可以用自定义构建函数封装(适合做内部页面封装)。

3.1自定义构造函数封装代码

1)全局自定义构造函数
// 定义商品类
class Item {
  name: string
  image: ResourceStr
  price: number
  discount: number

  // discount初始化为0
  constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
    this.name = name
    this.image = image
    this.price = price
    this.discount = discount
  }
}


import { Header } from '../components/CommonComponent'

// 全局自定义构造函数
@Builder function ItemCard(item: Item) {
  Row({ space: 10 }) {
    Image(item.image)
      .width(100)
    Column({ space: 4 }) {
      // 如果有折扣 渲染只有这里有变化
      if (item.discount) {
        Text(item.name)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        Text('原价:¥' + item.price)
          .fontColor('#CCC')
          .fontSize(14)
          .decoration({ type: TextDecorationType.LineThrough })
        Text('折扣价:¥' + (item.price - item.discount))
          .fontColor('#F36')
          .fontSize(18)
        Text('补贴:¥' + item.discount)
          .fontColor('#F36')
          .fontSize(18)
      } else {
        Text(item.name)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        Text('¥' + item.price)
          .fontColor('#F36')
          .fontSize(18)
      }
    }
    .height('100%')
    .alignItems(HorizontalAlign.Start)
  }
  .width('100%')
  .backgroundColor('#FFF')
  .borderRadius(20)
  .height(120)
  .padding(10)
}

@Entry
@Component
struct Index {
  private items: Array<Item> = [
    new Item('Pocket系列', $r('app.media.Pocket'), 7499, 500),
    new Item('Mate系列', $r('app.media.Mate'), 6499),
    new Item('P系列', $r('app.media.P'), 6988),
    new Item('nova系列', $r('app.media.nova'), 4699),
    new Item('畅想系列', $r('app.media.nav'), 1499),
    new Item('畅想系列', $r('app.media.nav'), 1499)
  ]

  build() {
    Column({ space: 8 }) {

      // 标题部分
      Header({ title: '商品列表' })
        .margin({ bottom: 20 })

      // 商品列表部分
      List({ space: 8 }) {
        ForEach(
          this.items,
          // 也可以写成item => {},但因为传进来的item类型为any,所以没有提示,声明变量类型后就有提示了
          (item: Item) => {
            ListItem() {
              // 使用自定义构建函数
              ItemCard(item)
            }
          }
        )
      }
      .width('100%')
      .layoutWeight(1) // 权重配比

    }
    .width('100%')
    .height('100%')
  }
}
2)局部自定义构造函数
// 定义商品类
class Item {
  name: string
  image: ResourceStr
  price: number
  discount: number

  // discount初始化为0
  constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
    this.name = name
    this.image = image
    this.price = price
    this.discount = discount
  }
}


import { Header } from '../components/CommonComponent'

/*// 全局自定义构造函数
@Builder function ItemCard(item: Item) {
  Row({ space: 10 }) {
    Image(item.image)
      .width(100)
    Column({ space: 4 }) {
      // 如果有折扣 渲染只有这里有变化
      if (item.discount) {
        Text(item.name)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        Text('原价:¥' + item.price)
          .fontColor('#CCC')
          .fontSize(14)
          .decoration({ type: TextDecorationType.LineThrough })
        Text('折扣价:¥' + (item.price - item.discount))
          .fontColor('#F36')
          .fontSize(18)
        Text('补贴:¥' + item.discount)
          .fontColor('#F36')
          .fontSize(18)
      } else {
        Text(item.name)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        Text('¥' + item.price)
          .fontColor('#F36')
          .fontSize(18)
      }
    }
    .height('100%')
    .alignItems(HorizontalAlign.Start)
  }
  .width('100%')
  .backgroundColor('#FFF')
  .borderRadius(20)
  .height(120)
  .padding(10)
}*/

@Entry
@Component
struct Index {
  private items: Array<Item> = [
    new Item('Pocket系列', $r('app.media.Pocket'), 7499, 500),
    new Item('Mate系列', $r('app.media.Mate'), 6499),
    new Item('P系列', $r('app.media.P'), 6988),
    new Item('nova系列', $r('app.media.nova'), 4699),
    new Item('畅想系列', $r('app.media.nav'), 1499),
    new Item('畅想系列', $r('app.media.nav'), 1499)
  ]

  build() {
    Column({ space: 8 }) {

      // 标题部分
      Header({ title: '商品列表' })
        .margin({ bottom: 20 })

      // 商品列表部分
      List({ space: 8 }) {
        ForEach(
          this.items,
          // 也可以写成item => {},但因为传进来的item类型为any,所以没有提示,声明变量类型后就有提示了
          (item: Item) => {
            ListItem() {
              // 使用自定义构建函数
              this.ItemCard(item)
            }
          }
        )
      }
      .width('100%')
      .layoutWeight(1) // 权重配比

    }
    .width('100%')
    .height('100%')
  }
  // 局部自定义构造函数 不加function
  @Builder ItemCard(item: Item) {
    Row({ space: 10 }) {
      Image(item.image)
        .width(100)
      Column({ space: 4 }) {
        // 如果有折扣 渲染只有这里有变化
        if (item.discount) {
          Text(item.name)
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
          Text('原价:¥' + item.price)
            .fontColor('#CCC')
            .fontSize(14)
            .decoration({ type: TextDecorationType.LineThrough })
          Text('折扣价:¥' + (item.price - item.discount))
            .fontColor('#F36')
            .fontSize(18)
          Text('补贴:¥' + item.discount)
            .fontColor('#F36')
            .fontSize(18)
        } else {
          Text(item.name)
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
          Text('¥' + item.price)
            .fontColor('#F36')
            .fontSize(18)
        }
      }
      .height('100%')
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .backgroundColor('#FFF')
    .borderRadius(20)
    .height(120)
    .padding(10)
  }
}

3.2样式优化

不管是全局的还是局部的,只能封装公共样式,组件特有的样式不行。

1)全局公共样式
// 定义商品类
class Item {
  name: string
  image: ResourceStr
  price: number
  discount: number

  // discount初始化为0
  constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
    this.name = name
    this.image = image
    this.price = price
    this.discount = discount
  }
}


import { Header } from '../components/CommonComponent'

/*// 全局自定义构造函数
@Builder function ItemCard(item: Item) {
  Row({ space: 10 }) {
    Image(item.image)
      .width(100)
    Column({ space: 4 }) {
      // 如果有折扣 渲染只有这里有变化
      if (item.discount) {
        Text(item.name)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        Text('原价:¥' + item.price)
          .fontColor('#CCC')
          .fontSize(14)
          .decoration({ type: TextDecorationType.LineThrough })
        Text('折扣价:¥' + (item.price - item.discount))
          .fontColor('#F36')
          .fontSize(18)
        Text('补贴:¥' + item.discount)
          .fontColor('#F36')
          .fontSize(18)
      } else {
        Text(item.name)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        Text('¥' + item.price)
          .fontColor('#F36')
          .fontSize(18)
      }
    }
    .height('100%')
    .alignItems(HorizontalAlign.Start)
  }
  .width('100%')
  .backgroundColor('#FFF')
  .borderRadius(20)
  .height(120)
  .padding(10)
}*/

// 全局公共样式
@Styles function fillScreen() {
  .width('100%')
  .height('100%')
}

@Entry
@Component
struct Index {
  private items: Array<Item> = [
    new Item('Pocket系列', $r('app.media.Pocket'), 7499, 500),
    new Item('Mate系列', $r('app.media.Mate'), 6499),
    new Item('P系列', $r('app.media.P'), 6988),
    new Item('nova系列', $r('app.media.nova'), 4699),
    new Item('畅想系列', $r('app.media.nav'), 1499),
    new Item('畅想系列', $r('app.media.nav'), 1499)
  ]

  build() {
    Column({ space: 8 }) {

      // 标题部分
      Header({ title: '商品列表' })
        .margin({ bottom: 20 })

      // 商品列表部分
      List({ space: 8 }) {
        ForEach(
          this.items,
          // 也可以写成item => {},但因为传进来的item类型为any,所以没有提示,声明变量类型后就有提示了
          (item: Item) => {
            ListItem() {
              // 使用自定义构建函数
              this.ItemCard(item)
            }
          }
        )
      }
      .width('100%')
      .layoutWeight(1) // 权重配比

    }
    .fillScreen()
  }
  // 局部自定义构造函数 不加function
  @Builder ItemCard(item: Item) {
    Row({ space: 10 }) {
      Image(item.image)
        .width(100)
      Column({ space: 4 }) {
        // 如果有折扣 渲染只有这里有变化
        if (item.discount) {
          Text(item.name)
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
          Text('原价:¥' + item.price)
            .fontColor('#CCC')
            .fontSize(14)
            .decoration({ type: TextDecorationType.LineThrough })
          Text('折扣价:¥' + (item.price - item.discount))
            .fontColor('#F36')
            .fontSize(18)
          Text('补贴:¥' + item.discount)
            .fontColor('#F36')
            .fontSize(18)
        } else {
          Text(item.name)
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
          Text('¥' + item.price)
            .fontColor('#F36')
            .fontSize(18)
        }
      }
      .height('100%')
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .backgroundColor('#FFF')
    .borderRadius(20)
    .height(120)
    .padding(10)
  }
}
2)局部公共样式
// 定义商品类
class Item {
  name: string
  image: ResourceStr
  price: number
  discount: number

  // discount初始化为0
  constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
    this.name = name
    this.image = image
    this.price = price
    this.discount = discount
  }
}


import { Header } from '../components/CommonComponent'

/*// 全局自定义构造函数
@Builder function ItemCard(item: Item) {
  Row({ space: 10 }) {
    Image(item.image)
      .width(100)
    Column({ space: 4 }) {
      // 如果有折扣 渲染只有这里有变化
      if (item.discount) {
        Text(item.name)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        Text('原价:¥' + item.price)
          .fontColor('#CCC')
          .fontSize(14)
          .decoration({ type: TextDecorationType.LineThrough })
        Text('折扣价:¥' + (item.price - item.discount))
          .fontColor('#F36')
          .fontSize(18)
        Text('补贴:¥' + item.discount)
          .fontColor('#F36')
          .fontSize(18)
      } else {
        Text(item.name)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        Text('¥' + item.price)
          .fontColor('#F36')
          .fontSize(18)
      }
    }
    .height('100%')
    .alignItems(HorizontalAlign.Start)
  }
  .width('100%')
  .backgroundColor('#FFF')
  .borderRadius(20)
  .height(120)
  .padding(10)
}*/

/*// 全局公共样式
@Styles function fillScreen() {
  .width('100%')
  .height('100%')
}*/

@Entry
@Component
struct Index {
  private items: Array<Item> = [
    new Item('Pocket系列', $r('app.media.Pocket'), 7499, 500),
    new Item('Mate系列', $r('app.media.Mate'), 6499),
    new Item('P系列', $r('app.media.P'), 6988),
    new Item('nova系列', $r('app.media.nova'), 4699),
    new Item('畅想系列', $r('app.media.nav'), 1499),
    new Item('畅想系列', $r('app.media.nav'), 1499)
  ]

  // 局部公共样式
  @Styles fillScreen() {
    .width('100%')
    .height('100%')
  }

  build() {
    Column({ space: 8 }) {

      // 标题部分
      Header({ title: '商品列表' })
        .margin({ bottom: 20 })

      // 商品列表部分
      List({ space: 8 }) {
        ForEach(
          this.items,
          // 也可以写成item => {},但因为传进来的item类型为any,所以没有提示,声明变量类型后就有提示了
          (item: Item) => {
            ListItem() {
              // 使用自定义构建函数
              this.ItemCard(item)
            }
          }
        )
      }
      .width('100%')
      .layoutWeight(1) // 权重配比

    }
    .fillScreen()
  }
  // 局部自定义构造函数 不加function
  @Builder ItemCard(item: Item) {
    Row({ space: 10 }) {
      Image(item.image)
        .width(100)
      Column({ space: 4 }) {
        // 如果有折扣 渲染只有这里有变化
        if (item.discount) {
          Text(item.name)
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
          Text('原价:¥' + item.price)
            .fontColor('#CCC')
            .fontSize(14)
            .decoration({ type: TextDecorationType.LineThrough })
          Text('折扣价:¥' + (item.price - item.discount))
            .fontColor('#F36')
            .fontSize(18)
          Text('补贴:¥' + item.discount)
            .fontColor('#F36')
            .fontSize(18)
        } else {
          Text(item.name)
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
          Text('¥' + item.price)
            .fontColor('#F36')
            .fontSize(18)
        }
      }
      .height('100%')
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .backgroundColor('#FFF')
    .borderRadius(20)
    .height(120)
    .padding(10)
  }
}

如果我们要对组件特有组件进行封装,则不能使用@Style,使用@Extend()

3)特有属性封装

@Extend不能写在组件内

// 定义商品类
class Item {
  name: string
  image: ResourceStr
  price: number
  discount: number

  // discount初始化为0
  constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
    this.name = name
    this.image = image
    this.price = price
    this.discount = discount
  }
}


import { Header } from '../components/CommonComponent'

/*// 全局自定义构造函数
@Builder function ItemCard(item: Item) {
  Row({ space: 10 }) {
    Image(item.image)
      .width(100)
    Column({ space: 4 }) {
      // 如果有折扣 渲染只有这里有变化
      if (item.discount) {
        Text(item.name)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        Text('原价:¥' + item.price)
          .fontColor('#CCC')
          .fontSize(14)
          .decoration({ type: TextDecorationType.LineThrough })
        Text('折扣价:¥' + (item.price - item.discount))
          .fontColor('#F36')
          .fontSize(18)
        Text('补贴:¥' + item.discount)
          .fontColor('#F36')
          .fontSize(18)
      } else {
        Text(item.name)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        Text('¥' + item.price)
          .fontColor('#F36')
          .fontSize(18)
      }
    }
    .height('100%')
    .alignItems(HorizontalAlign.Start)
  }
  .width('100%')
  .backgroundColor('#FFF')
  .borderRadius(20)
  .height(120)
  .padding(10)
}*/

/*// 全局公共样式
@Styles function fillScreen() {
  .width('100%')
  .height('100%')
}*/

// 继承模式,只能写在全局
@Extend(Text) function priceText() {
  .fontColor('#F36')
  .fontSize(18)

}

@Entry
@Component
struct Index {
  private items: Array<Item> = [
    new Item('Pocket系列', $r('app.media.Pocket'), 7499, 500),
    new Item('Mate系列', $r('app.media.Mate'), 6499),
    new Item('P系列', $r('app.media.P'), 6988),
    new Item('nova系列', $r('app.media.nova'), 4699),
    new Item('畅想系列', $r('app.media.nav'), 1499),
    new Item('畅想系列', $r('app.media.nav'), 1499)
  ]

  // 局部公共样式
  @Styles fillScreen() {
    .width('100%')
    .height('100%')
  }

  build() {
    Column({ space: 8 }) {

      // 标题部分
      Header({ title: '商品列表' })
        .margin({ bottom: 20 })

      // 商品列表部分
      List({ space: 8 }) {
        ForEach(
          this.items,
          // 也可以写成item => {},但因为传进来的item类型为any,所以没有提示,声明变量类型后就有提示了
          (item: Item) => {
            ListItem() {
              // 使用自定义构建函数
              this.ItemCard(item)
            }
          }
        )
      }
      .width('100%')
      .layoutWeight(1) // 权重配比

    }
    .fillScreen()
  }
  // 局部自定义构造函数 不加function
  @Builder ItemCard(item: Item) {
    Row({ space: 10 }) {
      Image(item.image)
        .width(100)
      Column({ space: 4 }) {
        // 如果有折扣 渲染只有这里有变化
        if (item.discount) {
          Text(item.name)
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
          Text('原价:¥' + item.price)
            .fontColor('#CCC')
            .fontSize(14)
            .decoration({ type: TextDecorationType.LineThrough })
          Text('折扣价:¥' + (item.price - item.discount))
            .priceText()
          Text('补贴:¥' + item.discount)
            .priceText()
        } else {
          Text(item.name)
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
          Text('¥' + item.price)
            .priceText()
        }
      }
      .height('100%')
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .backgroundColor('#FFF')
    .borderRadius(20)
    .height(120)
    .padding(10)
  }
}

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于 jQuery 的事件,我可以和你分享一些学习笔记。 1. 绑定事件 在 jQuery 中,我们可以通过以下方式来绑定事件: ``` $(selector).event(function(){ // 事件处理程序 }) ``` 其中,`selector` 表示要绑定事件的元素,`event` 表示要绑定的事件类型,比如 `click`、`mouseover` 等等。事件处理程序则是在事件触发时要执行的代码块。 2. 多个事件绑定 我们可以通过 `on()` 方法来同时绑定多个事件: ``` $(selector).on({ event1: function(){ // 事件处理程序1 }, event2: function(){ // 事件处理程序2 } }) ``` 这样,当 `event1` 或 `event2` 中任意一个事件触发时,对应的处理程序都会被执行。 3. 解除事件 如果需要解除某个元素的事件处理程序,可以使用 `off()` 方法: ``` $(selector).off(event); ``` 其中,`event` 表示要解除的事件类型。如果不指定事件类型,则会解除该元素上所有的事件处理程序。 4. 事件委托 在 jQuery 中,我们可以使用事件委托来提高性能。事件委托是指将事件绑定到父元素上,而不是绑定到子元素上,然后通过事件冒泡来判断是哪个子元素触发了该事件。这样,当子元素数量较多时,只需要绑定一次事件,就可以监听到所有子元素的事件。 ``` $(selector).on(event, childSelector, function(){ // 事件处理程序 }) ``` 其中,`selector` 表示父元素,`event` 表示要绑定的事件类型,`childSelector` 表示要委托的子元素的选择器,事件处理程序则是在子元素触发事件时要执行的代码块。 以上是 jQuery 中事件的一些基本操作,希望对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值