Wasm 為 Web 開發(fā)帶來無限可能
小廣告招聘: 螞蟻體驗(yàn)技術(shù)部招人啦!

不知道有沒有小伙伴關(guān)注今年的 Google 開發(fā)者大會(huì),今年的大會(huì)在 11.16 號(hào)開始。
Google 開發(fā)者大會(huì) (Google Developer Summit) 是 Google 面向開發(fā)者和科技愛好者展示最新產(chǎn)品和平臺(tái)的年度盛會(huì)。2021 年,Google 開發(fā)者大會(huì)以 “Develop as One” 為主題,攜手開發(fā)者與合作伙伴共創(chuàng)機(jī)遇,共謀發(fā)展!
今年在 Web 方面,有 Devtools、PWA、核心網(wǎng)頁指標(biāo)、CMS、隱私沙盒等等:

其中隱私沙盒的最新進(jìn)展我在前幾天的文章里已經(jīng)介紹過,沒看過的的小伙伴可以看這里:
三方 Cookie 替代品 — 隱私沙盒的最新進(jìn)展
今天,我們來看看另一個(gè)我比較感興趣的議題:WebAssembly。
本次大會(huì)上的分享人是來自 Google 的 WebAssembly 開發(fā)技術(shù)推廣工程師 Ingvar Stepanyan。
什么是 WebAssembly

WebAssembly 是一種二進(jìn)制指令格式,簡(jiǎn)稱為 Wsam,它可以運(yùn)行在適用于堆棧的虛擬機(jī)上。
WebAssembly 存在的意義就是成為編程語言的可移植編譯目標(biāo),讓在 Web 上部署客戶端和服務(wù)端應(yīng)用成為可能。
WebAssembly 可以為我們帶來什么
可移植性
如果你的網(wǎng)站現(xiàn)在想用一個(gè)能力,但是這個(gè)能力還沒有被任何的 JavaScript 庫實(shí)現(xiàn),但是在其他編程領(lǐng)域里已經(jīng)有了解決方案。
這時(shí),你就可以借助 WebAssembly 將所需要的庫編譯為可以在 Web 上運(yùn)行的二進(jìn)制格式,在某些情況下甚至你還可以編譯整個(gè)應(yīng)用。一旦編譯到 WebAssembly ,代碼就可以在任何裝有網(wǎng)絡(luò)瀏覽器的設(shè)備上運(yùn)行了,例如 PC、手機(jī)、平板電腦等等。
安全性
WebAssembly 需要在沙盒中運(yùn)行,在沙盒中,除了初始化時(shí)程序主動(dòng)提供給它的內(nèi)容,它無法訪問其他主機(jī)的內(nèi)存和函數(shù)。
這意味著, WebAssembly ,在你沒有給它下發(fā)命令的情況下,永遠(yuǎn)不會(huì)損壞你的主機(jī)進(jìn)程內(nèi)存,也無法隨意訪問文件系統(tǒng)或與其他設(shè)備通信。這就讓它與運(yùn)行在虛擬機(jī)和容器中的應(yīng)用有相同的優(yōu)勢(shì)
高效
與 JavaScript 等人類可讀的語言相比, WebAssembly 的字節(jié)碼可以用更少的字節(jié)表示相同的指令,并且在 WebAssembly 模塊依然處于下載期間就可以被編譯。
因?yàn)榫幾g器已經(jīng)事先完成了優(yōu)化工作,在 WebAssembly 中可以更輕松的獲取到可預(yù)測(cè)的性能
WebAssembly 的開源應(yīng)用
Squoosh

Squoosh 是一個(gè)超強(qiáng)的圖像壓縮Web應(yīng)用程序,可讓你深入研究各種圖像壓縮器提供的高級(jí)選項(xiàng),例如比較視覺差異和文件大小以及下載優(yōu)化后的圖片版本。
https://squoosh.app/
它借助 WebAssembly 納入了非常多的圖片編解碼器,這些編解碼器可能來源于 C、C++、Rust 等等,在瀏覽器的標(biāo)簽頁舊可以直接執(zhí)行它們,不需要服務(wù)端做任何額外的處理。這讓 Squoosh 可以處理很多舊的圖片格式(例如 JPEG、PNG),也可以處理很多新的圖片格式(例如 AVIF、JPEG-XL)。
FFMpeg
FFmpeg 是視頻處理最常用的開源軟件,它功能強(qiáng)大,用途廣泛,大量用于視頻網(wǎng)站和商業(yè)軟件(比如 Youtube 和 iTunes),也是許多音頻和視頻格式的標(biāo)準(zhǔn)編碼/解碼實(shí)現(xiàn)。

