Laya.Delegate 使用说明

简介

Laya.Delegate 是 LayaAir 引擎中的委托类,用于管理多个回调函数的注册、移除和触发。它提供了一种灵活的事件处理机制,允许将多个监听器绑定到同一个事件上。

核心特性

  • 多监听器支持:同一个委托可以注册多个回调函数
  • target 管理:通过 target 参数管理回调的所有者
  • 一次性回调:支持触发后自动移除的回调
  • 批量清理:支持按 target 清理所有相关回调

与 EventDispatcher 的区别

特性 Laya.Delegate Laya.EventDispatcher
事件类型 无类型区分 支持多种事件类型
监听器数量 支持多个 每种事件类型支持多个
典型场景 自定义回调管理 UI交互、系统事件

目录


API 参考

添加回调

方法 参数 返回值 说明
add(callback, target?, args?) 回调函数, 目标对象, 预设参数 void 添加回调函数
once(callback, target?, args?) 回调函数, 目标对象, 预设参数 void 添加一次性回调,触发后自动移除

移除回调

方法 参数 返回值 说明
remove(callback, target?) 回调函数, 目标对象 void 移除指定的回调函数

触发委托

方法 参数 返回值 说明
invoke(...args) 可变参数 void 触发所有已注册的回调函数

清理委托

方法 参数 返回值 说明
clear() void 清空所有回调函数
clearForTarget(target) 目标对象 void 清空指定 target 的所有回调

属性

属性 类型 说明
count number 获取当前已注册的回调函数数量

基础用法

1. 创建委托实例

// 创建委托
const myDelegate = new Laya.Delegate();

2. 添加回调

// 添加简单回调
myDelegate.add((data: string) => {
    console.log("收到数据:", data);
});

// 带 target 的方式(推荐)
class GameManager {
    public onEvent(data: string): void {
        console.log("处理事件:", data);
    }
}

const manager = new GameManager();
myDelegate.add(manager.onEvent, manager);

3. 触发委托

// 触发所有回调
myDelegate.invoke("Hello World");

// 传递多个参数
myDelegate.invoke("Player", 100, true);

4. 移除回调

// 移除特定回调
myDelegate.remove(callbackFunction);

// 带 target 的移除
myDelegate.remove(manager.onEvent, manager);

5. 清理委托

// 清空所有回调
myDelegate.clear();

// 清空指定 target 的所有回调
myDelegate.clearForTarget(manager);

6. 查看回调数量

console.log("当前回调数量:", myDelegate.count);

实用示例

示例1: 异步任务管理

@regClass()
export class TaskManager extends Laya.Script {
    private onComplete: Laya.Delegate;
    private onProgress: Laya.Delegate;

    onAwake(): void {
        this.onComplete = new Laya.Delegate();
        this.onProgress = new Laya.Delegate();

        // 注册监听器
        this.onComplete.add(this.handleComplete, this);
        this.onProgress.add(this.handleProgress, this);
    }

    public loadResource(): void {
        let progress = 0;

        const timer = setInterval(() => {
            progress += 20;
            this.onProgress.invoke(progress);

            if (progress >= 100) {
                clearInterval(timer);
                this.onComplete.invoke();
            }
        }, 500);
    }

    private handleProgress(value: number): void {
        console.log("加载进度:", value + "%");
    }

    private handleComplete(): void {
        console.log("加载完成!");
    }

    onDestroy(): void {
        this.onComplete.clear();
        this.onProgress.clear();
    }
}

示例2: 一次性回调

@regClass()
export class OneTimeEvent extends Laya.Script {
    private eventDelegate: Laya.Delegate;

    onAwake(): void {
        this.eventDelegate = new Laya.Delegate();

        // 添加一次性回调
        this.eventDelegate.once(() => {
            console.log("首次触发");
        });

        // 第一次触发 - 会执行
        this.eventDelegate.invoke();
        console.log("回调数量:", this.eventDelegate.count); // 0

        // 第二次触发 - 不会执行
        this.eventDelegate.invoke();
    }
}

示例3: 带预设参数的回调

@regClass()
export class ButtonGroup extends Laya.Script {
    private clickDelegate: Laya.Delegate;

    onAwake(): void {
        this.clickDelegate = new Laya.Delegate();

        // 添加带预设参数的回调
        this.clickDelegate.add(
            this.onButtonClick,
            this,
            ["button_001", "预设信息"]
        );
    }

