先来个最简例子

// 1. 定义自定义组件,其实跟页面的定义差异不大,页面也是一个组件,万物兼组件


@Component
struct MyButton {
  // 组件属性,用prop表示可以外部传入参数值,state则为内部独有数据
  @Prop text: string = '按钮';
  
  // 事件回调
  private onClick: () => void = () => {};
  
  // 必须的build方法
  build() {
    Button(this.text)
      .onClick(() => this.onClick())
  }
}

// 2. 使用自定义组件


@Entry
@Component
struct MyPage {
  @State count: number = 0;
  
  // 按钮点击处理
  handleClick() {
    this.count++;
    console.log('点击次数:', this.count);
  }
  
  build() {
    Column() {
      Text(`计数: ${this.count}`)
      
      // 使用自定义组件
      MyButton({
        text: '点我增加',
        onClick: this.handleClick.bind(this) // 绑定事件
      })
    }
  }
}

插槽使用

注意定义的时候用的@BuilderParam 以及他的定义方式和默认值(title: string) => void = () => {};这个格式是固定的
使用的时候就当做一个普通的预设组件是几乎完全相同的

// 1. 定义组件
@Component
struct DualSlot {
  // 带参数插槽
  @BuilderParam header: (title: string) => void = () => {};
  
  // 不带参数插槽
  @BuilderParam content: () => void = () => {};

  build() {
    Column() {
      // 调用带参数插槽
      this.header("标题")
      
      // 调用不带参数插槽
      this.content()
    }
  }
}

// 2. 使用组件
@Entry
@Component
struct MainPage {
  build() {
    DualSlot()
      // 带参数插槽使用
      .header((title: string) => {
        Text(title) // 使用传入的参数
      })
      
      // 不带参数插槽使用
      .content(() => {
        Text("内容")
      })
  }
}

父子组件数据传递

// 1. 子组件(包含三种接收方式)
@Component
struct Child {
  // 1. 双向绑定
  @Link linkValue: number;
  
  // 2. 单向传递
  @Prop propValue: number;
  
  // 3. 只读传递
  normalValue: number;

  build() {
    Column() {
      // 双向绑定组件
      Button(`双向: ${this.linkValue}`)
        .onClick(() => this.linkValue++) // 可修改父组件数据
        
      // 单向传递组件
      Button(`单向: ${this.propValue}`)
        .onClick(() => {
          this.propValue++; // 只能修改本地副本
        })
        
      // 只读传递组件
      Text(`只读: ${this.normalValue}`)
    }
  }
}

// 2. 父组件
@Entry
@Component
struct Parent {
  @State count: number = 0;

  build() {
    Column() {
      Text(`父组件值: ${this.count}`)
        .fontSize(20)
        .margin(20)
      
      // 传递三种类型数据
      Child({
        linkValue: $count,    // 双向绑定(使用$)
        propValue: this.count, // 单向传递
        normalValue: this.count // 只读传递
      })
      
      Button('父组件增加')
        .onClick(() => this.count++)
        .margin(20)
    }
  }
}
传递方式 装饰器 方向 子组件能否修改 父组件更新 代码示例
双向绑定 @Link 父 ↔ 子 自动同步 Child({ value: $count })
单向传递 @Prop 父 → 子 是(仅本地) 不自动更新 Child({ value: this.count })
只读传递 无装饰器 父 → 子 不自动更新(因为没绑定到组件上) Child({ value: this.count })

祖孙组件通信:@Provide 和 @Consume

// 1. 祖先组件
@Component
struct Grandparent {
  @Provide theme: string = 'light';
  
  build() {
    Column() {
      Text('祖先组件').fontSize(20)
      Parent()
    }
  }
}

// 2. 父组件(中间层)
@Component
struct Parent {
  build() {
    Column() {
      Text('父组件').fontSize(18)
      Child()
    }
  }
}

// 3. 子孙组件
@Component
struct Child {
  @Consume theme: string;
  
  build() {
    Text(`当前主题: ${this.theme}`)
      .fontColor(this.theme === 'light' ? '#000' : '#FFF')
  }
}

事件总线实现数据更新

简单实现

// eventBus.ts
type EventHandler = (data: any) => void;

class EventBus {
  private static events: Map<string, EventHandler[]> = new Map();
  
  // 订阅事件
  static on(event: string, handler: EventHandler) {
    if (!this.events.has(event)) {
      this.events.set(event, []);
    }
    this.events.get(event)?.push(handler);
  }
  
  // 取消订阅
  static off(event: string, handler?: EventHandler) {
    if (!handler) {
      this.events.delete(event);
      return;
    }
    
    const handlers = this.events.get(event);
    if (handlers) {
      const index = handlers.indexOf(handler);
      if (index > -1) {
        handlers.splice(index, 1);
      }
    }
  }
  
  // 发布事件
  static emit(event: string, data?: any) {
    const handlers = this.events.get(event);
    if (handlers) {
      handlers.forEach(handler => handler(data));
    }
  }
}

export default EventBus;

使用

import EventBus from './eventBus';

// 在组件A中
Button('发送事件').onClick(() => {
  EventBus.emit('DATA_CHANGE', { value: 100 });
});
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