借助 WebAssembly 的能力,它現(xiàn)在有了一個(gè) Web 版本:FFMPEG.WASM,讓你可以在瀏覽器里處理視頻,你可以到下面這個(gè)網(wǎng)址上去體驗(yàn)一下:
https://ffmpegwasm.netlify.app/
MediaPipe

MediaPipe 是一款由 Google 開發(fā)并開源的數(shù)據(jù)流處理機(jī)器學(xué)習(xí)應(yīng)用開發(fā)框架。它是一個(gè)基于圖的數(shù)據(jù)處理管線,用于構(gòu)建使用了多種形式的數(shù)據(jù)源,如視頻、音頻、傳感器數(shù)據(jù)以及任何時(shí)間序列數(shù)據(jù)。
https://google.github.io/mediapipe/
它支持多個(gè)平臺(tái),融入了 WebAssembly 和 WebGL 的強(qiáng)大能力,可以通過 JavaScript 在 Web 上提供機(jī)器學(xué)習(xí)模型。
WebAssembly 用法
如果你現(xiàn)在有一個(gè)想要移植到 WebAssembly 的庫,該怎么用呢?

實(shí)際上, WebAssembly 的官網(wǎng) webassembly.org 是一個(gè)很好的開始,上面對(duì)于各種語言的教程都是比較全的,在這些教程里你可以學(xué)到怎么去用相應(yīng)的工具鏈,怎么向 WebAssembly 構(gòu)建代碼,以及如何利用到 Web 上,下面我們看幾個(gè)最常用的工具鏈。
Emscripten

Emscripten 是一個(gè)開源的編譯器,可以將 C/C++ 的代碼編譯成高度優(yōu)化的 JavaScript 并且高效運(yùn)行在現(xiàn)代瀏覽器上面,它推出的時(shí)間甚至比 WebAssembly 還要早。
現(xiàn)在,它可以將相同的 C/C++ 代碼編譯到 WebAssembly ,并提供各種各樣的工具和綁定關(guān)系幫助你將生成的代碼繼承到 Web 中。
例如,Emscripten 提供 SDL 實(shí)現(xiàn),可以用于在畫布上繪制內(nèi)容以及播放 Web 中的音頻,來轉(zhuǎn)換對(duì) WebGL 的調(diào)用。
SDL(簡(jiǎn)單直接媒體層)是一個(gè)跨平臺(tái)的開源開發(fā)庫,旨在提供對(duì)輸入和圖形硬件的低級(jí)訪問,用 C 語言編寫,視頻播放軟件、模擬器和許多流行游戲都使用它。
Embind
不同語言都擁有不同的類型和內(nèi)存表示法,JavaScript 和 C++ 也不例外,當(dāng)你編譯成 WebAssembly 也是一樣的情況,所以僅僅通過編譯是無法解決這個(gè)問題的。
想要使用這些庫中的結(jié)果,還需要一些中間層來轉(zhuǎn)換雙向傳遞的值。
在 Emscripten 中實(shí)現(xiàn)這點(diǎn)最簡(jiǎn)單的方法,是使用一個(gè)叫 Embind 的功能,下面是一個(gè)示例:
// 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 函數(shù)形式聲明對(duì)外開放的 API,以及轉(zhuǎn)換作為實(shí)參傳遞到 C++ 函數(shù)的值或者從 C++ 返回的值。這樣一來,你就可以將現(xiàn)有任何的 C++ 庫封裝到一個(gè)對(duì) JavaScript 友好的 API 中。
最后你可以同時(shí)編譯 API 封裝容器和之前構(gòu)建的依賴項(xiàng),并傳遞一個(gè) --bind 參數(shù)來啟用 Embind。
emcc --bind -o quick_example.js quick_example.cpp
如果將其編譯為 擴(kuò)展項(xiàng),它會(huì)生成一個(gè) ES6 兼容模塊,然后你就可以從 JavaScript 代碼導(dǎo)入它,異步初始化這個(gè)模塊。
import initModule from './mylib.mjs';
const Module = await initModule();
Module.lerp(1,2,3);
然后你就可以使用之前從 EMSCRIPTEN_BINDINGS 塊聲明的所有 API。
wasm-bindgen
如果你熟悉 Rust ,就知道它在 WebAssembly 領(lǐng)域的貢獻(xiàn)是非常大的。