    public triggerClick(): void {
        // 不传递参数,使用预设参数
        this.clickDelegate.invoke();
    }

    private onButtonClick(buttonId: string, extra: string): void {
        console.log(`点击了: ${buttonId}, ${extra}`);
    }
}

示例4: 事件管理器

export class EventManager {
    private static instance: EventManager;
    private events: Map<string, Laya.Delegate>;

    private constructor() {
        this.events = new Map();
    }

    public static getInstance(): EventManager {
        if (!EventManager.instance) {
            EventManager.instance = new EventManager();
        }
        return EventManager.instance;
    }

    public on(eventName: string, callback: Function, target?: any): void {
        if (!this.events.has(eventName)) {
            this.events.set(eventName, new Laya.Delegate());
        }
        this.events.get(eventName).add(callback, target);
    }

    public once(eventName: string, callback: Function, target?: any): void {
        if (!this.events.has(eventName)) {
            this.events.set(eventName, new Laya.Delegate());
        }
        this.events.get(eventName).once(callback, target);
    }

    public off(eventName: string, callback: Function, target?: any): void {
        const delegate = this.events.get(eventName);
        if (delegate) {
            delegate.remove(callback, target);
        }
    }

    public emit(eventName: string, ...args: any[]): void {
        const delegate = this.events.get(eventName);
        if (delegate) {
            delegate.invoke(...args);
        }
    }

    public offAllTarget(target: any): void {
        this.events.forEach((delegate) => {
            delegate.clearForTarget(target);
        });
    }

    public clear(): void {
        this.events.forEach((delegate) => delegate.clear());
        this.events.clear();
    }
}

// 使用示例
@regClass()
export class Player extends Laya.Script {
    private name: string;

    constructor(name: string) {
        super();
        this.name = name;
    }

    public onPlayerDie(reason: string): void {
        console.log(`${this.name} 死亡: ${reason}`);
    }
}

@regClass()
export class GameMain extends Laya.Script {
    private eventManager: EventManager;
    private players: Player[] = [];

    onAwake(): void {
        this.eventManager = EventManager.getInstance();

        // 创建玩家
        this.players.push(new Player("张三") as any);
        this.players.push(new Player("李四") as any);

        // 注册事件
        this.eventManager.on("playerDie", this.players[0].onPlayerDie, this.players[0]);
        this.eventManager.on("playerDie", this.players[1].onPlayerDie, this.players[1]);
    }

    public killPlayer(index: number): void {
        this.eventManager.emit("playerDie", "中毒");
    }

    public removePlayer(index: number): void {
        const player = this.players[index];
        this.eventManager.offAllTarget(player);
    }
}

示例5: 可观察属性

@regClass()
export class ObservableValue extends Laya.Script {
    private _value: number;
    private onChange: Laya.Delegate;

    onAwake(): void {
        this._value = 0;
        this.onChange = new Laya.Delegate();
    }

    public get value(): number {
        return this._value;
    }

    public set value(v: number) {
        if (this._value !== v) {
            this._value = v;
            this.onChange.invoke(v);
        }
    }

    public watch(callback: (value: number) => void, target?: any): void {
        this.onChange.add(callback, target);
    }
}

// 使用
@regClass()
export class UIController extends Laya.Script {
    declare owner: Laya.Sprite;

    private healthValue: ObservableValue;

    onAwake(): void {
        this.healthValue = new ObservableValue();

        // 监听值变化
        this.healthValue.watch((value: number) => {
            console.log("血量变化:", value);
            this.updateHPBar(value);
        }, this);
    }

    private updateHPBar(value: number): void {
        // 更新血条显示
    }

    public setHealth(value: number): void {
        this.healthValue.value = value;
    }
}

高级技巧

1. 链式调用

// Delegate 方法不返回 this,不支持链式调用
// ❌ 错误
myDelegate.add(cb1).add(cb2);

// ✅ 正确
myDelegate.add(cb1);
myDelegate.add(cb2);

2. 条件移除

class CallbackManager {
    private callbacks: Function[] = [];

    public add(callback: Function): void {
        this.callbacks.push(callback);
    }

    public remove(callback: Function): void {
        const index = this.callbacks.indexOf(callback);
        if (index !== -1) {
            this.callbacks.splice(index, 1);
        }
    }
}

