@cjhd/cj-event-emitter 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ # 全局事件触发器:零依赖、轻量级
2
+
3
+ ## 前置条件
4
+ [游戏引擎](https://www.cocos.com/en/creator-download)
5
+ [开发框架](https://github.com/a1076559139/cocos-creator-frame-3d)
6
+
7
+ ## 使用案例
8
+ ```TS
9
+ import {} from 'db://pkg/@cjhd/cc-event-emitter';
10
+
11
+ ...
12
+ ```
@@ -0,0 +1,129 @@
1
+ /***********************************************************************
2
+ * EventEmitter.ts —— 轻量级全局事件管理器(支持 ctx 精确解绑 & targetOff)
3
+ *
4
+ * 修复点:
5
+ * - on / once 阶段拦截非函数 listener,避免 emit 阶段出现 fn.apply undefined。
6
+ * - emit 阶段二次防御并清理脏监听,不改变合法监听的执行语义。
7
+ * - off(type) 支持清空该事件全部监听,兼容常见 EventEmitter 用法。
8
+ ***********************************************************************/
9
+
10
+ type EventType = string | number | symbol;
11
+ export type AnyListener = (...args: any[]) => unknown;
12
+
13
+ /** 内部包装:记录回调与上下文 */
14
+ interface ListenerItem {
15
+ fn: AnyListener;
16
+ ctx?: any;
17
+ once?: boolean;
18
+ }
19
+
20
+ function isValidListener(fn: unknown): fn is AnyListener {
21
+ return typeof fn === "function";
22
+ }
23
+
24
+ export class EventEmitter {
25
+ /** Map<事件类型, Set<ListenerItem>> */
26
+ private readonly _map = new Map<EventType, Set<ListenerItem>>();
27
+
28
+ /* ───────────── 监听 ───────────── */
29
+
30
+ on(type: EventType, fn: AnyListener, ctx?: any): void {
31
+ if (!isValidListener(fn)) {
32
+ console.warn("[EventEmitter] ignore invalid on listener:", type, fn);
33
+ return;
34
+ }
35
+
36
+ let set = this._map.get(type);
37
+ if (!set) {
38
+ set = new Set<ListenerItem>();
39
+ this._map.set(type, set);
40
+ }
41
+ set.add({ fn, ctx });
42
+ }
43
+
44
+ once(type: EventType, fn: AnyListener, ctx?: any): void {
45
+ if (!isValidListener(fn)) {
46
+ console.warn("[EventEmitter] ignore invalid once listener:", type, fn);
47
+ return;
48
+ }
49
+
50
+ let set = this._map.get(type);
51
+ if (!set) {
52
+ set = new Set<ListenerItem>();
53
+ this._map.set(type, set);
54
+ }
55
+ set.add({ fn, ctx, once: true });
56
+ }
57
+
58
+ /* ───────────── 取消监听 ───────────── */
59
+
60
+ /**
61
+ * 取消指定回调
62
+ * - off(type):取消该事件全部监听
63
+ * - off(type, fn):取消所有同名函数
64
+ * - off(type, fn, ctx):仅取消 (fn, ctx) 组合
65
+ */
66
+ off(type: EventType, fn?: AnyListener, ctx?: any): void {
67
+ const set = this._map.get(type);
68
+ if (!set) return;
69
+
70
+ if (fn === undefined || fn === null) {
71
+ this._map.delete(type);
72
+ return;
73
+ }
74
+
75
+ for (const item of Array.from(set)) {
76
+ if (!item || item.fn !== fn) continue;
77
+ if (ctx !== undefined && ctx !== item.ctx) continue;
78
+ set.delete(item);
79
+ }
80
+ if (set.size === 0) this._map.delete(type);
81
+ }
82
+
83
+ /**
84
+ * 根据 ctx 移除当前 EventEmitter 上的所有监听
85
+ * 等同于 Cocos EventTarget.targetOff
86
+ */
87
+ targetOff(ctx: any): void {
88
+ if (ctx === undefined || ctx === null) return;
89
+
90
+ for (const [type, set] of Array.from(this._map.entries())) {
91
+ for (const item of Array.from(set)) {
92
+ if (item && item.ctx === ctx) set.delete(item);
93
+ }
94
+ if (set.size === 0) this._map.delete(type);
95
+ }
96
+ }
97
+
98
+ /**
99
+ * 取消某事件全部监听;不传 type ⇒ 清空所有事件
100
+ */
101
+ offAll(type?: EventType): void {
102
+ if (type === undefined) this._map.clear();
103
+ else this._map.delete(type);
104
+ }
105
+
106
+ /* ───────────── 触发事件 ───────────── */
107
+
108
+ emit(type: EventType, ...args: any[]): void {
109
+ const set = this._map.get(type);
110
+ if (!set) return;
111
+
112
+ // 拷贝一份,防止回调里修改集合导致遍历异常
113
+ const listeners = Array.from(set);
114
+ for (const item of listeners) {
115
+ if (!item || !isValidListener(item.fn)) {
116
+ set.delete(item as any);
117
+ console.warn("[EventEmitter] remove invalid listener while emitting:", type, item);
118
+ continue;
119
+ }
120
+
121
+ try {
122
+ item.fn.apply(item.ctx, args);
123
+ } finally {
124
+ if (item.once) set.delete(item);
125
+ }
126
+ }
127
+ if (set.size === 0) this._map.delete(type);
128
+ }
129
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "ver": "4.0.24",
3
+ "importer": "typescript",
4
+ "imported": true,
5
+ "uuid": "de97f5f0-a281-4cfb-9138-c4266b84f188",
6
+ "files": [],
7
+ "subMetas": {},
8
+ "userData": {}
9
+ }
package/assets.meta ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "ver": "1.2.0",
3
+ "importer": "directory",
4
+ "imported": true,
5
+ "uuid": "7927f3d6-977d-4388-afc3-e365069e9754",
6
+ "files": [],
7
+ "subMetas": {},
8
+ "userData": {}
9
+ }
package/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ // 负责导出assets下的模块,如: export { default } from './assets/xxx.ts'
2
+ export { EventEmitter } from './assets/EventEmitter';
package/index.ts.meta ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "ver": "4.0.24",
3
+ "importer": "typescript",
4
+ "imported": true,
5
+ "uuid": "875ad7b0-b2f1-4157-bb41-d6465bfa1a07",
6
+ "files": [],
7
+ "subMetas": {},
8
+ "userData": {}
9
+ }
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@cjhd/cj-event-emitter",
3
+ "version": "1.0.0",
4
+ "engine": ">=3.8.0",
5
+ "description": "全局事件触发器:零依赖、轻量级",
6
+ "main": "index.ts",
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://gitee.com/cocos2d-zp/cococs-creator-frame-3d"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public",
16
+ "registry": "https://registry.npmjs.org"
17
+ },
18
+ "author": "超M <402879660@qq.com>",
19
+ "license": "MIT"
20
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "ver": "2.0.1",
3
+ "importer": "json",
4
+ "imported": true,
5
+ "uuid": "ab519119-3f49-4697-b90d-28f076d502b5",
6
+ "files": [
7
+ ".json"
8
+ ],
9
+ "subMetas": {},
10
+ "userData": {}
11
+ }