安卓开发者自学鸿蒙开发4自定义组件
先来个最简例子
// 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 });
});