Skip to content

Commit c5b7e4d

Browse files
committed
update use DOM in worker
1 parent c454cb3 commit c5b7e4d

File tree

1 file changed

+165
-3
lines changed

1 file changed

+165
-3
lines changed

22 Three.js优化之OffscreenCanvas与WebWorker.md

Lines changed: 165 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -698,16 +698,16 @@ https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
698698
DOMEvent 的主要内容有:
699699
700700
1. type:交互事件类型名称,有 resize、contextmenu、mousedown、mousemove、mouseup、touchstart、touchmove、wheel、keydown
701-
2. preventDefault、stopPropagation:阻止事件冒泡函数
701+
2. preventDefault()、stopPropagation():阻止事件冒泡的函数
702702
3. `[key: string]: any`:这里虚拟定义了一些属性值,例如鼠标事件对应的一些属性值 clientX/clientY/pageX....,或 触摸事件对应的 pageX/pageY 等。
703703
704704
**具体代码:**
705705
706706
```
707707
export interface DOMEvent {
708708
type: string
709-
preventDefault: () => void
710-
stopPropagation: () => void
709+
preventDefault(): void
710+
stopPropagation(): void
711711
[key: string]: any
712712
}
713713
```
@@ -904,3 +904,165 @@ export default ElementProxyReceiver
904904
905905
906906
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

Comments
 (0)