你知道WebAssembly嗎?
為什么要說WebAssembly呢?
其實對于我而言,之前有了解過 WebAssembly,知道他很強大,但是沒有實際的使用過。偶然間在調研和使用 ffmpeg 的過程中,看到了很多瀏覽器端的方案,都是使用了 WebAssembly,原來已經有很多實際的應用真的在使用它,那么它是不是 web 的未來呢?這篇文章主要就是帶大家走進 WebAssembly,網上有很多類似的文章,但對于大多數的前端同學來說可能有難以理解的地方,所以本次文章盡量用通俗易懂的方式帶大家了解 WebAssembly。
WebAssembly 是什么?
「官方的解釋」:WebAssembly/wasm WebAssembly 或者 wasm 是一個可移植、體積小、加載快并且兼容 Web 的全新格式,是由主流瀏覽器廠商組成的 W3C 社區團體制定的一個新的規范。
「我們來通俗的解釋」:WebAssembly 能夠把非 JavaScript 代碼運行在瀏覽器中,這些代碼可以是C、C++、Rust等等。
WebAssembly 如何與 Web兼容的?
WebAssembly 提供了一種在網絡平臺以接近本地速度的方式運行多種語言編寫的代碼的方式。
WebAssembly原理
Web 平臺可以看成有兩個部分:
VM,用于運行 Web 應用代碼,例如 JS 引擎運行 JS 代碼??
Web API,例如 DOM、CSSOM、WebGL、IndexedDB、Web Audio API?
在以前,VM 只能加載 JS 運行,JS 可能足夠滿足我們的需求,但如今JS會有各種各樣的原生性能的領域,比如3D 游戲、VR/AR、計算機視覺、圖片/視頻編輯等,并且,下載和解析體積比較大的JS是很困難的。
隨著 WebAssembly 的出現,上述提到的 VM 現在可以加載兩種類型的代碼執行:JavaScript 和 WebAssembly。
雖然同樣運行在瀏覽器中,但是 WebAssembly 不是用來替代 Javascript 的,他們其實是相輔相成的。WebAssembly 會被編譯進你的瀏覽器,在你的 CPU 上以接近原生的速度運行。你可以直接在 JavaScript 中將它們當作模塊來用。也就是說,你可以通過 WebAssembly來充分利用編譯代碼的性能,同時保持 JavaScript 的靈活性。WebAssembly 其實是一種中間格式。
JS 是高層次的語言,靈活且極具表現力,動態類型、不需要編譯步驟,并且有強大的生態,非常易于編寫 Web 應用。
WebAssembly 是一種低層次、類匯編的語言,使用一種緊湊的二級制格式,能夠以近乎原生的性能運行,并提供了低層次的內存模型,是 C++、Rust 等語言的編譯目標,使得這類語言編寫的代碼能夠在 Web 上運行。
WebAssembly 的特性?
- 「快速、高效、可移植」—— 通過利用常見的硬件能力,WebAssembly 代碼在不同平臺上能夠以接近本地速度運行。
- 「可讀、可調試」—— WebAssembly 是一門低階語言,但是它確實有一種人類可讀的文本格式(其標準即將得到最終版本),這允許通過手工來寫代碼,看代碼以及調試代碼。
- 「保持安全」—— WebAssembly 被限制運行在一個安全的沙箱執行環境中。像其他網絡代碼一樣,它遵循瀏覽器的同源策略和授權策略。
- 「不破壞網絡」—— WebAssembly 的設計原則是與其他網絡技術和諧共處并保持向后兼容。
WebAssembly 和 JavaScript?
我們先來看下 WebAssembly 在瀏覽器中的位置:
WebAssembly在瀏覽器中的位置
- WebAssembly 和 JavaScript 在同一個層次執行,也就是JS Engine
- 和 JavaScript 一樣,能夠操作 WebAPI
根據上圖,我們先來看下 JavaScript 在 Web 中所做的工作:
每個圖形大概的表示每個階段消耗的時間,JS 在 Web 中主要經過了這些過程
- 解析(parse)、編譯+優化(compile + optimize)、重新優化(re-optimize)、執行(execute)、垃圾回收(garbage collection)
我們再來看下 WebAssembly 在 Web 中所做的工作:
- 解碼(parse)、編譯+優化(compile + optimize)、執行(execute)
因為 Wasm 的特性和它特殊的格式,在很多情況下,Wasm 比 Javascript 要更快
- 獲取 Wasm 花費的時間更少,因為它比 JavaScript 更緊湊,特有的二進制格式有效地減小了包體積,進一步提升了瀏覽器的加載速度。
- 解碼 Wasm 花費的時間更少。
- 編譯和優化,因為 Wasm 更接近機器代碼。
- 不需要重新優化,因為 Wasm 內置了類型和其他信息,因此 JS 引擎不需要去推測它。
- 執行通常需要更少的時間,因為 Wasm 的指令集更適合機器。
- 由于內存是手動管理的,因此不需要垃圾收集。
WebAssembly 的使用?
「WebAssembly 編寫和使用過程:」
- 各種語言當前有分別對應的不同編譯工具,能夠將代碼編譯為 wasm 格式的文件,如果所對應的需求所使用的 wasm 是比較常見的庫,比如 ffmpeg,那么可以在 github 上找到很多已經編譯好的 wasm 文件。
- 有文件后,可以通過 fetch 或者 等方式獲取 wasm 文件內容,并得到一份 ArrayBuffer。
- 將得到的 ArrayBuffer 編譯為瀏覽器可執行的模塊,并實例化
- 到這里就可以調用從 Wasm 模塊內導出的方法了
下面我們具體看下每一步中的實際例子:
- 首先需要配置編譯 WASM 所需要的環境,你首先需要先編譯 LLVM,這是運行后續工具的先決條件。
- Git。
- CMake
- 系統編譯工具。Linux上,安裝 GCC。OS X 上,安裝 Xcode。Windows 上安裝 Visual Studio 2015 Community with Update 3 或更新版本。
- Python 2.7.x,在 Linux 和 OS X上,很可能已經裝好了。看這里。
- 接下來,您需要通過源碼自己編譯一個 Emscripten。運行下列命令來自動化地使用 Emscripten SDK。(因為下面用c舉例,所以使用 c 語言的 Wasm 編譯工具 Emscripten)。
安裝程序會設置所有 Emscripten 運行所需要的環境變量。
- 編寫C代碼(這里用C舉例)
- 轉到一個已經配置過 Emscripten 編譯環境的終端窗口中,進入剛剛保存 hello.c 文件的文件夾中,然后運行下列命令:
- 經過上面步驟就可以得到 .wasm 文件,下面我們使用 fetch 來加載和運行 wasm,我們先來看一下代碼。
- 首先我們使用 fetch 函數來獲取了 wasm 文件內容,函數返回了 response 對象
- 我們使用 arrayBuffer 函數把 response 轉換為帶類型數組
- 最后使用 WebAssembly.instantiate 函數來進一步編譯和實例化數組。
WebAssembly 對象是原生提供的包含了所有 WebAssembly 相關功能的命名空間 其中 WebAssembly.Instance 對象是一個有狀態的、可執行的模塊的實例。實例對象包含所有的能夠從 JavaScript 調用到 WebAssembly 代碼的導出的 WebAssembly 函數。MDN WebAssembly API參考
- 我們可以使用上面的函數來運行我們自己的 wasm 文件。
總結一下上面的內容,其實就是兩種語言之間以 wasm 為橋梁進行了一次通信:
上面的整個步驟就是我們從環境搭建到最后使用 wasm 文件的流程,當前你所使用的語言可能不同,所使用的編譯工具自然也不同。并且以上只是最簡單的使用方式,如果考慮到性能、優化等問題,還有很深的內容值得探討。
WebAssembly 的應用?
figma - 基于瀏覽器的多人實時協作 UI 設計工具
- https://www.figma.com/
Google Earth — 支持各大瀏覽器的 3D 地圖,而且運行流暢
- https://earth.google.com/web/?
WebAssembly 的兼容性?
可以看到的是,很多現代瀏覽器對于 WebAssembly 的支持越來越好,未來的趨勢對于 Webassembly 來說是向好的
總結?
當前 WebAssembly 大多數被用在對原生能力有很高要求的地方,或者是將一些應用程序移植到 Web,但是 WebAssembly 并不僅僅局限在瀏覽器,它還被應用在使代碼跨平臺、跨設備工作。
WebAssembly 到底是不是 web 的未來?現在來看的話,它還在發展階段,首先要解決的問題其實就是各瀏覽器的兼容性,其次就是性能問題。我認為它并不是用來取代 JS 的,但是極有可能演變為 ES6 之后 Web 的新拐點。
參考文獻?
WASM中文網
- https://www.wasm.com.cn/
MDN WebAssembly概念
- https://developer.mozilla.org/zh-CN/docs/WebAssembly/Concepts
W3C Web 中文興趣組 · WebAssembly 線上研討會2020年8月29日
- https://www.w3.org/2020/08/29-chinese-web-wasm.minutes.html?
記一次完整 C++ 項目編譯成 WebAssembly 的實踐
- https://developer.aliyun.com/article/740902