cocos creator教程:框架 - 事件

【muzzik 教程】:框架 - 事件

相信大多数人经常用到事件,那么我们真正需要的其实是什么呢?简单,安全 有这两个就够了

简单

为什么简单,因为我们直接继承了 cc.EventTarget,这是我们 cocos 开发者接触最多的事件类型了

安全

直接上图更有说服力

  1. 具有事件键提示及注释
    image

  2. 事件参数注释
    image

  3. 类型安全
    image

两种实现方式

多参数事件实现了请求接口(等待事件返回)

单参数事件

顾名思义,事件只有一个参数,多参数则包含在一个对象内
好处:写事件协议时比较简单
坏处:派发事件必须存在参数

import * as cc from "cc";

class event<T> extends cc.EventTarget {
	key: { [key in keyof T]: key } = new Proxy(Object.create(null), {
		get: (target, key) => key,
	});

	// @ts-ignore
	on<T2 extends keyof T, T3 extends (event: T[T2]) => void>(
		type_: T2 | T2[],
		callback_: T3,
		this_?: any,
		once_b_?: boolean
	): typeof callback_ | null {
		if (Array.isArray(type_)) {
			type_.forEach((v) => {
				super.on(v as any, callback_, this_, once_b_);
			});
			return null;
		} else {
			return super.on(type_ as any, callback_, this_, once_b_);
		}
	}

	// @ts-ignore
	once<T2 extends keyof T, T3 extends (event: T[T2]) => void>(type_: T2 | T2[], callback_: T3, this_?: any): typeof callback_ | null {
		if (Array.isArray(type_)) {
			type_.forEach((v) => {
				super.once(v as any, callback_, this_);
			});
			return null;
		} else {
			return super.once(type_ as any, callback_, this_);
		}
	}

	// @ts-ignore
	off<T2 extends keyof T, T3 extends (event: T[T2]) => void>(type_: T2 | T2[], callback_?: T3, this_?: any): void {
		if (Array.isArray(type_)) {
			type_.forEach((v) => {
				super.off(v as any, callback_, this_);
			});
		} else {
			super.off(type_ as any, callback_, this_);
		}
	}

	// @ts-ignore
	emit<T2 extends keyof T, T3 extends T[T2]>(type_: T2 | T2[], args_: T3): void {
		if (Array.isArray(type_)) {
			type_.forEach((v) => {
				super.emit(v as any, args_);
			});
		} else {
			super.emit(type_ as any, args_);
		}
	}

	// @ts-ignore
	hasEventListener<T2 extends keyof T, T3 extends (event: T[T2]) => void>(type_: T2, callback_?: T3, target_?: any): boolean {
		return super.hasEventListener(type_ as any, callback_, target_);
	}

	clear(): void {
		return super["clear"]();
	}
}

export default event;

使用方式
image

多参数事件

可以存在多个参数,但是非对象类型的参数没有注释提示,最好把多个参数包含在对象内
好处:和原本的使用方式一致
坏处:写事件协议需要把参数包含在函数类型内

import * as cc from "cc";

class event<T> extends cc.EventTarget {
	key: { [key in keyof T]: key } = new Proxy(Object.create(null), {
		get: (target, key) => key,
	});

	// @ts-ignore
	on<T2 extends keyof T, T3 extends (...event_: Parameters<T[T2]>) => void>(
		type_: T2 | T2[],
		callback_: T3,
		this_?: any,
		once_b_?: boolean
	): typeof callback_ | null {
		if (Array.isArray(type_)) {
			type_.forEach((v) => {
				super.on(v as any, callback_ as any, this_, once_b_);
			});
			return null;
		} else {
			return super.on(type_ as any, callback_ as any, this_, once_b_);
		}
	}

	// @ts-ignore
	once<T2 extends keyof T, T3 extends (...event_: Parameters<T[T2]>) => void>(
		type_: T2 | T2[],
		callback_: T3,
		this_?: any
	): typeof callback_ | null {
		if (Array.isArray(type_)) {
			type_.forEach((v) => {
				super.once(v as any, callback_ as any, this_);
			});
			return null;
		} else {
			return super.once(type_ as any, callback_ as any, this_);
		}
	}

	// @ts-ignore
	off<T2 extends keyof T, T3 extends (...event_: Parameters<T[T2]>) => void>(type_: T2 | T2[], callback_?: T3, this_?: any): void {
		if (Array.isArray(type_)) {
			type_.forEach((v) => {
				super.off(v as any, callback_ as any, this_);
			});
		} else {
			super.off(type_ as any, callback_ as any, this_);
		}
	}

	// @ts-ignore
	emit<T2 extends keyof T, T3 extends Parameters<T[T2]>>(type_: T2 | T2[], ...args_: T3): void {
		if (Array.isArray(type_)) {
			type_.forEach((v) => {
				super.emit(v as any, ...args_);
			});
		} else {
			super.emit(type_ as any, ...args_);
		}
	}

	// @ts-ignore
	hasEventListener<T2 extends keyof T, T3 extends (...event_: Parameters<T[T2]>) => void>(type_: T2, callback_?: T3, target_?: any): boolean {
		return super.hasEventListener(type_ as any, callback_ as any, target_);
	}

	clear(): void {
		return super["clear"]();
	}

	/** 请求(等待返回) */
	// @ts-ignore
	request<T2 extends keyof T, T3 extends Parameters<T[T2]>>(type_: T2 | T2[], ...args_: T3): Promise<any>[] {
		if (Array.isArray(type_)) {
			const result_as: Promise<any>[] = [];

			type_.forEach((v) => {
				result_as.push(...this._request_single(v, ...args_));
			});
			return result_as;
		} else {
			return this._request_single(type_, ...args_);
		}
	}

	/** 请求单个事件 */
	// @ts-ignore
	private _request_single<T2 extends keyof T, T3 extends Parameters<T[T2]>>(type_: T2, ...args_: T3): Promise<any>[] {
		/** 返回值 */
		const result_as: Promise<any>[] = [];
		/** 回调列表 */
		const callback_as: { callback: Function; target?: any }[] = this["_callbackTable"][type_]?.callbackInfos;

		if (!callback_as) {
			return result_as;
		}
		callback_as.forEach((v) => {
			const old_callback_f = v.callback;
			const target = v.target;

			v.callback = (...args: any[]) => {
				result_as.push(old_callback_f.call(target, ...args));
				v.callback = old_callback_f;
			};
		});
		this.emit(type_, ...args_);
		return result_as;
	}
}

export default event;

使用方式
image


以上两种都是类型安全的,第二种兼容性更好点

posted @ 2023-01-11 21:44  Muzzik  阅读(525)  评论(0编辑  收藏  举报