其实主要是下面红框部分,而且我的rpc调用是自定义序列化和反序列化,不是protobuf
底层看你自己选择了,萝卜青菜各有所爱,我是基于 proto 开发的
好好好,cv一下
不明白你这两种方式有啥区别,我是为了更贴合真实RPC调用才这样实现的,比如远程服务器有一个RPC服务:
前端TS调用可以这样:
Global.remotingInvoke("DemoService:sum", [1, 1000]).then(res => {
console.log("远程计算两数之间所有数字之和:" + res);
}).catch(err => {
console.log(err);
});
还可以如下这样:
服务端:
客户端调用:
我是从 tsrpc 这个项目中第一次看到这种用法,这种无论怎样,都是要有一个数据和消息的关连关系来辅助类型推断。
要么在消息数据体 data 中,要么是对应关系映射表。
其中映射表的方式除了集中式(事件一般是集中式)。还可以用 global declare {
interface XX {
[event_name]: event_data_type
}
}
进行分布式定义。而这种机制可以泛化到【类型到类】【类型到函数】【类型到类型】,这种机制的复用让理解成本更低。
TS的类型比C#要强大,很多C#办不到或很难办到的,用TS就比较好实现。
按我的理解事件系统是为了解决“你中无我,我中无你,但是我们俩还想通信”,或者是为了达到“你中无我,我中无你”的状态而引入事件系统以求解耦。还有一种情况比较简单“你中有我,但是我中不能有你,因为可能循环引用”,虽然可以用接口去解决,但是也可以用事件系统达到目的。
题主的事件系统能搞这么复杂,也是让人头皮发麻,没有看下去的欲望,我一度以为我理解的事件系统和题主理解的不是一回事,我都懒得造轮子,感觉造出来也没啥意义,直接用的EventEmitter3,只是简单封装了下,封装的目的是为了方便使用仅此而已。
源码:
例子:
代码提示:
我觉得这就够了,而且type申明的类型构建后也擦除了
这个类型 最后其实就是直接调用了那个函数本身 不算是发消息就是找到那个对象的地址直接发消息差不多的样子,事件系统一般就是向世界抛出一个事件谁监听了这个时间 谁就可以获取到这个事件的数据
直接用game.emit 感觉一样的呀 
就是为了解耦才用事件,结果还给事件类型耦合起来…
楼主是为了解决数据类型约束,不过方案过于复杂
耦合的主体有区别,你说的是调用主体解耦。楼主说的是事件派发的数据类型约束
你这个 IEvent 类型方案其实和 MK 的类似,我以前尝试过,结论是:不好用。会有几个问题点
1、集中式的声明。有些逻辑其实是分包的,这个 IEvent 没有支持分布式定义。(当然 declare global 可解决这个问题)
2、所有事件都要声明(这个比较蛋疼,越用越难受)
3、事件处理函数有时候是多个函数。有的函数只心事件,不关心参数,通常是类似 refreshUI() 的刷新函数,而注册时就会报红,导致必须加 as any 这类的特殊处理。难受。
我的方案就是,通过约定做隐式数据约束。
emit<DATA = any>(name: string | number, data: DATA = null)
on<DATA = any>(name: string | number, cb: (data?: DATA) => any, ctx: any): EventBox;
于是,在开发的时候:
1、派发的地方有数据,前面的类型一定要补上,方便知道是什么数据类型;
2、监听的地方,一定根据派发的数据约束写上数据约束(不写表示只关心事件本身);
3、在新增消息处理函数的时候,只要查一下别的地方怎么用,然后做同样的约束。
4、派发数据更改时(重构)的时候。也是查下消息的引用。统一修改各个监听函数的处理逻辑的。
这套逻辑流程用起来最舒服。
ikun 2.5 year

最佩服一个事件系统都可以有自己一套理论的人了 
两个emitter 更难用
我只能说明你的不好用是针对你自己的,原因如下
-
分布式定义事件类型是最差的方案,因为不能一眼看到有什么事件,只有自己知道或者利用编辑器查看引用,这本身就是浪费时间和协作开发的拖累
-
事件类型只需要定义一次,和 Enum 方案相比只是多了几个参数但是保证了类型安全
-
事件必须关注参数,除非你不用事件参数,或者不在乎事件参数类型和数量是否正确
你的方案就是直接忽略事件参数校验,我可以随便传递任何类型的事件参数但是不会在编辑器报错,只有运行时报错







