a2bei4-utils 1.0.0 → 1.0.1

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.
Files changed (57) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +2 -2
  3. package/dist/a2bei4.utils.cjs.js +1051 -250
  4. package/dist/a2bei4.utils.cjs.js.map +1 -1
  5. package/dist/a2bei4.utils.cjs.min.js +1 -1
  6. package/dist/a2bei4.utils.cjs.min.js.map +1 -1
  7. package/dist/a2bei4.utils.esm.js +1047 -251
  8. package/dist/a2bei4.utils.esm.js.map +1 -1
  9. package/dist/a2bei4.utils.esm.min.js +1 -1
  10. package/dist/a2bei4.utils.esm.min.js.map +1 -1
  11. package/dist/a2bei4.utils.umd.js +1051 -250
  12. package/dist/a2bei4.utils.umd.js.map +1 -1
  13. package/dist/a2bei4.utils.umd.min.js +1 -1
  14. package/dist/a2bei4.utils.umd.min.js.map +1 -1
  15. package/dist/arr.cjs +27 -27
  16. package/dist/arr.cjs.map +1 -1
  17. package/dist/arr.js +27 -27
  18. package/dist/arr.js.map +1 -1
  19. package/dist/audio.cjs +281 -0
  20. package/dist/audio.cjs.map +1 -0
  21. package/dist/audio.js +278 -0
  22. package/dist/audio.js.map +1 -0
  23. package/dist/common.cjs +6 -6
  24. package/dist/common.cjs.map +1 -1
  25. package/dist/common.js +6 -6
  26. package/dist/common.js.map +1 -1
  27. package/dist/download.cjs +43 -0
  28. package/dist/download.cjs.map +1 -1
  29. package/dist/download.js +43 -1
  30. package/dist/download.js.map +1 -1
  31. package/dist/evt.cjs +148 -148
  32. package/dist/evt.cjs.map +1 -1
  33. package/dist/evt.js +148 -148
  34. package/dist/evt.js.map +1 -1
  35. package/dist/id.cjs +68 -68
  36. package/dist/id.cjs.map +1 -1
  37. package/dist/id.js +68 -68
  38. package/dist/id.js.map +1 -1
  39. package/dist/timer.cjs +0 -1
  40. package/dist/timer.cjs.map +1 -1
  41. package/dist/timer.js +0 -1
  42. package/dist/timer.js.map +1 -1
  43. package/dist/tree.cjs +75 -0
  44. package/dist/tree.cjs.map +1 -1
  45. package/dist/tree.js +75 -1
  46. package/dist/tree.js.map +1 -1
  47. package/dist/webSocket.cjs +409 -0
  48. package/dist/webSocket.cjs.map +1 -0
  49. package/dist/webSocket.js +407 -0
  50. package/dist/webSocket.js.map +1 -0
  51. package/package.json +11 -1
  52. package/readme.txt +8 -5
  53. package/types/audio.d.ts +57 -0
  54. package/types/download.d.ts +12 -1
  55. package/types/index.d.ts +207 -1
  56. package/types/tree.d.ts +17 -1
  57. package/types/webSocket.d.ts +124 -0
package/dist/evt.cjs CHANGED
@@ -1,153 +1,153 @@
1
1
  'use strict';
2
2
 
