前端以后也要多線程編程了么?
大家好,我卡頌。
前端工程師多年來習慣了在瀏覽器環境進行單線程開發。
隨著瀏覽器對web worker的廣泛支持、前端項目復雜度逐漸提高,「利用worker線程緩解主線程計算壓力」逐漸成為一種可行方案。
比如,React就曾嘗試將運行時的diff算法放在worker線程執行。
然而,社區眾多的第三方庫都或多或少操作DOM,worker線程無法操作DOM的限制(也可以說是特性)使得其應用領域被大大限制。
partytown是一個大小僅6kb的庫,他的作用是讓worker線程擁有包括「操作DOM」在內的多項能力。
一旦潘多拉的盒子被打開,這會成為前端多線程編程的起點么?
第三方庫的壞處
我們經常在Github上搜索第三方庫,這些開源庫極大提高了我們的開發效率。然而第三方庫有很多潛在隱患:
- 第三方庫可能執行不為人知的操作(比如向未知服務器發送請求)
- 可能占用主線程過多算力
- 可能使用一些有害的API(比如document.write)
究其原因,對于前端應用,不管是通過<script>標簽插入,還是通過打包工具打包,最終在宿主環境(比如瀏覽器),第三方庫與你編寫的代碼都是以同樣的地位運行在主線程中的。
這就帶來性能、安全方面的潛在風險。
partytown的出現就是為了解決以上問題。
partytown的作用
當引入partytown后,書寫在如下script內的腳步會在worker線程中執行:
- <script type="text/partytown">
- // 第三方庫
- </script>
對于主線程的API,例如:window、document、localStorage,partytown通過proxy劫持并轉發對他們的調用。
比如,如下代碼:
- var w = document.body.clientWidth;
涉及到3個getter:
- get document
- get body
- get clientWidth
partytown會完成:
- 劫持getter,將他們轉發到主線程
- 獲取數據后返回
- 步驟1和2之間數據的序列化、反序列化
由于代理了主線程API,可以實現沙箱功能,比如:
- 限制對document.cookie訪問
- 返回定制的navigator.userAgent
- 禁止第三方庫訪問localStorage
- 重置危險方法(如document.write)
- 阻止腳步訪問其他腳本
對于網絡請求,web worker會發送同步的XHR請求,經由Service Worker攔截后與主線程異步通信。
數據返回后,Service Worker會響應web worker的請求。
所以,從worker線程角度看,一切調用都是同步的。這使得大部分原生API在worker線程與主線程中表現一致。
這意味著理論上任何第三方庫都可以經由partytown遷移到worker線程執行。
總結
當然,凡事都有取舍,對于partytown來說:
- worker線程的DOM操作都是阻塞的,不如主線程高效
- 經由Service Worker攔截的請求在Network面板相比普通請求略有差異
- 類似event.preventDefault()和passive event listeners在worker線程無效
但是,這終究是一次有意義的嘗試。相信在不遠的將來,會有越來越多前端應用從「多線程」中收益。