Wasm 爲 Web 開發帶來無限可能

大家好,我是 ConardLi

Google 開發者大會 (Google Developer Summit) 是 Google 面向開發者和科技愛好者展示最新產品和平臺的年度盛會。2021 年,Google 開發者大會以 “Develop as One” 爲主題,攜手開發者與合作伙伴共創機遇,共謀發展!

今年在 Web 方面,有 DevtoolsPWA、核心網頁指標、CMS、隱私沙盒等等:

其中隱私沙盒的最新進展我在前幾天的文章裏已經介紹過,沒看過的的小夥伴可以看這裏:

三方 Cookie 替代品 — 隱私沙盒的最新進展

今天,我們來看看另一個我比較感興趣的議題:WebAssembly

本次大會上的分享人是來自 Google 的 WebAssembly 開發技術推廣工程師 Ingvar Stepanyan

什麼是 WebAssembly

WebAssembly 是一種二進制指令格式,簡稱爲 Wsam,它可以運行在適用於堆棧的虛擬機上。

WebAssembly 存在的意義就是成爲編程語言的可移植編譯目標,讓在 Web 上部署客戶端和服務端應用成爲可能。

WebAssembly 可以爲我們帶來什麼

可移植性

如果你的網站現在想用一個能力,但是這個能力還沒有被任何的 JavaScript 庫實現,但是在其他編程領域裏已經有了解決方案。

這時,你就可以藉助 WebAssembly 將所需要的庫編譯爲可以在 Web 上運行的二進制格式,在某些情況下甚至你還可以編譯整個應用。一旦編譯到 WebAssembly ,代碼就可以在任何裝有網絡瀏覽器的設備上運行了,例如 PC、手機、平板電腦等等。

安全性

WebAssembly  需要在沙盒中運行,在沙盒中,除了初始化時程序主動提供給它的內容,它無法訪問其他主機的內存和函數。

這意味着, WebAssembly ,在你沒有給它下發命令的情況下,永遠不會損壞你的主機進程內存,也無法隨意訪問文件系統或與其他設備通信。這就讓它與運行在虛擬機和容器中的應用有相同的優勢

高效

與 JavaScript 等人類可讀的語言相比, WebAssembly 的字節碼可以用更少的字節表示相同的指令,並且在  WebAssembly  模塊依然處於下載期間就可以被編譯。

因爲編譯器已經事先完成了優化工作,在 WebAssembly 中可以更輕鬆的獲取到可預測的性能

WebAssembly 的開源應用

Squoosh

Squoosh 是一個超強的圖像壓縮 Web 應用程序,可讓你深入研究各種圖像壓縮器提供的高級選項,例如比較視覺差異和文件大小以及下載優化後的圖片版本。

https://squoosh.app/

它藉助 WebAssembly 納入了非常多的圖片編解碼器,這些編解碼器可能來源於 C、C++、Rust 等等,在瀏覽器的標籤頁舊可以直接執行它們,不需要服務端做任何額外的處理。這讓 Squoosh 可以處理很多舊的圖片格式(例如 JPEG、PNG),也可以處理很多新的圖片格式(例如 AVIF、JPEG-XL)。

FFMpeg

FFmpeg 是視頻處理最常用的開源軟件,它功能強大,用途廣泛,大量用於視頻網站和商業軟件(比如 Youtube 和 iTunes),也是許多音頻和視頻格式的標準編碼 / 解碼實現。

藉助  WebAssembly 的能力,它現在有了一個 Web 版本:FFMPEG.WASM,讓你可以在瀏覽器裏處理視頻,你可以到下面這個網址上去體驗一下:

https://ffmpegwasm.netlify.app/

MediaPipe

MediaPipe 是一款由 Google 開發並開源的數據流處理機器學習應用開發框架。它是一個基於圖的數據處理管線,用於構建使用了多種形式的數據源,如視頻、音頻、傳感器數據以及任何時間序列數據。

https://google.github.io/mediapipe/

它支持多個平臺,融入了  WebAssembly 和 WebGL 的強大能力,可以通過 JavaScript 在 Web 上提供機器學習模型。

WebAssembly 用法

如果你現在有一個想要移植到 WebAssembly 的庫,該怎麼用呢?

實際上, WebAssembly 的官網 webassembly.org 是一個很好的開始,上面對於各種語言的教程都是比較全的,在這些教程裏你可以學到怎麼去用相應的工具鏈,怎麼向 WebAssembly 構建代碼,以及如何利用到 Web 上,下面我們看幾個最常用的工具鏈。

Emscripten

Emscripten 是一個開源的編譯器,可以將 C/C++ 的代碼編譯成高度優化的 JavaScript 並且高效運行在現代瀏覽器上面,它推出的時間甚至比 WebAssembly 還要早。