3. 动态参数组合

@regClass()
export class DynamicArgs extends Laya.Script {
    private delegate: Laya.Delegate;

    onAwake(): void {
        this.delegate = new Laya.Delegate();

        // 预设参数 + invoke 参数组合
        this.delegate.add(
            (preset: string, dynamic: number) => {
                console.log(`预设: ${preset}, 动态: ${dynamic}`);
            },
            this,
            ["预设值"]
        );
    }

    public trigger(value: number): void {
        // 预设参数会自动传递
        this.delegate.invoke(value);
    }
}

4. 防抖委托

@regClass()
export class DebounceDelegate extends Laya.Script {
    private delegate: Laya.Delegate;
    private timer: number = null;
    private delay: number = 300;

    constructor(delay: number = 300) {
        super();
        this.delay = delay;
        this.delegate = new Laya.Delegate();
    }

    public add(callback: Function, target?: any): void {
        this.delegate.add(callback, target);
    }

    public invoke(...args: any[]): void {
        if (this.timer !== null) {
            clearTimeout(this.timer);
        }

        this.timer = setTimeout(() => {
            this.delegate.invoke(...args);
            this.timer = null;
        }, this.delay);
    }

    public destroy(): void {
        if (this.timer !== null) {
            clearTimeout(this.timer);
        }
        this.delegate.clear();
    }
}

最佳实践

1. 始终使用 target 参数

// ✅ 推荐:可以正确移除
myDelegate.add(this.onCallback, this);
myDelegate.remove(this.onCallback, this);

// ❌ 不推荐:移除可能失败
myDelegate.add(this.onCallback);
myDelegate.remove(this.onCallback);

2. 及时清理委托

@regClass()
export class MyComponent extends Laya.Script {
    private delegate: Laya.Delegate;

    onAwake(): void {
        this.delegate = new Laya.Delegate();
        this.delegate.add(this.onCallback, this);
    }

    onDestroy(): void {
        // ✅ 推荐:清理委托
        this.delegate.clear();
        this.delegate = null;

        // ❌ 避免:忘记清理
    }
}

3. 使用一次性回调简化代码

// ❌ 不推荐:手动移除
this.delegate.add(this.onFirstClick, this);
// ...回调中手动移除
private onFirstClick(): void {
    this.delegate.remove(this.onFirstClick, this);
}

// ✅ 推荐:使用 once
this.delegate.once(this.onFirstClick, this);

4. 避免循环引用

@regClass()
export class SafeDelegate extends Laya.Script {
    private delegate: Laya.Delegate;

    onAwake(): void {
        this.delegate = new Laya.Delegate();
        this.delegate.add(this.onCallback, this);
    }

    private onCallback(): void {
        // ✅ 安全:不持有外部引用
        console.log("Callback");
    }

    onDestroy(): void {
        // 清理委托,释放引用
        this.delegate.clear();
    }
}

5. 委托链管理

export class DelegateChain {
    private delegates: Laya.Delegate[] = [];

    public add(delegate: Laya.Delegate): void {
        this.delegates.push(delegate);
    }

    public invoke(...args: any[]): void {
        this.delegates.forEach(delegate => {
            delegate.invoke(...args);
        });
    }

    public clear(): void {
        this.delegates.forEach(delegate => delegate.clear());
        this.delegates = [];
    }
}

6. 条件回调

@regClass()
export class ConditionalDelegate extends Laya.Script {
    private delegate: Laya.Delegate;
    private condition: () => boolean;

    constructor(condition: () => boolean) {
        super();
        this.delegate = new Laya.Delegate();
        this.condition = condition;
    }

    public add(callback: Function, target?: any): void {
        this.delegate.add(callback, target);
    }

    public invoke(...args: any[]): void {
        if (this.condition()) {
            this.delegate.invoke(...args);
        }
    }

    public clear(): void {
        this.delegate.clear();
    }
}

注意事项

  1. target 参数:强烈建议添加时传入 target,便于后续管理和移除
  2. 内存泄漏:使用完毕后必须清理委托,避免内存泄漏
  3. 重复添加:同一个回调可以重复添加,会被多次调用
  4. 执行顺序:回调按照添加顺序依次执行
  5. 异常处理:一个回调中的异常不会影响其他回调的执行
  6. 线程安全:回调在同一线程中同步执行

相关文档

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