上篇文章講了 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 之後會進一步完善。
這次的東西寫是寫完了,但不知道是不是隻有我自己能看懂自己寫的啥。就當練手把。有空把整個專案的實現都寫一遍,複習下也行,感覺不僅文筆差,思路好像也不是很好吧。