現在,它可以將相同的 C/C++ 代碼編譯到 WebAssembly ,並提供各種各樣的工具和綁定關係幫助你將生成的代碼繼承到 Web 中。

例如,Emscripten 提供 SDL 實現,可以用於在畫布上繪製內容以及播放 Web 中的音頻,來轉換對 WebGL 的調用。

SDL(簡單直接媒體層)是一個跨平臺的開源開發庫,旨在提供對輸入和圖形硬件的低級訪問,用 C 語言編寫,視頻播放軟件、模擬器和許多流行遊戲都使用它。

Embind

不同語言都擁有不同的類型和內存表示法,JavaScript 和 C++ 也不例外,當你編譯成  WebAssembly 也是一樣的情況,所以僅僅通過編譯是無法解決這個問題的。

想要使用這些庫中的結果,還需要一些中間層來轉換雙向傳遞的值。

在 Emscripten 中實現這點最簡單的方法,是使用一個叫 Embind 的功能,下面是一個示例:

// quick_example.cpp
#include <emscripten/bind.h>

using namespace emscripten;

float lerp(float a, float b, float t) {
    return (1 - t) * a + t * b;
}

EMSCRIPTEN_BINDINGS(my_module) {
    function("lerp"&lerp);
}

通過 EMSCRIPTEN_BINDINGS 塊,就可以以 JavaScript 函數形式聲明對外開放的 API,以及轉換作爲實參傳遞到 C++ 函數的值或者從 C++ 返回的值。這樣一來,你就可以將現有任何的 C++ 庫封裝到一個對 JavaScript 友好的 API 中。

最後你可以同時編譯 API 封裝容器和之前構建的依賴項,並傳遞一個 --bind 參數來啓用  Embind

emcc --bind -o quick_example.js quick_example.cpp

如果將其編譯爲 擴展項,它會生成一個 ES6 兼容模塊,然後你就可以從 JavaScript 代碼導入它,異步初始化這個模塊。

import initModule from './mylib.mjs';

const Module = await initModule();
Module.lerp(1,2,3);

然後你就可以使用之前從 EMSCRIPTEN_BINDINGS 塊聲明的所有 API。

wasm-bindgen

如果你熟悉 Rust ,就知道它在 WebAssembly 領域的貢獻是非常大的。

Rust 提供了 wasm-bindgen 這個工具來支持爲任何 Web API 生成綁定關係,以及將你自己的 Rust 函數導出爲 JavaScript

感興趣你可以看一下下面這個在線教程:https://rustwasm.github.io/

教程中有將 Rust 函數導出爲 JavaScript 的詳細指引,以及一些示例,和 Embind  一樣,它也負責在語言之間的雙向類型轉換,參考下面這段代碼:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}

當你在一個 extern 塊上應用 wasm_bindgen 屬性時,就可以導入指定的 API,當你在自己的類型和函數上應用 wasm_bindgen 屬性時,系統會導出相應的類型和函數。

在每種情況下,工具鏈都負責在後臺爲庫生成類型轉換, 以及 JavaScript 封裝容器,甚至是 TypeScript 定義,聲明 API 後,就可以編譯庫生成一個 ES6 模塊。

const rust = import('./pkg');

rust
  .then(m => m.greet('World!'))
  .catch(console.error);

與 Emscripten 的示例類似,也需要異步將其初始化一次,然後就可以作爲常規的 JavaScript 模塊進行調用。

將 JS/TS 編譯成 WebAssembly

那麼,JavaScript、TypeScript 能不能編譯成 WebAssembly 呢?

答案是否定的,因爲 JavaScript 是高度動態的語言,而  WebAssembly 屬於靜態類型語言,不過我們可以藉助 AssemblyScript 來幫助我們模擬實現這一點。

AssemblyScript 是一個 TypeScript 到 WebAssembly 的編譯器,你可以到 https://www.assemblyscript.org/ 去了解它的詳細用法。

未來

WebAssembly 現在已經處於穩定階段了,幾年前就被所有主流瀏覽器所支持,但是它仍在不斷髮展,探索新的能力。

在這些探索中,有一些改進了與 JavaScript 和 Web 的集成,有一些縮減了代碼體積、,還有一些進一步提升了性能,想了解更多,可以到下面的網址進行查看:

https://webassembly.org/roadmap/

現代瀏覽器的功能早已不侷限在簡單的頁面呈現,這就是爲什麼 WebAssembly 會誕生的重要原因之一。爲了將沉重的任務性能提升到一個新的水平,在 JavaScript 和機器代碼之間搭建了一座橋樑,由此纔有了 WebAssembly 。

讓我們期待 WebAssembly 可以在 Web 上帶給我們更多的可能性吧~

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/50zQO11cheyVkrVueqXgsA