3
- /**
4
- * 简单、高性能的通用事件总线。
5
- * - 支持命名空间事件
6
- * - 支持一次性监听器
7
- * - 返回唯一 flag,用于精确卸载
8
- * - emit 时可选自定义 this 指向
9
- */
10
- class MyEvent {
11
- constructor() {
12
- this.evtPool = new Map();
13
- }
14
-
15
- /**
16
- * 注册事件监听器。
17
- * @param {string} name - 事件名
18
- * @param {Function} fn - 回调函数
19
- * @returns {string} flag - 唯一标识,用于 off
20
- */
21
- on(name, fn) {
22
- let flag = Date.now() + "_" + parseInt(Math.random() * 1e8);
23
- const evtItem = {
24
- flag,
25
- fn
26
- };
27
- if (this.evtPool.has(name)) {
28
- this.evtPool.get(name).push(evtItem);
29
- } else {
30
- this.evtPool.set(name, [evtItem]);
31
- }
32
- return flag;
33
- }
34
-
35
- /**
36
- * 注册一次性监听器,触发后自动移除。
37
- * @param {string} name - 事件名
38
- * @param {Function} fn - 回调函数
39
- * @returns {string} flag - 唯一标识
40
- */
41
- once(name, fn) {
42
- const _this = this;
43
- let wrapper;
44
- wrapper = function (data) {
45
- _this.off(name, wrapper);
46
- fn.call(this, data);
47
- };
48
- return this.on(name, wrapper);
49
- }
50
-
51
- /**
52
- * 移除指定事件监听器。
53
- * @param {string} name - 事件名
54
- * @param {Function|string} fnOrFlag - 回调函数或 flag
55
- */
56
- off(name, fnOrFlag) {
57
- if (!this.evtPool.has(name)) return;
58
- const evtItems = this.evtPool.get(name);
59
- const filtered = evtItems.filter((item) => item.fn !== fnOrFlag && item.flag !== fnOrFlag);
60
- if (filtered.length === 0) {
61
- this.evtPool.delete(name);
62
- } else {
63
- this.evtPool.set(name, filtered);
64
- }
65
- }
66
-
67
- /**
68
- * 触发事件(同步执行)。
69
- * @param {string} name - 事件名
70
- * @param {*} [data] - 任意载荷
71
- * @param {*} [fnThis] - 回调内部 this 指向,默认 undefined
72
- */
73
- emit(name, data, fnThis) {
74
- if (!this.evtPool.has(name)) return;
75
- const evtItems = this.evtPool.get(name);
76
- evtItems.forEach((item) => {
77
- try {
78
- item.fn.call(fnThis, data);
79
- } catch (err) {
80
- console.error(`Error in event listener for "${name}":`, err);
81
- }
82
- });
83
- }
84
- }
85
-
86
- /**
87
- * 跨页通信插件:通过 localStorage + storage 事件将当前实例的 emit 广播到其他同源页面。
88
- * 支持节流、命名空间隔离。
89
- */
90
- const MyEvent_CrossPagePlugin = (() => {
91
- const INSTALLED = new WeakSet(); // 防止重复安装
92
-
93
- return {
94
- /**
95
- * 为指定 MyEvent 实例安装跨页插件。
96
- * @param {MyEvent} bus - 事件总线实例
97
- * @param {Options} [opts] - 配置项
98
- */
99
- install(bus, opts = {}) {
100
- if (INSTALLED.has(bus)) return;
101
- INSTALLED.add(bus);
102
-
103
- const ns = `___my-event-cross-page-${opts.namespace || "default"}___`;
104
- const delay = opts.throttle || 16;
105
- let last = 0;
106
-
107
- // 1、重写 emit
108
- const rawEmit = bus.emit;
109
- bus.emit = function (name, data, fnThis) {
110
- rawEmit.call(bus, name, data, fnThis); // 本地先执行
111
- const now = Date.now();
112
- if (now - last < delay) return;
113
- last = now;
114
- const key = ns + name;
115
- try {
116
- localStorage.setItem(key, JSON.stringify({ name, data, ts: now }));
117
- localStorage.removeItem(key); // 触发 storage 事件
118
- } catch (e) {}
119
- };
120
-
121
- // 2、监听其他页广播
122
- function onStorageHandler(e) {
123
- if (!e.key || !e.key.startsWith(ns)) return;
124
- let payload;
125
- try {
126
- payload = JSON.parse(e.newValue || "{}");
127
- } catch {
128
- return;
129
- }
130
- if (!payload.ts || payload.ts <= last) return;
131
- rawEmit.call(bus, e.key.slice(ns.length), payload.data); // 仅本地
132
- }
133
- addEventListener("storage", onStorageHandler);
134
-
135
- // 3、保存卸载器
136
- bus._uninstallCrossPage = () => {
137
- removeEventListener("storage", onStorageHandler);
138
- bus.emit = rawEmit;
139
- INSTALLED.delete(bus);
140
- };
141
- },
142
-
143
- /**
144
- * 卸载插件,恢复原始 emit 并停止监听。
145
- * @param {MyEvent} bus - 事件总线实例
146
- */
147
- uninstall(bus) {
148
- if (typeof bus._uninstallCrossPage === "function") bus._uninstallCrossPage();
149
- }
150
- };
3
+ /**
4
+ * 简单、高性能的通用事件总线。
5
+ * - 支持命名空间事件
6
+ * - 支持一次性监听器
7
+ * - 返回唯一 flag,用于精确卸载
8
+ * - emit 时可选自定义 this 指向
9
+ */
10
+ class MyEvent {
11
+ constructor() {
12
+ this.evtPool = new Map();
13
+ }
14
+
15
+ /**
16
+ * 注册事件监听器。
17
+ * @param {string} name - 事件名
18
+ * @param {Function} fn - 回调函数
19
+ * @returns {string} flag - 唯一标识,用于 off
20
+ */
21
+ on(name, fn) {
22
+ let flag = Date.now() + "_" + parseInt(Math.random() * 1e8);
23
+ const evtItem = {
24
+ flag,
25
+ fn
26
+ };
27
+ if (this.evtPool.has(name)) {
28
+ this.evtPool.get(name).push(evtItem);
29
+ } else {
30
+ this.evtPool.set(name, [evtItem]);
31
+ }
32
+ return flag;
33
+ }
34
+
35
+ /**
36
+ * 注册一次性监听器,触发后自动移除。
37
+ * @param {string} name - 事件名
38
+ * @param {Function} fn - 回调函数
39
+ * @returns {string} flag - 唯一标识
40
+ */
41
+ once(name, fn) {
42
+ const _this = this;
43
+ let wrapper;
44
+ wrapper = function (data) {
45
+ _this.off(name, wrapper);
46
+ fn.call(this, data);
47
+ };
48
+ return this.on(name, wrapper);
49
+ }
50
+
51
+ /**
52
+ * 移除指定事件监听器。
53
+ * @param {string} name - 事件名
54
+ * @param {Function|string} fnOrFlag - 回调函数或 flag
55
+ */
56
+ off(name, fnOrFlag) {
57
+ if (!this.evtPool.has(name)) return;
58
+ const evtItems = this.evtPool.get(name);
59
+ const filtered = evtItems.filter((item) => item.fn !== fnOrFlag && item.flag !== fnOrFlag);
60
+ if (filtered.length === 0) {
61
+ this.evtPool.delete(name);
62
+ } else {
63
+ this.evtPool.set(name, filtered);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * 触发事件(同步执行)。
69
+ * @param {string} name - 事件名
70
+ * @param {*} [data] - 任意载荷
71
+ * @param {*} [fnThis] - 回调内部 this 指向,默认 undefined
72
+ */
73
+ emit(name, data, fnThis) {
74
+ if (!this.evtPool.has(name)) return;
75
+ const evtItems = this.evtPool.get(name);
76
+ evtItems.forEach((item) => {
77
+ try {
78
+ item.fn.call(fnThis, data);
79
+ } catch (err) {
80
+ console.error(`Error in event listener for "${name}":`, err);
81
+ }
82
+ });
83
+ }
84
+ }
85
+
86
+ /**
87
+ * 跨页通信插件:通过 localStorage + storage 事件将当前实例的 emit 广播到其他同源页面。
88
+ * 支持节流、命名空间隔离。
89
+ */
90
+ const MyEvent_CrossPagePlugin = (() => {
91
+ const INSTALLED = new WeakSet(); // 防止重复安装
92
+
93
+ return {
94
+ /**
95
+ * 为指定 MyEvent 实例安装跨页插件。
96
+ * @param {MyEvent} bus - 事件总线实例
97
+ * @param {Options} [opts] - 配置项
98
+ */
99
+ install(bus, opts = {}) {
100
+ if (INSTALLED.has(bus)) return;
101
+ INSTALLED.add(bus);
102
+
103
+ const ns = `___my-event-cross-page-${opts.namespace || "default"}___`;
104
+ const delay = opts.throttle || 16;
105
+ let last = 0;
106
+
107
+ // 1、重写 emit
108
+ const rawEmit = bus.emit;
109
+ bus.emit = function (name, data, fnThis) {
110
+ rawEmit.call(bus, name, data, fnThis); // 本地先执行
111
+ const now = Date.now();
112
+ if (now - last < delay) return;
113
+ last = now;
114
+ const key = ns + name;
115
+ try {
116
+ localStorage.setItem(key, JSON.stringify({ name, data, ts: now }));
117
+ localStorage.removeItem(key); // 触发 storage 事件
118
+ } catch (e) {}
119
+ };
120
+
121
+ // 2、监听其他页广播
122
+ function onStorageHandler(e) {
123
+ if (!e.key || !e.key.startsWith(ns)) return;
124
+ let payload;
125
+ try {
126
+ payload = JSON.parse(e.newValue || "{}");
127
+ } catch {
128
+ return;
129
+ }
130
+ if (!payload.ts || payload.ts <= last) return;
131
+ rawEmit.call(bus, e.key.slice(ns.length), payload.data); // 仅本地
132
+ }
133
+ addEventListener("storage", onStorageHandler);
134
+
135
+ // 3、保存卸载器
136
+ bus._uninstallCrossPage = () => {
137
+ removeEventListener("storage", onStorageHandler);
138
+ bus.emit = rawEmit;
139
+ INSTALLED.delete(bus);
140
+ };
141
+ },
142
+
143
+ /**
144
+ * 卸载插件,恢复原始 emit 并停止监听。
145
+ * @param {MyEvent} bus - 事件总线实例
146
+ */
147
+ uninstall(bus) {
148
+ if (typeof bus._uninstallCrossPage === "function") bus._uninstallCrossPage();
149
+ }
150
+ };
151
151
  })();
152
152
 
153
153
  exports.MyEvent = MyEvent;
package/dist/evt.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"evt.cjs","sources":["../src/source/evt.js"],"sourcesContent":["/**\r\n * 简单、高性能的通用事件总线。\r\n * - 支持命名空间事件\r\n * - 支持一次性监听器\r\n * - 返回唯一 flag,用于精确卸载\r\n * - emit 时可选自定义 this 指向\r\n */\r\nexport class MyEvent {\r\n constructor() {\r\n this.evtPool = new Map();\r\n }\r\n\r\n /**\r\n * 注册事件监听器。\r\n * @param {string} name - 事件名\r\n * @param {Function} fn - 回调函数\r\n * @returns {string} flag - 唯一标识,用于 off\r\n */\r\n on(name, fn) {\r\n let flag = Date.now() + \"_\" + parseInt(Math.random() * 1e8);\r\n const evtItem = {\r\n flag,\r\n fn\r\n };\r\n if (this.evtPool.has(name)) {\r\n this.evtPool.get(name).push(evtItem);\r\n } else {\r\n this.evtPool.set(name, [evtItem]);\r\n }\r\n return flag;\r\n }\r\n\r\n /**\r\n * 注册一次性监听器,触发后自动移除。\r\n * @param {string} name - 事件名\r\n * @param {Function} fn - 回调函数\r\n * @returns {string} flag - 唯一标识\r\n */\r\n once(name, fn) {\r\n const _this = this;\r\n let wrapper;\r\n wrapper = function (data) {\r\n _this.off(name, wrapper);\r\n fn.call(this, data);\r\n };\r\n return this.on(name, wrapper);\r\n }\r\n\r\n /**\r\n * 移除指定事件监听器。\r\n * @param {string} name - 事件名\r\n * @param {Function|string} fnOrFlag - 回调函数或 flag\r\n */\r\n off(name, fnOrFlag) {\r\n if (!this.evtPool.has(name)) return;\r\n const evtItems = this.evtPool.get(name);\r\n const filtered = evtItems.filter((item) => item.fn !== fnOrFlag && item.flag !== fnOrFlag);\r\n if (filtered.length === 0) {\r\n this.evtPool.delete(name);\r\n } else {\r\n this.evtPool.set(name, filtered);\r\n }\r\n }\r\n\r\n /**\r\n * 触发事件(同步执行)。\r\n * @param {string} name - 事件名\r\n * @param {*} [data] - 任意载荷\r\n * @param {*} [fnThis] - 回调内部 this 指向,默认 undefined\r\n */\r\n emit(name, data, fnThis) {\r\n if (!this.evtPool.has(name)) return;\r\n const evtItems = this.evtPool.get(name);\r\n evtItems.forEach((item) => {\r\n try {\r\n item.fn.call(fnThis, data);\r\n } catch (err) {\r\n console.error(`Error in event listener for \"${name}\":`, err);\r\n }\r\n });\r\n }\r\n}\r\n\r\n/**\r\n * 跨页通信插件:通过 localStorage + storage 事件将当前实例的 emit 广播到其他同源页面。\r\n * 支持节流、命名空间隔离。\r\n */\r\nexport const MyEvent_CrossPagePlugin = (() => {\r\n const INSTALLED = new WeakSet(); // 防止重复安装\r\n\r\n return {\r\n /**\r\n * 为指定 MyEvent 实例安装跨页插件。\r\n * @param {MyEvent} bus - 事件总线实例\r\n * @param {Options} [opts] - 配置项\r\n */\r\n install(bus, opts = {}) {\r\n if (INSTALLED.has(bus)) return;\r\n INSTALLED.add(bus);\r\n\r\n const ns = `___my-event-cross-page-${opts.namespace || \"default\"}___`;\r\n const delay = opts.throttle || 16;\r\n let last = 0;\r\n\r\n // 1、重写 emit\r\n const rawEmit = bus.emit;\r\n bus.emit = function (name, data, fnThis) {\r\n rawEmit.call(bus, name, data, fnThis); // 本地先执行\r\n const now = Date.now();\r\n if (now - last < delay) return;\r\n last = now;\r\n const key = ns + name;\r\n try {\r\n localStorage.setItem(key, JSON.stringify({ name, data, ts: now }));\r\n localStorage.removeItem(key); // 触发 storage 事件\r\n } catch (e) {}\r\n };\r\n\r\n // 2、监听其他页广播\r\n function onStorageHandler(e) {\r\n if (!e.key || !e.key.startsWith(ns)) return;\r\n let payload;\r\n try {\r\n payload = JSON.parse(e.newValue || \"{}\");\r\n } catch {\r\n return;\r\n }\r\n if (!payload.ts || payload.ts <= last) return;\r\n rawEmit.call(bus, e.key.slice(ns.length), payload.data); // 仅本地\r\n }\r\n addEventListener(\"storage\", onStorageHandler);\r\n\r\n // 3、保存卸载器\r\n bus._uninstallCrossPage = () => {\r\n removeEventListener(\"storage\", onStorageHandler);\r\n bus.emit = rawEmit;\r\n INSTALLED.delete(bus);\r\n };\r\n },\r\n\r\n /**\r\n * 卸载插件,恢复原始 emit 并停止监听。\r\n * @param {MyEvent} bus - 事件总线实例\r\n */\r\n uninstall(bus) {\r\n if (typeof bus._uninstallCrossPage === \"function\") bus._uninstallCrossPage();\r\n }\r\n };\r\n})();\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"evt.cjs","sources":["../src/source/evt.js"],"sourcesContent":["/**\n * 简单、高性能的通用事件总线。\n * - 支持命名空间事件\n * - 支持一次性监听器\n * - 返回唯一 flag,用于精确卸载\n * - emit 时可选自定义 this 指向\n */\nexport class MyEvent {\n constructor() {\n this.evtPool = new Map();\n }\n\n /**\n * 注册事件监听器。\n * @param {string} name - 事件名\n * @param {Function} fn - 回调函数\n * @returns {string} flag - 唯一标识,用于 off\n */\n on(name, fn) {\n let flag = Date.now() + \"_\" + parseInt(Math.random() * 1e8);\n const evtItem = {\n flag,\n fn\n };\n if (this.evtPool.has(name)) {\n this.evtPool.get(name).push(evtItem);\n } else {\n this.evtPool.set(name, [evtItem]);\n }\n return flag;\n }\n\n /**\n * 注册一次性监听器,触发后自动移除。\n * @param {string} name - 事件名\n * @param {Function} fn - 回调函数\n * @returns {string} flag - 唯一标识\n */\n once(name, fn) {\n const _this = this;\n let wrapper;\n wrapper = function (data) {\n _this.off(name, wrapper);\n fn.call(this, data);\n };\n return this.on(name, wrapper);\n }\n\n /**\n * 移除指定事件监听器。\n * @param {string} name - 事件名\n * @param {Function|string} fnOrFlag - 回调函数或 flag\n */\n off(name, fnOrFlag) {\n if (!this.evtPool.has(name)) return;\n const evtItems = this.evtPool.get(name);\n const filtered = evtItems.filter((item) => item.fn !== fnOrFlag && item.flag !== fnOrFlag);\n if (filtered.length === 0) {\n this.evtPool.delete(name);\n } else {\n this.evtPool.set(name, filtered);\n }\n }\n\n /**\n * 触发事件(同步执行)。\n * @param {string} name - 事件名\n * @param {*} [data] - 任意载荷\n * @param {*} [fnThis] - 回调内部 this 指向,默认 undefined\n */\n emit(name, data, fnThis) {\n if (!this.evtPool.has(name)) return;\n const evtItems = this.evtPool.get(name);\n evtItems.forEach((item) => {\n try {\n item.fn.call(fnThis, data);\n } catch (err) {\n console.error(`Error in event listener for \"${name}\":`, err);\n }\n });\n }\n}\n\n/**\n * 跨页通信插件:通过 localStorage + storage 事件将当前实例的 emit 广播到其他同源页面。\n * 支持节流、命名空间隔离。\n */\nexport const MyEvent_CrossPagePlugin = (() => {\n const INSTALLED = new WeakSet(); // 防止重复安装\n\n return {\n /**\n * 为指定 MyEvent 实例安装跨页插件。\n * @param {MyEvent} bus - 事件总线实例\n * @param {Options} [opts] - 配置项\n */\n install(bus, opts = {}) {\n if (INSTALLED.has(bus)) return;\n INSTALLED.add(bus);\n\n const ns = `___my-event-cross-page-${opts.namespace || \"default\"}___`;\n const delay = opts.throttle || 16;\n let last = 0;\n\n // 1、重写 emit\n const rawEmit = bus.emit;\n bus.emit = function (name, data, fnThis) {\n rawEmit.call(bus, name, data, fnThis); // 本地先执行\n const now = Date.now();\n if (now - last < delay) return;\n last = now;\n const key = ns + name;\n try {\n localStorage.setItem(key, JSON.stringify({ name, data, ts: now }));\n localStorage.removeItem(key); // 触发 storage 事件\n } catch (e) {}\n };\n\n // 2、监听其他页广播\n function onStorageHandler(e) {\n if (!e.key || !e.key.startsWith(ns)) return;\n let payload;\n try {\n payload = JSON.parse(e.newValue || \"{}\");\n } catch {\n return;\n }\n if (!payload.ts || payload.ts <= last) return;\n rawEmit.call(bus, e.key.slice(ns.length), payload.data); // 仅本地\n }\n addEventListener(\"storage\", onStorageHandler);\n\n // 3、保存卸载器\n bus._uninstallCrossPage = () => {\n removeEventListener(\"storage\", onStorageHandler);\n bus.emit = rawEmit;\n INSTALLED.delete(bus);\n };\n },\n\n /**\n * 卸载插件,恢复原始 emit 并停止监听。\n * @param {MyEvent} bus - 事件总线实例\n */\n uninstall(bus) {\n if (typeof bus._uninstallCrossPage === \"function\") bus._uninstallCrossPage();\n }\n };\n})();\n"],"names":[],"mappings":"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"}
package/dist/evt.js CHANGED
@@ -1,151 +1,151 @@
1
- /**
2
- * 简单、高性能的通用事件总线。
3
- * - 支持命名空间事件
4
- * - 支持一次性监听器
5
- * - 返回唯一 flag,用于精确卸载
6
- * - emit 时可选自定义 this 指向
7
- */
8
- class MyEvent {
9
- constructor() {
10
- this.evtPool = new Map();
11
- }
12
-
13
- /**
14
- * 注册事件监听器。
15
- * @param {string} name - 事件名
16
- * @param {Function} fn - 回调函数
17
- * @returns {string} flag - 唯一标识,用于 off
18
- */
19
- on(name, fn) {
20
- let flag = Date.now() + "_" + parseInt(Math.random() * 1e8);
21
- const evtItem = {
22
- flag,
23
- fn
24
- };
25
- if (this.evtPool.has(name)) {
26
- this.evtPool.get(name).push(evtItem);
27
- } else {
28
- this.evtPool.set(name, [evtItem]);
29
- }
30
- return flag;
31
- }
32
-
33
- /**
34
- * 注册一次性监听器,触发后自动移除。
35
- * @param {string} name - 事件名
36
- * @param {Function} fn - 回调函数
37
- * @returns {string} flag - 唯一标识
38
- */
39
- once(name, fn) {
40
- const _this = this;
41
- let wrapper;
42
- wrapper = function (data) {
43
- _this.off(name, wrapper);
44
- fn.call(this, data);
45
- };
46
- return this.on(name, wrapper);
47
- }
48
-
49
- /**
50
- * 移除指定事件监听器。
51
- * @param {string} name - 事件名
52
- * @param {Function|string} fnOrFlag - 回调函数或 flag
53
- */
54
- off(name, fnOrFlag) {
55
- if (!this.evtPool.has(name)) return;
56
- const evtItems = this.evtPool.get(name);
57
- const filtered = evtItems.filter((item) => item.fn !== fnOrFlag && item.flag !== fnOrFlag);
58
- if (filtered.length === 0) {
59
- this.evtPool.delete(name);
60
- } else {
61
- this.evtPool.set(name, filtered);
62
- }
63
- }
64
-
65
- /**
66
- * 触发事件(同步执行)。
67
- * @param {string} name - 事件名
68
- * @param {*} [data] - 任意载荷
69
- * @param {*} [fnThis] - 回调内部 this 指向,默认 undefined
70
- */
71
- emit(name, data, fnThis) {
72
- if (!this.evtPool.has(name)) return;
73
- const evtItems = this.evtPool.get(name);
74
- evtItems.forEach((item) => {
75
- try {
76
- item.fn.call(fnThis, data);
77
- } catch (err) {
78
- console.error(`Error in event listener for "${name}":`, err);
79
- }
80
- });
81
- }
82
- }
83
-
84
- /**
85
- * 跨页通信插件:通过 localStorage + storage 事件将当前实例的 emit 广播到其他同源页面。
86
- * 支持节流、命名空间隔离。
87
- */
88
- const MyEvent_CrossPagePlugin = (() => {
89
- const INSTALLED = new WeakSet(); // 防止重复安装
90
-
91
- return {
92
- /**
93
- * 为指定 MyEvent 实例安装跨页插件。
94
- * @param {MyEvent} bus - 事件总线实例
95
- * @param {Options} [opts] - 配置项
96
- */
97
- install(bus, opts = {}) {
98
- if (INSTALLED.has(bus)) return;
99
- INSTALLED.add(bus);
100
-
101
- const ns = `___my-event-cross-page-${opts.namespace || "default"}___`;
102
- const delay = opts.throttle || 16;
103
- let last = 0;
104
-
105
- // 1、重写 emit
106
- const rawEmit = bus.emit;
107
- bus.emit = function (name, data, fnThis) {
108
- rawEmit.call(bus, name, data, fnThis); // 本地先执行
109
- const now = Date.now();
110
- if (now - last < delay) return;
111
- last = now;
112
- const key = ns + name;
113
- try {
114
- localStorage.setItem(key, JSON.stringify({ name, data, ts: now }));
115
- localStorage.removeItem(key); // 触发 storage 事件
116
- } catch (e) {}
117
- };
118
-
119
- // 2、监听其他页广播
120
- function onStorageHandler(e) {
121
- if (!e.key || !e.key.startsWith(ns)) return;
122
- let payload;
123
- try {
124
- payload = JSON.parse(e.newValue || "{}");
125
- } catch {
126
- return;
127
- }
128
- if (!payload.ts || payload.ts <= last) return;
129
- rawEmit.call(bus, e.key.slice(ns.length), payload.data); // 仅本地
130
- }
131
- addEventListener("storage", onStorageHandler);
132
-
133
- // 3、保存卸载器
134
- bus._uninstallCrossPage = () => {
135
- removeEventListener("storage", onStorageHandler);
136
- bus.emit = rawEmit;
137
- INSTALLED.delete(bus);
138
- };
139
- },
140
-
141
- /**
142
- * 卸载插件,恢复原始 emit 并停止监听。
143
- * @param {MyEvent} bus - 事件总线实例
144
- */
145
- uninstall(bus) {
146
- if (typeof bus._uninstallCrossPage === "function") bus._uninstallCrossPage();
147
- }
148
- };
1
+ /**
2
+ * 简单、高性能的通用事件总线。
3
+ * - 支持命名空间事件
4
+ * - 支持一次性监听器
5
+ * - 返回唯一 flag,用于精确卸载
6
+ * - emit 时可选自定义 this 指向
7
+ */
8
+ class MyEvent {
9
+ constructor() {
10
+ this.evtPool = new Map();
11
+ }
12
+
13
+ /**
14
+ * 注册事件监听器。
15
+ * @param {string} name - 事件名
16
+ * @param {Function} fn - 回调函数
17
+ * @returns {string} flag - 唯一标识,用于 off
18
+ */
19
+ on(name, fn) {
20
+ let flag = Date.now() + "_" + parseInt(Math.random() * 1e8);
21
+ const evtItem = {
22
+ flag,
23
+ fn
24
+ };
25
+ if (this.evtPool.has(name)) {
26
+ this.evtPool.get(name).push(evtItem);
27
+ } else {
28
+ this.evtPool.set(name, [evtItem]);
29
+ }
30
+ return flag;
31
+ }
32
+
33
+ /**
34
+ * 注册一次性监听器,触发后自动移除。
35
+ * @param {string} name - 事件名
36
+ * @param {Function} fn - 回调函数
37
+ * @returns {string} flag - 唯一标识
38
+ */
39
+ once(name, fn) {
40
+ const _this = this;
41
+ let wrapper;
42
+ wrapper = function (data) {
43
+ _this.off(name, wrapper);
44
+ fn.call(this, data);
45
+ };
46
+ return this.on(name, wrapper);
47
+ }
48
+
49
+ /**
50
+ * 移除指定事件监听器。
51
+ * @param {string} name - 事件名
52
+ * @param {Function|string} fnOrFlag - 回调函数或 flag
53
+ */
54
+ off(name, fnOrFlag) {
55
+ if (!this.evtPool.has(name)) return;
56
+ const evtItems = this.evtPool.get(name);
57
+ const filtered = evtItems.filter((item) => item.fn !== fnOrFlag && item.flag !== fnOrFlag);
58
+ if (filtered.length === 0) {
59
+ this.evtPool.delete(name);
60
+ } else {
61
+ this.evtPool.set(name, filtered);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * 触发事件(同步执行)。
67
+ * @param {string} name - 事件名
68
+ * @param {*} [data] - 任意载荷
69
+ * @param {*} [fnThis] - 回调内部 this 指向,默认 undefined
70
+ */
71
+ emit(name, data, fnThis) {
72
+ if (!this.evtPool.has(name)) return;
73
+ const evtItems = this.evtPool.get(name);
74
+ evtItems.forEach((item) => {
75
+ try {
76
+ item.fn.call(fnThis, data);
77
+ } catch (err) {
78
+ console.error(`Error in event listener for "${name}":`, err);
79
+ }
80
+ });
81
+ }
82
+ }
83
+
84
+ /**
85
+ * 跨页通信插件:通过 localStorage + storage 事件将当前实例的 emit 广播到其他同源页面。
86
+ * 支持节流、命名空间隔离。
87
+ */
88
+ const MyEvent_CrossPagePlugin = (() => {
89
+ const INSTALLED = new WeakSet(); // 防止重复安装
90
+
91
+ return {
92
+ /**
93
+ * 为指定 MyEvent 实例安装跨页插件。
94
+ * @param {MyEvent} bus - 事件总线实例
95
+ * @param {Options} [opts] - 配置项
96
+ */
97
+ install(bus, opts = {}) {
98
+ if (INSTALLED.has(bus)) return;
99
+ INSTALLED.add(bus);
100
+
101
+ const ns = `___my-event-cross-page-${opts.namespace || "default"}___`;
102
+ const delay = opts.throttle || 16;
103
+ let last = 0;
104
+
105
+ // 1、重写 emit
106
+ const rawEmit = bus.emit;
107
+ bus.emit = function (name, data, fnThis) {
108
+ rawEmit.call(bus, name, data, fnThis); // 本地先执行
109
+ const now = Date.now();
110
+ if (now - last < delay) return;
111
+ last = now;
112
+ const key = ns + name;
113
+ try {
114
+ localStorage.setItem(key, JSON.stringify({ name, data, ts: now }));
115
+ localStorage.removeItem(key); // 触发 storage 事件
116
+ } catch (e) {}
117
+ };
118
+
119
+ // 2、监听其他页广播
120
+ function onStorageHandler(e) {
121
+ if (!e.key || !e.key.startsWith(ns)) return;
122
+ let payload;
123
+ try {
124
+ payload = JSON.parse(e.newValue || "{}");
125
+ } catch {
126
+ return;
127
+ }
128
+ if (!payload.ts || payload.ts <= last) return;
129
+ rawEmit.call(bus, e.key.slice(ns.length), payload.data); // 仅本地
130
+ }
131
+ addEventListener("storage", onStorageHandler);
132
+
133
+ // 3、保存卸载器
134
+ bus._uninstallCrossPage = () => {
135
+ removeEventListener("storage", onStorageHandler);
136
+ bus.emit = rawEmit;
137
+ INSTALLED.delete(bus);
138
+ };
139
+ },
140
+
141
+ /**
142
+ * 卸载插件,恢复原始 emit 并停止监听。
143
+ * @param {MyEvent} bus - 事件总线实例
144
+ */
145
+ uninstall(bus) {
146
+ if (typeof bus._uninstallCrossPage === "function") bus._uninstallCrossPage();
147
+ }
148
+ };
149
149
  })();
150
150
 
151
151
  export { MyEvent, MyEvent_CrossPagePlugin };