@kkarum/framework 2.3.17 → 2.3.19

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,414 @@
1
+ # Framework API
2
+
3
+ 本文档记录业务开发最常复用的框架 API。除非维护框架本身,不要直接修改 `assets/framework`。
4
+
5
+ ## 全局入口
6
+
7
+ ### `FW.Entry`
8
+
9
+ 常用属性:
10
+
11
+ | 属性 | 用途 |
12
+ | --- | --- |
13
+ | `bundleName` | 当前业务 bundle 名 |
14
+ | `scene` | 当前 `FW.Scene` |
15
+ | `resMgr` | 资源和 bundle 管理 |
16
+ | `layerMgr` | Layer 打开、关闭、栈、队列 |
17
+ | `uiMgr` | UI 事件、节点查找 |
18
+ | `evtMgr` | 框架事件总线 |
19
+ | `timeMgr` | 定时器和 update |
20
+ | `promiseMgr` | 可取消/超时/重试 Promise |
21
+ | `socketMgr` | Socket 连接 |
22
+ | `objectMgr` | 对象池 |
23
+ | `taskMgr` | 分帧任务 |
24
+ | `languageMgr` | 多语言 |
25
+ | `performanceMgr` | 性能采样 |
26
+ | `utils` | 通用工具 |
27
+
28
+ 方法:
29
+
30
+ ```ts
31
+ FW.Entry.getComponent<T>(identifier): T
32
+ FW.Entry.getComponents<T>(identifier?): T[]
33
+ FW.Entry.launchScene(bundleName, forceRelease?)
34
+ FW.Entry.register({ bundleName, sceneName, depend, autoRelease })
35
+ FW.Entry.unRegister(...)
36
+ FW.Entry.getDepend(bundleName): string[]
37
+ FW.Entry.update(dt)
38
+ FW.Entry.restart()
39
+ ```
40
+
41
+ ## 依赖容器
42
+
43
+ ### `FW.Framework`
44
+
45
+ ```ts
46
+ FW.Framework.register({
47
+ bundleName,
48
+ logic,
49
+ data,
50
+ config,
51
+ sender,
52
+ handle,
53
+ });
54
+
55
+ FW.Framework.unRegister(...)
56
+ FW.Framework.getComponent<T>(ClassOrKey)
57
+ FW.Framework.getComponents<T>(BaseClass)
58
+ FW.Framework.addRegistry(bundleName, RegistryClass)
59
+ FW.Framework.createRegistry(bundleName)
60
+ FW.Framework.destroyRegistry(bundleName)
61
+ FW.Framework.restart()
62
+ ```
63
+
64
+ 绑定 key 规则:`${bundleName}${FW.SystemDefine.FWBindTag.*}`。
65
+
66
+ 业务代码通常通过 `FW.Entry.getComponent()` 间接使用,不直接操作容器。
67
+
68
+ ## 基类
69
+
70
+ ### `FW.FrameworkBase`
71
+
72
+ 继承者:
73
+
74
+ - `FW.Logic`
75
+ - `FW.Data`
76
+ - `FW.Service`
77
+ - `FW.Sender`
78
+ - `FW.Handle`
79
+ - `FWManager`
80
+ - `FW.LayerController`
81
+
82
+ 可用方法:
83
+
84
+ ```ts
85
+ this.entry
86
+ this.getLogic<T>()
87
+ this.getData<T>()
88
+ this.getConfig<T>()
89
+ this.getSender<T>()
90
+ this.getHandle<T>()
91
+ this.initializeDependencies()
92
+ await this.invoke(promise, "operationName")
93
+ ```
94
+
95
+ 自动注入依赖依赖类名约定:
96
+
97
+ - `*Logic`
98
+ - `*Data`
99
+ - `*Config` 或 `*AssetConfig`
100
+ - `*Sender` 或 `*SocketSender`
101
+ - `*Handle` 或 `*SocketHandle`
102
+
103
+ 类名还必须包含 bundle 名。例如 bundle 为 `shop`,推荐 `ShopLogic`、`ShopData`。
104
+
105
+ ## Registry
106
+
107
+ ### `FW.Registry`
108
+
109
+ ```ts
110
+ abstract readonly bundleName: string;
111
+ readonly autoRelease?: boolean;
112
+ readonly sceneName?: string;
113
+ readonly depend?: string[];
114
+ readonly logic?: FW.Newable<FW.Logic>;
115
+ readonly data?: FW.Newable<FW.Data>;
116
+ readonly config?: FW.Newable<FW.AssetConfig>;
117
+ readonly sender?: FW.Newable<FW.Sender>;
118
+ readonly handle?: FW.Newable<FW.Handle>;
119
+ register(): void;
120
+ unRegister(): void;
121
+ ```
122
+
123
+ 注册表用于把 bundle 元信息和业务组件交给框架。bundle 加载/释放会自动调用。
124
+
125
+ ## Layer API
126
+
127
+ ### `FW.Entry.layerMgr`
128
+
129
+ ```ts
130
+ openAsync<Ctr>({ type, parent?, position?, args? }): Promise<Ctr>
131
+ openSync<Ctr>({ type, parent?, position?, args? }): Ctr
132
+ close(ctr): Promise<Ctr>
133
+ closeFromStack(count?)
134
+ closeFromLayerName(name | name[])
135
+ displayLayer(ctr)
136
+ hideLayer(ctr)
137
+ removeAllChildren(node)
138
+ clear()
139
+ openDebug()
140
+ getLayerMap()
141
+ ```
142
+
143
+ `LayerOpenArgs`:
144
+
145
+ ```ts
146
+ type LayerOpenArgs = {
147
+ type: new () => FW.LayerController;
148
+ parent?: cc.Node;
149
+ position?: { x: number; y: number };
150
+ args?: any;
151
+ };
152
+ ```
153
+
154
+ ### `FW.LayerController`
155
+
156
+ 业务控制器必须实现:
157
+
158
+ ```ts
159
+ renderOrder: FW.SystemDefine.FWLayerRenderOrder;
160
+ layerType: FW.SystemDefine.FWLayerType;
161
+ layer: FW.Layer;
162
+ layerAssetProperty: FW.AssetProperty;
163
+ autoRelease: boolean;
164
+ isRepeatOpen: boolean;
165
+ ```
166
+
167
+ 生命周期:
168
+
169
+ ```ts
170
+ initialize()
171
+ onInit(...args)
172
+ onLoad()
173
+ onStart()
174
+ onEnable()
175
+ onDisable()
176
+ onUpdate(dt)
177
+ onLateUpdate(dt)
178
+ onClose()
179
+ onDestroy()
180
+ destroy()
181
+ ```
182
+
183
+ 便捷方法:
184
+
185
+ ```ts
186
+ find(path, referenceNode)
187
+ findComponent<T>(path, referenceNode, type)
188
+ registerEvent(args)
189
+ cc(targetNode, cb, responseInterval?, eventName?)
190
+ fw(eventName, cb, target?, responseInterval?)
191
+ pauseEvent()
192
+ resumeEvent()
193
+ hide()
194
+ display()
195
+ close()
196
+ ```
197
+
198
+ ## 资源 API
199
+
200
+ ### `FW.AssetProperty`
201
+
202
+ ```ts
203
+ type AssetProperty = {
204
+ resourceId?: number;
205
+ bundle?: string;
206
+ path: string;
207
+ type?: typeof cc.Asset;
208
+ cb?: (asset: FW.AssetData | FW.AssetData[]) => void;
209
+ progress?: (finish: number, total: number, item: cc.AssetManager.RequestItem) => void;
210
+ user?: string;
211
+ autoRelease?: boolean;
212
+ priorityLoaded?: boolean;
213
+ };
214
+ ```
215
+
216
+ ### `FW.Entry.resMgr`
217
+
218
+ ```ts
219
+ loadBundle(bundleName): Promise<cc.AssetManager.Bundle>
220
+ getBundle(bundleName): cc.AssetManager.Bundle
221
+ releaseBundle(bundleName): void
222
+ hasBundle(bundleName): boolean
223
+
224
+ loadAssetData<T>(assetProperty): Promise<FW.AssetData>
225
+ loadAsset<T>(assetProperty, texture?): Promise<T>
226
+ getAssetData<T>(assetProperty, texture?): FW.AssetData
227
+ getAsset<T>(assetProperty, texture?): T
228
+ preLoad(assetProperty | assetProperty[])
229
+ loadDir(assetProperty): Promise<FW.AssetData[]>
230
+ loadRemote<T>(url, options?): Promise<T>
231
+ loadSpineDataFromRemote({ img, ske, atlas, bin? }): Promise<sp.SkeletonData>
232
+ releaseAsset(assetProperty): void
233
+ hasAsset(assetProperty): boolean
234
+ registerRejectHandler(listener): void
235
+ ```
236
+
237
+ 纹理注意:默认 `Texture2D` 会转换为 `SpriteFrame`。需要原始纹理时传 `texture: true`。
238
+
239
+ ## 事件 API
240
+
241
+ ### `FW.Entry.evtMgr`
242
+
243
+ ```ts
244
+ register(eventName, cb, target, options?)
245
+ registerOnce(eventName, cb, target, options?)
246
+ dispatch(eventName, ...args)
247
+ unRegister(eventName, target)
248
+ eventOff(eventName)
249
+ targetOff(target)
250
+ targetPause(target)
251
+ targetResume(target)
252
+ has(eventName, target)
253
+ getListenerCount(eventName)
254
+ ```
255
+
256
+ `options`:
257
+
258
+ ```ts
259
+ {
260
+ priority?: FW.SystemDefine.FWPriorityOrder;
261
+ intercept?: boolean;
262
+ once?: boolean;
263
+ }
264
+ ```
265
+
266
+ ### `FW.Entry.uiMgr`
267
+
268
+ ```ts
269
+ register({ target?, CCEvent?, FWEvent? })
270
+ find(path, rootNode): cc.Node | null
271
+ pauseEvent(target)
272
+ resumeEvent(target)
273
+ unRegisterEvent(args)
274
+ unRegisterTarget(target)
275
+ unRegisterAll()
276
+ hasRegister(eventName, target)
277
+ buttonEnable(target)
278
+ buttonDisable(target)
279
+ ```
280
+
281
+ ## Time API
282
+
283
+ ```ts
284
+ FW.Entry.timeMgr.schedule(cb, intervalSeconds?, repeat?, target?, tag?)
285
+ FW.Entry.timeMgr.scheduleOnce(cbOrTime?, timeOrTargetOrTag?, targetOrTag?)
286
+ FW.Entry.timeMgr.update(cb, tagOrTarget?, target?)
287
+ FW.Entry.timeMgr.pauseSchedule(tag | id | target)
288
+ FW.Entry.timeMgr.resumeSchedule(tag | id | target)
289
+ FW.Entry.timeMgr.unSchedule(tag | id | target)
290
+ FW.Entry.timeMgr.onUpdate(dt)
291
+ ```
292
+
293
+ 返回的 `TimerSchedule` 支持:
294
+
295
+ ```ts
296
+ unSchedule()
297
+ pauseSchedule()
298
+ resumeSchedule()
299
+ promise? // scheduleOnce
300
+ ```
301
+
302
+ ## Promise API
303
+
304
+ ```ts
305
+ const proxy = FW.Entry.promiseMgr.execute(executor, {
306
+ reason,
307
+ timeout,
308
+ retryCount,
309
+ retryInterval,
310
+ retryCondition,
311
+ });
312
+
313
+ proxy.id
314
+ proxy.promise
315
+ proxy.status
316
+ proxy.abort(reason?)
317
+ proxy.addAbortEventListener(listener)
318
+ ```
319
+
320
+ 批量:
321
+
322
+ ```ts
323
+ FW.Entry.promiseMgr.all([proxy1, proxy2], options)
324
+ FW.Entry.promiseMgr.cancel(id, reason?)
325
+ FW.Entry.promiseMgr.cancelMultiple(ids, reason?)
326
+ FW.Entry.promiseMgr.cancelAll(reason?)
327
+ FW.Entry.promiseMgr.getStatus(id)
328
+ FW.Entry.promiseMgr.getAllStatus()
329
+ FW.Entry.promiseMgr.getActiveCount()
330
+ FW.Entry.promiseMgr.clearCompletedPromise()
331
+ ```
332
+
333
+ ## Socket API
334
+
335
+ ### `FW.Entry.socketMgr`
336
+
337
+ ```ts
338
+ createSocket(tag, address, sender, handle, config): Promise<FW.Socket>
339
+ getSocket(tag?): FW.Socket
340
+ getSocketMap(): Map<string, FW.Socket>
341
+ closeSocket(socket)
342
+ closeAll()
343
+ pauseMessageHandle()
344
+ resumeMessageHandle()
345
+ ```
346
+
347
+ ### `FW.Sender`
348
+
349
+ ```ts
350
+ sendHeart?()
351
+ onHeart?(msg): Promise<boolean>
352
+ onBeforeSendingMessage?(msg): Promise<FW.SocketMessage>
353
+ getProtocolKey?(msg): string
354
+ send(msg)
355
+ ```
356
+
357
+ ### `FW.Handle`
358
+
359
+ ```ts
360
+ onMessage(msg): void
361
+ onBeforeReceivingMessage?(msg): Promise<FW.SocketMessage>
362
+ onHeart?(msg): Promise<boolean>
363
+ getProtocolKey?(msg): string
364
+ onOpen?()
365
+ onClose?()
366
+ onError?(msg)
367
+ onTimeout?()
368
+ onWeakNetWork?()
369
+ ```
370
+
371
+ ## Object Pool API
372
+
373
+ ```ts
374
+ const pool = await FW.Entry.objectMgr.createObjectPool(prefabOrNodeOrAsset, parent, tagOrType?)
375
+ FW.Entry.objectMgr.getObjectPool(tag)
376
+ FW.Entry.objectMgr.destroyObjectPool(tag)
377
+ FW.Entry.objectMgr.getPoolStats()
378
+ FW.Entry.objectMgr.clearAllPools()
379
+ ```
380
+
381
+ `FW.ObjectPool`:
382
+
383
+ ```ts
384
+ get(...args): FW.ObjectProperty
385
+ put(node | component | uniqueId)
386
+ puts(array)
387
+ findObjectProperty(node | component | uniqueId)
388
+ isEmpty()
389
+ getEnableObjectMap()
390
+ getDisableObjectMap()
391
+ getAllObjectMap()
392
+ onDestroy()
393
+ ```
394
+
395
+ 池对象组件继承 `FW.Object`,实现 `onInit()`、`onGet()`、`onPut()`、`onDestroy()`。
396
+
397
+ ## 装饰器
398
+
399
+ 全局装饰器由 `initializeFramework()` 挂到 `globalThis`:
400
+
401
+ ```ts
402
+ @FWPropertyNode({ path? })
403
+ @FWPropertyNodes(...paths)
404
+ @FWPropertyComponent(ComponentClass, childName?, mute?)
405
+ @FWPropertyComponents(ComponentClass, childName?)
406
+ @PerformanceMonitor(operationName?)
407
+ @FWDeprecated(description?)
408
+ @FWSocketAutoProcessPause()
409
+ @FWSocketAutoProcessResume()
410
+ @AutoRegisterCCEvent(nodePath, eventName?)
411
+ @AutoRegisterFWEvent(eventName)
412
+ ```
413
+
414
+ 装饰器会改写 `onLoad` 或方法 descriptor。使用时必须确认目标类生命周期和路径稳定。
@@ -0,0 +1,6 @@
1
+ {
2
+ "ver": "2.0.2",
3
+ "uuid": "66b8acf8-acdf-43e2-a401-8b9afa24b01c",
4
+ "importer": "markdown",
5
+ "subMetas": {}
6
+ }
@@ -0,0 +1,309 @@
1
+ # Framework Examples
2
+
3
+ 本文档提供可复制的业务代码骨架。示例中的 bundle 名使用 `shop`,实际开发时替换为目标业务名。
4
+
5
+ ## Registry
6
+
7
+ ```ts
8
+ import { FWRegistry } from "../../framework/registry/FWRegistry";
9
+ import { ShopLogic } from "../logic/ShopLogic";
10
+ import { ShopData } from "../data/ShopData";
11
+ import { ShopAssetConfig } from "../config/ShopAssetConfig";
12
+ import { ShopSender } from "../socket/ShopSender";
13
+ import { ShopHandle } from "../socket/ShopHandle";
14
+
15
+ export class ShopRegistry extends FWRegistry {
16
+ readonly bundleName = "shop";
17
+ readonly sceneName = "ShopScene";
18
+ readonly depend = ["common"];
19
+ readonly autoRelease = true;
20
+ readonly logic = ShopLogic;
21
+ readonly data = ShopData;
22
+ readonly config = ShopAssetConfig;
23
+ readonly sender = ShopSender;
24
+ readonly handle = ShopHandle;
25
+ }
26
+ ```
27
+
28
+ ## AssetConfig
29
+
30
+ ```ts
31
+ export class ShopAssetConfig extends FW.AssetConfig {
32
+ preLoad: FW.LoadConfig = {
33
+ prefab: {
34
+ ShopLayer: {
35
+ bundle: "shop",
36
+ path: "prefab/ShopLayer",
37
+ type: cc.Prefab,
38
+ priorityLoaded: true,
39
+ autoRelease: true,
40
+ },
41
+ ShopItem: {
42
+ bundle: "shop",
43
+ path: "prefab/ShopItem",
44
+ type: cc.Prefab,
45
+ },
46
+ },
47
+ };
48
+
49
+ demandLoad: FW.LoadConfig = {
50
+ texture: {
51
+ CoinIcon: {
52
+ bundle: "shop",
53
+ path: "texture/CoinIcon",
54
+ type: cc.Texture2D,
55
+ },
56
+ },
57
+ };
58
+ }
59
+ ```
60
+
61
+ ## Data
62
+
63
+ ```ts
64
+ export type ShopGoods = {
65
+ id: number;
66
+ name: string;
67
+ price: number;
68
+ icon: string;
69
+ };
70
+
71
+ export class ShopData extends FW.Data {
72
+ private goods: ShopGoods[] = [];
73
+
74
+ setGoods(goods: ShopGoods[]) {
75
+ this.goods = goods || [];
76
+ FW.Entry.evtMgr.dispatch("SHOP_GOODS_CHANGED", this.goods);
77
+ }
78
+
79
+ getGoods() {
80
+ return this.goods;
81
+ }
82
+ }
83
+ ```
84
+
85
+ ## Logic
86
+
87
+ ```ts
88
+ export class ShopLogic extends FW.Logic {
89
+ async openShop() {
90
+ await FW.Entry.layerMgr.openAsync({
91
+ type: ShopLayerController,
92
+ });
93
+ }
94
+
95
+ async refreshGoods() {
96
+ const response = await this.invoke(
97
+ this.getSender<ShopSender>().requestGoods(),
98
+ "requestGoods",
99
+ );
100
+
101
+ if (!response) return;
102
+ this.getData<ShopData>().setGoods(response.goods);
103
+ }
104
+ }
105
+ ```
106
+
107
+ ## LayerController
108
+
109
+ ```ts
110
+ export class ShopLayerController extends FW.LayerController {
111
+ renderOrder = FW.SystemDefine.FWLayerRenderOrder.UI;
112
+ layerType = FW.SystemDefine.FWLayerType.REPEAT;
113
+ autoRelease = true;
114
+ isRepeatOpen = false;
115
+
116
+ private listNode: cc.Node;
117
+ private closeButton: cc.Node;
118
+
119
+ get layerAssetProperty(): FW.AssetProperty {
120
+ return this.getConfig<ShopAssetConfig>().preLoad.prefab["ShopLayer"] as FW.AssetProperty;
121
+ }
122
+
123
+ onInit() {
124
+ this.cacheNodes();
125
+ this.bindEvents();
126
+ this.getLogic<ShopLogic>().refreshGoods();
127
+ }
128
+
129
+ private cacheNodes() {
130
+ this.listNode = this.find("GoodsList", this.layer.node);
131
+ this.closeButton = this.find("CloseButton", this.layer.node);
132
+ }
133
+
134
+ private bindEvents() {
135
+ this.cc(this.closeButton, this.onCloseClick);
136
+ this.fw("SHOP_GOODS_CHANGED", this.renderGoods);
137
+ }
138
+
139
+ private onCloseClick = () => {
140
+ this.close();
141
+ };
142
+
143
+ private renderGoods = (goods: ShopGoods[]) => {
144
+ // 渲染列表或刷新虚拟列表
145
+ };
146
+ }
147
+ ```
148
+
149
+ ## Button Event
150
+
151
+ ```ts
152
+ private bindEvents() {
153
+ const buyButton = this.find("BuyButton", this.layer.node);
154
+ this.cc(buyButton, this.onBuyClick, 500);
155
+ }
156
+
157
+ private onBuyClick = () => {
158
+ this.getLogic<ShopLogic>().buySelectedGoods();
159
+ };
160
+ ```
161
+
162
+ ## Framework Event
163
+
164
+ ```ts
165
+ // dispatch
166
+ FW.Entry.evtMgr.dispatch("SHOP_BUY_SUCCESS", goodsId);
167
+
168
+ // listen inside LayerController
169
+ this.fw("SHOP_BUY_SUCCESS", this.onBuySuccess);
170
+
171
+ private onBuySuccess = (goodsId: number) => {
172
+ FW.Log.debug("buy success", goodsId);
173
+ };
174
+ ```
175
+
176
+ ## Resource Load
177
+
178
+ ```ts
179
+ async loadCoinIcon(sprite: cc.Sprite) {
180
+ const cfg = this.getConfig<ShopAssetConfig>();
181
+ const frame = await this.invoke(
182
+ FW.Entry.resMgr.loadAsset<cc.SpriteFrame>(
183
+ cfg.demandLoad.texture["CoinIcon"] as FW.AssetProperty,
184
+ ),
185
+ "loadCoinIcon",
186
+ );
187
+
188
+ if (frame && cc.isValid(sprite)) {
189
+ sprite.spriteFrame = frame;
190
+ }
191
+ }
192
+ ```
193
+
194
+ ## Object Pool Item
195
+
196
+ ```ts
197
+ export class ShopItem extends FW.Object {
198
+ private label: cc.Label;
199
+
200
+ onInit() {
201
+ this.label = this.node.getChildByName("Name").getComponent(cc.Label);
202
+ }
203
+
204
+ onGet(args: any[]) {
205
+ const goods = args[0] as ShopGoods;
206
+ this.label.string = goods.name;
207
+ }
208
+
209
+ onPut() {
210
+ this.label.string = "";
211
+ }
212
+ }
213
+ ```
214
+
215
+ ```ts
216
+ const cfg = this.getConfig<ShopAssetConfig>();
217
+ const prefab = await FW.Entry.resMgr.loadAsset<cc.Prefab>(
218
+ cfg.preLoad.prefab["ShopItem"] as FW.AssetProperty,
219
+ );
220
+ const pool = await FW.Entry.objectMgr.createObjectPool(prefab, this.listNode, "shop_item");
221
+ const item = pool.get(goods);
222
+ ```
223
+
224
+ ## Virtual View
225
+
226
+ ```ts
227
+ const list = this.findComponent<FW.VirtualViewComponent>(
228
+ "GoodsScrollView",
229
+ this.layer.node,
230
+ FWVirtualViewComponent,
231
+ );
232
+
233
+ list.onRender = (item: cc.Node, index: number) => {
234
+ const goods = this.getData<ShopData>().getGoods()[index];
235
+ item.getChildByName("Name").getComponent(cc.Label).string = goods.name;
236
+ };
237
+
238
+ list.count = this.getData<ShopData>().getGoods().length;
239
+ ```
240
+
241
+ ## Promise With Timeout
242
+
243
+ ```ts
244
+ async waitForAnimation(node: cc.Node) {
245
+ const proxy = FW.Entry.promiseMgr.execute<void>((resolve) => {
246
+ cc.tween(node)
247
+ .to(0.2, { opacity: 255 })
248
+ .call(() => resolve())
249
+ .start();
250
+ }, {
251
+ timeout: 1000,
252
+ });
253
+
254
+ await proxy.promise;
255
+ }
256
+ ```
257
+
258
+ ## Sender And Handle
259
+
260
+ ```ts
261
+ export class ShopSender extends FW.Sender {
262
+ requestGoods() {
263
+ const msg = { cmd: "shop.goods", seq: Date.now() };
264
+ this.send(msg);
265
+ return new Promise<any>((resolve) => {
266
+ FW.Entry.evtMgr.registerOnce(`SOCKET_RESPONSE_${msg.seq}`, resolve, this);
267
+ });
268
+ }
269
+
270
+ sendHeart() {
271
+ this.send({ cmd: "heart" });
272
+ }
273
+
274
+ async onHeart(msg: any) {
275
+ return msg?.cmd === "heart";
276
+ }
277
+
278
+ async onBeforeSendingMessage(msg: any) {
279
+ return JSON.stringify(msg);
280
+ }
281
+
282
+ getProtocolKey(msg: any) {
283
+ return msg?.seq ? String(msg.seq) : "";
284
+ }
285
+ }
286
+ ```
287
+
288
+ ```ts
289
+ export class ShopHandle extends FW.Handle {
290
+ async onBeforeReceivingMessage(event: MessageEvent) {
291
+ return JSON.parse(event.data);
292
+ }
293
+
294
+ onMessage(msg: any) {
295
+ if (msg.seq) {
296
+ FW.Entry.evtMgr.dispatch(`SOCKET_RESPONSE_${msg.seq}`, msg);
297
+ }
298
+ FW.Entry.evtMgr.dispatch(`SOCKET_CMD_${msg.cmd}`, msg);
299
+ }
300
+
301
+ async onHeart(msg: any) {
302
+ return msg?.cmd === "heart";
303
+ }
304
+
305
+ getProtocolKey(msg: any) {
306
+ return msg?.seq ? String(msg.seq) : "";
307
+ }
308
+ }
309
+ ```
@@ -0,0 +1,6 @@
1
+ {
2
+ "ver": "2.0.2",
3
+ "uuid": "95541d8a-714f-40e7-ad8b-7889c597c67a",
4
+ "importer": "markdown",
5
+ "subMetas": {}
6
+ }