@@ -698,16 +698,16 @@ https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
698
698
DOMEvent 的主要内容有:
699
699
700
700
1. type:交互事件类型名称,有 resize、contextmenu、mousedown、mousemove、mouseup、touchstart、touchmove、wheel、keydown
701
- 2. preventDefault、stopPropagation:阻止事件冒泡函数
701
+ 2. preventDefault() 、stopPropagation():阻止事件冒泡的函数
702
702
3. `[key: string]: any`:这里虚拟定义了一些属性值,例如鼠标事件对应的一些属性值 clientX/clientY/pageX....,或 触摸事件对应的 pageX/pageY 等。
703
703
704
704
**具体代码:**
705
705
706
706
```
707
707
export interface DOMEvent {
708
708
type: string
709
- preventDefault: () => void
710
- stopPropagation: () => void
709
+ preventDefault(): void
710
+ stopPropagation(): void
711
711
[ key: string] : any
712
712
}
713
713
```
@@ -904,3 +904,165 @@ export default ElementProxyReceiver
904
904
905
905
906
906
907
+ ### 管理模拟DOM元素的类:ProxyManager
908
+
909
+ ProxyManager 用来管理 模拟的DOM元素,具体的属性和方法有:
910
+
911
+ 1. targets:内部用来存放所有 模拟的 DOM 元素集合
912
+
913
+ > 我们使用 Object 来作为储存集合的类型,事实上也可以使用 Map 这种类型。
914
+
915
+ 2. makeProxy():负责创建 模拟的 DOM 元素。
916
+
917
+ 3. getProxy():负责获取 模拟的 DOM 元素
918
+
919
+ 4. deleteProxy():负责删除 模拟的 DOM 元素
920
+
921
+ 5. handleProxyMessage():负责接收事件调度命令,并将该调度命令转发给指定的 模拟的 DOM 元素
922
+
923
+ > 在原版教程中,使用的是 handleEvent(),但是我认为原版教程中的 handleEvent() 名称很容易让人误解为这是一个事件处理函数,事实上并不是事件,而是普通的调用函数。
924
+ >
925
+ > 同时为了避免容易和 ElementProxyReceiver 的 handEvent() 混淆,所以我才使用 handleProxyMessage() 的。
926
+
927
+
928
+
929
+ **具体代码形式 1:**
930
+
931
+ 先看一下,假设 targets 为 Object 类型时对应的代码。
932
+
933
+ ```
934
+ import ElementProxyReceiver from "./element-proxy-receiver"
935
+ import { WorkerMessage } from "./worker-message"
936
+
937
+ export interface ProxyMessageData {
938
+ id: number,
939
+ message: WorkerMessage
940
+ }
941
+
942
+ class ProxyManager {
943
+
944
+ targets: { [key: number]: ElementProxyReceiver }
945
+
946
+ constructor() {
947
+ this.targets = {}
948
+ this.handleProxyMessage = this.handleProxyMessage.bind(this)
949
+ }
950
+
951
+ makeProxy(id: number) {
952
+ const proxy = new ElementProxyReceiver()
953
+ this.targets[id] = proxy
954
+ }
955
+
956
+ getProxy(id: number) {
957
+ return this.targets[id]
958
+ }
959
+
960
+ deleteProxy(id: number) {
961
+ delete this.targets[id]
962
+ }
963
+
964
+ handleProxyMessage(data: ProxyMessageData) {
965
+ this.targets[data.id].handleEvent(data.message)
966
+ }
967
+ }
968
+
969
+ export default ProxyManager
970
+ ```
971
+
972
+ > 上面中的 id 我们选择 number,而非 string 的原因是:我们在后面需要定义的 ElementProxy 中,可以不断 将数字 id++ 从而获得新的 id 值。
973
+
974
+
975
+
976
+ **具体代码形式 2:**
977
+
978
+ 如果 targets 我们不使用 Object 类型,而是使用 Map 类型,那么就可以利用 Map 的特性额外延展出其他非常多的方法,例如 hasProxy() 等,虽然这些方法对我们本示例来说并没有用到,但是还是使用 Map 定义一次。
979
+
980
+ 如果使用 Map 类型,那么代码如下:
981
+
982
+ ```
983
+ import ElementProxyReceiver from "./element-proxy-receiver"
984
+ import { WorkerMessage } from "./worker-message"
985
+
986
+ export interface ProxyMessageData {
987
+ id: number,
988
+ message: WorkerMessage
989
+ }
990
+
991
+ class ProxyManager {
992
+
993
+ targets: Map<number, ElementProxyReceiver>
994
+
995
+ constructor() {
996
+ this.targets = new Map()
997
+ this.handleProxyMessage = this.handleProxyMessage.bind(this)
998
+ }
999
+
1000
+ makeProxy(id: number) {
1001
+ return this.targets.set(id, new ElementProxyReceiver())
1002
+ }
1003
+
1004
+ getProxy(id: number) {
1005
+ return this.targets.get(id)
1006
+ }
1007
+
1008
+ deleteProxy(id: number) {
1009
+ return this.targets.delete(id)
1010
+ }
1011
+
1012
+ handleProxyMessage({ id, message }: ProxyMessageData) {
1013
+ const element = this.targets.get(id)
1014
+ if (element) {
1015
+ element.handleEvent(message)
1016
+ } else {
1017
+ throw new Error(`ProxyManager: Can't find target by id ${id}`)
1018
+ }
1019
+ }
1020
+ }
1021
+
1022
+ export default ProxyManager
1023
+ ```
1024
+
1025
+ > 我们通过 `targets: Map<number, ElementProxyReceiver>` 这种形式 可以约束 targets 的结构类型。
1026
+
1027
+ > 你可能注意到,在上面代码中 setProxy()、deleteProxy() 分别新增了 return ,这样其实是利用了 Map 类型的一些操作特性,让我们的函数不光可以执行,而且还可以返回执行结果。
1028
+
1029
+ > 同时,我们也将 handleProxyMessage(data: ProxyMessageData) 参数 data 进行了解构。
1030
+
1031
+
1032
+
1033
+ 虽然看上去使用 Map 要比 Object 的代码量更多,但是 Map 这种写法会让我们的代码更加严谨。
1034
+
1035
+ > 当然事实情况是我们的 ProxyManager 操作并不会频繁,使用哪种方式差别不大,所以以上 2 种定义 ProxyManger 的代码都是可以的。
1036
+
1037
+
1038
+
1039
+ **补充说明:**
1040
+
1041
+ 在我看来,如果项目本身并不复杂,且网页需要管理的用户交互操作只有一个 DOM 元素,那么我们是完全没有必要使用 ProxyManager 的。
1042
+
1043
+ 但是原版教程中既然这样做了,那我也对应的实现一下 ProxyManager。
1044
+
1045
+
1046
+
1047
+ **关于 ProxyManager 作用的再次补充说明 :**
1048
+
1049
+ 事实上你可以将 ProxyManager 简单粗暴的理解成 document,也就是说:
1050
+
1051
+ 1. ProxyManager.makeProxy() 理解成 document.createElement()
1052
+ 2. ProxyManage.getProxy() 理解成 document.getElementById()
1053
+
1054
+
1055
+
1056
+ 至此,我们目前已经实现的有:
1057
+
1058
+ 1. 模拟 DOM 元素的 ElementProxyReceiver
1059
+ 2. 模拟 DOM 事件的 DOMEvent
1060
+ 3. 模拟 document 的 ProxyManager
1061
+ 4. 定义 主线程与 web worker 发送消息格式的 WorkerMessage
1062
+
1063
+ 接下来就需要来实现负责 主线程与 web worker 之间搭建沟通桥梁的 ElementProxy。
1064
+
1065
+
1066
+
1067
+ ### 负责主线程与WebWorker通信的类:ElementProxy
1068
+
0 commit comments