WebWorker 中將已處理好的 VDOM 資料提交主執行緒渲染 DOM

Javenn發表於2018-12-28

上篇文章講了 WebWorker 的簡單用法,其實網上很多類似的文章,我寫的也比較垃圾。不會的建議可以網上看比較好點的資料。

這裡我會先講下我的大致思路。然後會貼上一堆不實用的垃圾程式碼供參考。

WebWorker 中必然是無法訪問 DOM 的,更無法建立 DOM 元素。如果想要實現把 Worker 中的東西渲染出來,只能把相關資料什麼的放到主執行緒去渲染。這用訊息機制是可以實現的。

DOM 既然只能在主執行緒渲染,那麼事件 Worker 執行緒自然也是無能為力了。而更關鍵的是 Worker 和主執行緒通訊的時候不能傳遞函式作為引數(toString 這種方式不作數),而事件處理程式都是函式。那麼就只好通過一個標識來代替函式,在 Worker 裡通過這個標識來決定具體執行什麼操作。

Worker 和主執行緒的分工相對是明確的,完善後的程式碼也基本上不怎麼會去寫執行在主執行緒上程式碼的,主要還是去寫執行在 Worker 裡面的程式碼。而等下次要討論的是如何把 HTML 編譯成類似在下邊的 worker.js 程式碼的 render 函式中直接用 JSON 寫的那部分。

再之後要討論 VDOM 相關的演算法,到時候不管寫的多爛,能用就行。

之後的之後就討論一些動畫,事件防抖等的處理方式,要放到很久之後才討論了,還有很多沒想好應該什麼時候討論的事情,一步步來吧,想到哪裡就寫到哪裡,我一個純高技術的,還是亂七八糟都學的人確實寫不出多好的東西來,多練習下也許N年後就好些了吧。

這裡直接不涉及任何 VDOM 相關的東西。所有的東西都是用 JSON 來表示而已。也說過以後的文章才會討論 VDOM,我對 VDOM 也是一知半解。搞出的東西不會有多麼高的保障。自己懂得的還是自己去研究的好,但好在這些演算法開源的實現很多。學習後換成好點的就行了。

HTML 裡面沒有什麼東西:

1 <div id="root"></div>
2 <script src="main.js"></script>

在主執行緒中執行的 main.js 中主要是為了渲染 DOM,繫結事件。這裡的程式碼遠遠不足以滿足正常使用,但這裡只是為了講一部分想法,沒必要寫那麼複雜。能夠渲染 DOM,繫結個事件就行了。至於更多的需求以後慢慢再討論。這裡宣告,這些程式碼完全不足以在專案中使用,不要拿著亂用,不然裡面很多問題你會很難解決的。

 1 var worker = new Worker('worker.js');
 2 
 3 worker.addEventListener('message', function (event) {
 4     console.log(event.data);
 5     var message = event.data;
 6 
 7     switch (message.type) {
 8         case 'render':
 9             render(message.payload);
10             break;
11     }
12 });
13 
14 
15 function render(data) {
16     var root = document.getElementById('root');
17 
18     root.innerHTML = '';
19 
20     root.appendChild(createElement(data));
21 }
22 
23 function setAttrs(element, attrs) {
24     if (attrs) {
25         for (var name in attrs) {
26             element.setAttribute(name, attrs[name]);
27         }
28     }
29 }
30 
31 function setProps(element, props) {
32     if (props) {
33         for (var name in props) {
34             element[name] = props[name];
35         }
36     }
37 }
38 
39 function setEvents(element, events) {
40     if (events) {
41         for (var name in events) {
42             attachEvent(element, name, events[name]);
43         }
44     }
45 }
46 
47 function setChildren(element, children) {
48     if (children && children instanceof Array) {
49         children.forEach(function (item) {
50             element.appendChild(createElement(item));
51         });
52     }
53 }
54 
55 function createElement(data) {
56     if (!data) {
57         return null;
58     }
59 
60     var element = document.createElement(data.type);
61 
62     setAttrs(element, data.attrs);
63     setProps(element, data.props);
64     setEvents(element, data.events);
65     setChildren(element, data.children);
66 
67     return element;
68 }
69 
70 var eventBinders = {
71     onTap: {
72         bind: function (element, name, value) {
73             element.addEventListener('click', function () {
74                 worker.postMessage({
75                     type: 'command',
76                     payload: {
77                         name: value
78                     }
79                 });
80             });
81         }
82     }
83 };
84 
85 function attachEvent(element, name, value) {
86     eventBinders[name].bind(element, name, value);
87 }
88 
89 function createAttribute(element, name, value) {
90     element.setAttribute(name, value);
91 }

在 Worker 執行緒中中執行的 worker.js 會處理很多事務。基本上邏輯什麼的都在裡面了。

 1 var data = {
 2     value: 1
 3 };
 4 
 5 function onDecreaseClick() {
 6     data.value -= 1;
 7     render(data);
 8 }
 9 
10 function onIncreaseClick() {
11     data.value += 1;
12     render(data);
13 }
14 
15 function handleCommand(data) {
16     switch (data.name) {
17         case 'onDecreaseClick':
18             onDecreaseClick(data);
19             break;
20         case 'onIncreaseClick':
21             onIncreaseClick(data);
22             break;
23     }
24 }
25 
26 function render(data) {
27     self.postMessage({
28         type: 'render',
29         payload: {
30             type: 'div',
31             attrs: {},
32             props: {},
33             children: [
34                 {
35                     type: 'button',
36                     events: {
37                         onTap: 'onDecreaseClick',
38                     },
39                     props: {
40                         textContent: 'Decrease'
41                     }
42                 },
43                 {
44                     type: 'span',
45                     props: {
46                         textContent: data.value
47                     }
48                 },
49                 {
50                     type: 'button',
51                     events: {
52                         onTap: 'onIncreaseClick',
53                     },
54                     props: {
55                         textContent: 'Increase'
56                     }
57                 }
58             ]
59         }
60     });
61 }68 
69 self.addEventListener('message', function (event) {
70     var message = event.data;
71 
72     switch (message.type) {
73         case 'command':
74             handleCommand(message.payload);
75             break;
76     }
77 });
78 80 render(data);

程式碼都很簡單粗暴。雖然確實可以跑起來。但是無論是 DOM 渲染 還是 Worker 中的處理都是很簡單的,同時也是不可實際生產使用的。接下來討論過 VDOM 之後會進一步完善。

這次的東西寫是寫完了,但不知道是不是隻有我自己能看懂自己寫的啥。就當練手把。有空把整個專案的實現都寫一遍,複習下也行,感覺不僅文筆差,思路好像也不是很好吧。

相關文章