Rust 提供了 wasm-bindgen 這個(gè)工具來支持為任何 Web API 生成綁定關(guān)系,以及將你自己的 Rust 函數(shù)導(dǎo)出為 JavaScript。
感興趣你可以看一下下面這個(gè)在線教程:https://rustwasm.github.io/。

教程中有將 Rust 函數(shù)導(dǎo)出為 JavaScript 的詳細(xì)指引,以及一些示例,和 Embind 一樣,它也負(fù)責(zé)在語言之間的雙向類型轉(zhuǎn)換,參考下面這段代碼:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
當(dāng)你在一個(gè) extern 塊上應(yīng)用 wasm_bindgen 屬性時(shí),就可以導(dǎo)入指定的 API,當(dāng)你在自己的類型和函數(shù)上應(yīng)用 wasm_bindgen 屬性時(shí),系統(tǒng)會(huì)導(dǎo)出相應(yīng)的類型和函數(shù)。
在每種情況下,工具鏈都負(fù)責(zé)在后臺(tái)為庫生成類型轉(zhuǎn)換, 以及 JavaScript 封裝容器,甚至是 TypeScript 定義,聲明 API 后,就可以編譯庫生成一個(gè) ES6 模塊。
const rust = import('./pkg');
rust
.then(m => m.greet('World!'))
.catch(console.error);
與 Emscripten 的示例類似,也需要異步將其初始化一次,然后就可以作為常規(guī)的 JavaScript 模塊進(jìn)行調(diào)用。
將 JS/TS 編譯成 WebAssembly
那么,JavaScript、TypeScript 能不能編譯成 WebAssembly 呢?
答案是否定的,因?yàn)?JavaScript 是高度動(dòng)態(tài)的語言,而 WebAssembly 屬于靜態(tài)類型語言,不過我們可以借助 AssemblyScript 來幫助我們模擬實(shí)現(xiàn)這一點(diǎn)。

AssemblyScript 是一個(gè) TypeScript 到 WebAssembly 的編譯器,你可以到 https://www.assemblyscript.org/ 去了解它的詳細(xì)用法。
未來
WebAssembly 現(xiàn)在已經(jīng)處于穩(wěn)定階段了,幾年前就被所有主流瀏覽器所支持,但是它仍在不斷發(fā)展,探索新的能力。

在這些探索中,有一些改進(jìn)了與 JavaScript 和 Web 的集成,有一些縮減了代碼體積、,還有一些進(jìn)一步提升了性能,想了解更多,可以到下面的網(wǎng)址進(jìn)行查看:
https://webassembly.org/roadmap/
現(xiàn)代瀏覽器的功能早已不局限在簡(jiǎn)單的頁面呈現(xiàn),這就是為什么
WebAssembly會(huì)誕生的重要原因之一。為了將沉重的任務(wù)性能提升到一個(gè)新的水平,在JavaScript和機(jī)器代碼之間搭建了一座橋梁,由此才有了WebAssembly。
讓我們期待 WebAssembly 可以在 Web 上帶給我們更多的可能性吧~
文中如有錯(cuò)誤,歡迎在后臺(tái)和我留言,如果這篇文章幫助到了你,歡迎點(diǎn)贊、在看和關(guān)注。你的點(diǎn)贊、在看和關(guān)注是對(duì)我最大的支持!
創(chuàng)作不易,你的每一個(gè)點(diǎn)贊、在看、分享都是對(duì)我最大的支持!??
