WebAssembly初探

WebAssembly初探

WebAssembly?

自從互聯網誕生以來,到今天,它已經成爲了世界上最大的社區。隨着互聯網發展起來的還有各種腳本語言,其中JavaScript在幾輪鬥爭中存活下來成爲今天當之無愧的「霸主」。全球最大的包管理npm中數億的包天天被無數人引用,撐起了互聯網的半壁江山。html

隨着互聯網的發展,本來簡單的應用變得逐漸臃腫。這個時候,JavaScript顯得比較力不從心了。帶寬的增大和網絡費用的下調,網絡應用彷佛愈來愈成爲現實。從以往的經驗來看,帶寬已經基本知足網絡應用所需,可是更大的問題是:什麼樣的應用能夠運行在互聯網上呢?前端

幾乎是共識的一件事兒是:腳本語言的性能與編譯型語言的是存在差距的,這一點兒在js上體現得比較明顯。Google雖然引入了V8引擎,讓js有點兒靜態語言的意思,性能上也提高了很多,可是和標準靜態語言相比相差甚遠。node

爲了應對高速發展的互聯網,解決傳統腳本語言性能不足的缺點,互聯網標準化組織在接受提議並通過討論之後,發佈了WebAssembly標準。WebAssembly很大程度上提高了性能,力圖解決js性能不足以應對大量計算應用的缺點。react

解決性能問題的方案有不少,例如asm.js也是個優秀的解決方案。webpack

WebAssembly提出已經至關長一段時間了,也已經有了許多成熟的項目。其中我最爲震撼的應該是DOOM3,它的成功移植也讓人看到WebAssembly的巨大潛力。git

WebAssembluy須要瀏覽器提供支持,你能夠在這裏查看各個瀏覽器的兼容性。使人振奮的是,除了IE之外,幾乎全部瀏覽器都開始陸陸續續添加對WebAssembly的支持!程序員

並且,沒法預料的是,就目前看來,WebAssembly有可能替代js成爲開發首選。頗有可能在不久的未來,WebAssembly將成爲js的替代者,這不是危言聳聽,Web IDL草案已經開始陸續徵集意見,這個草案的完成度極高。Web IDL可以提供瀏覽器的API接口給其餘語言,這意味着操縱DOM再也不是js專屬,只要符合標準,均可以調用!並且,調用這些API的速度會比JS更快!github

因此這車,得上!穩!web

這個系列我會寫幾篇,把我對其的瞭解逐一經過文章的形式分享出來。注意,這篇文章只是初探,不會過度涉及代碼和原理,請不要疑惑爲何這麼簡單。chrome

WebAssembly暫時沒法提供polyfill支持。

怎麼coding?

WebAssembly不是一門語言,是一個標準集。通過解析後,它看起來和彙編很像,能夠將它看做瀏覽器上運行的「彙編」。直接書寫不太現實,更爲常規的作法是將靜態語言編譯成WebAssembly,就像編譯成連接文件同樣。

編譯就須要編譯器。得益於emscripten項目,目前已經有多種語言支持編譯成爲WebAssembly。理論上,只要符合必定標準,均可以使用它編譯編譯成WebAssembly

本教程中,咱們會選擇一門比較前沿的語言:Rust

Rust的理念很先進,絕大部分錯誤均可以經過編譯器檢測出來,沒有GC,這意味着它不須要runtime。同時Rust的抽象是零開銷的,而且代碼可優化程度很高。在經過LLVM編譯優化之後,Rust的性能能夠直逼C/C++的運行速度,這也使得它進入T1梯隊。有得有失,Rust爲了實現這些,加了不少條條框框,使得書寫起來不是那麼簡單。但我認爲,Rust應該成爲每一個有追求的程序員應該學習的語言。

工具準備

rust的安裝比較傻瓜化,只須要運行一段命令,接着按照提示弄好環境變量就好了.

安裝方法(*unix):

curl https://sh.rustup.rs -sSf | sh
複製代碼

上面方法是用於安裝rustup的。運行完上面的腳本以後,一般須要把~/.cargo/bin加入$PATH裏面的。運行下面的命令:

echo PATH="$PATH:\$HOME/.cargo/bin" >> you_profile && source your_profile && rustc --version
複製代碼

your profile根據你的shell環境不一樣而不一樣,一般大多數人使用的是bash的.bash_profile。我是使用的zsh,所以是.zshrc

值得注意的是,rust分爲多個版本,對於支持WebAssembly的一些特性而言,須要nightly版本支持,所以通常狀況下,咱們都是在使用nightly。使用下面的命令切換默認配置爲nightly

rustup default nightly
複製代碼

接着咱們須要可以將Rust代碼編譯成WebAssembly的工具。這裏推薦wasm-pack,它幾乎是如今最佳的WebAssembly的編譯器,上手幾乎沒有難度。並且它爲了和npm生態聯動,使用起來和一些庫很類似,尤爲是webpack。它會自動將Rust編譯,而且產生js代碼,這個js代碼是對wasm調用的封裝,這樣對開發者而言,使用起來就像一個普通的js包同樣。另外它還產生了ts的定義文件,方便IDE代碼提示。

因爲是初探,所以不要把東西弄複雜了。咱們先看看官方的模板吧!

首先,咱們下載一個cargo-generate。顧名思義,用於根據模板生成項目的工具,相似於create-react-app

接着,前端開發必備nodejs全家桶。

最後,瀏覽器,請使用最新版firefox或者chrome

初探

咱們首先下載官方提供的例子**[1]**:

cargo generate --git https://github.com/rustwasm/wasm-pack-template
複製代碼

接着你會獲得一個工程,它的目錄看起來是這樣的:

├── Cargo.toml
├── LICENSE_APACHE
├── LICENSE_MIT
├── README.md
├── src
│   ├── lib.rs
│   └── utils.rs
└── tests
    └── web.rs
複製代碼

這是一個標準的rust工程,咱們得稍加改造,使它能夠在瀏覽器運行。

npm init**[2]**運行一下,使該工程一樣也成爲一個nodejs工程。如今,目錄看起來是:

├── Cargo.toml
├── LICENSE_APACHE
├── LICENSE_MIT
├── README.md
├── package.json
├── src
│   ├── lib.rs
│   └── utils.rs
└── tests
    └── web.rs
複製代碼

接着,添加js依賴。咱們須要webpack。運行如下命令**[3]**:

npm install webpack webpack-cli webpack-dev-server --save-dev
複製代碼

而後添加配置文件**[4]**,webpack.config.js,並寫入如下內容:

const path = require('path');

module.exports = {
    entry: "./bootstrap.js",
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bootstrap.js",
    },
    mode: "development",
};
複製代碼

咱們把bootstrap.js做爲入口文件,所以在項目跟路徑下建立它**[5]**,並在裏面寫入:

import('./pkg/webassembly')
    .then(wasm => {
				wasm.greet();
    })
    .catch(e => console.error(e));
複製代碼

注意,wasm模塊目前只能經過異步調用!所以咱們須要bootstrap.js文件來異步引入它,不能直接import導入。

'./pkg/webassembly'如今不存在,那麼怎麼獲得呢?wasm-pack派上用場了,直接build**[6]**便可,他會產生的!

wasm-pack build --out-name webassembly
複製代碼

該過程可能會很慢甚至失敗,這是因爲衆所周知的緣由。請搜索中科大源,將rustup和rust的源都替換爲中科大提供的鏡像源。運行成功之後,項目裏面會出現pkg目錄,裏面有該wasm項目的組成文件,咱們一般不會去關注他們。咱們只須要調用那個js文件就好了。

而後,建立index.html文件,並引用bootstrap.js文件**[6]**:

<html>
  <head>
    <script src="./bootstrap.js"></script>
  </head>
</html>
複製代碼

接着,咱們直接啓動webpack服務**[7]**,看看效果:

npx webpack-dev-server
複製代碼

若是一切順利,那麼你打開localhost:8080的時候,應該會出現一個Hello, XXX的彈窗。這意味着,你邁出了WebAssembly的第一步。如今,讓咱們揭開這個項目的神祕面紗。

代碼

關於js的部分,我默認你已經很熟悉js了,所以再也不解釋。須要值得注意的是,看起來咱們只是在bootstrap.js裏面只是調用了import('./pkg/webassembly')來引入了wasm代碼,但實際上真的這麼簡單嗎?

固然不是。之因此咱們可以這麼輕鬆地使用,得歸功於wasm-packwebpackwasm-pack爲你生成了wasm代碼的同時,也生成了相應的封裝好的jsd.ts文件,這些js文件裏爲了自動導出了你在原生rust代碼裏面但願導出給js的一些方法等。

若是你去閱讀其中的js代碼,你會發如今裏面也只是簡單的使用import wasm from './webassembly.wasm'來導入WebAssembly代碼。但真的是這樣簡單嗎?wasm代碼可以像普通js庫同樣導入嗎?答案是否認的,熟悉js的你或許已經有答案了,那就是webpackwebpackimport語句轉換成了對應的WebAssembly的API。這些API之後可能會出文介紹。若是你感興趣,MDN上關於WebAssembly的主題或許能夠給你提供幫助。

跳過js,咱們來看看rust代碼部分。這部分比較困難,由於它涉及的部分比較多。這裏不會去幫你解讀它的具體原理,也不會教你學Rust

Rust教程十分的棒!

src目錄下兩個文件lib.rsutils.rs,後者沒什麼用,至少如今是這樣,由於根本沒調用。實際上它是用來調試的wasm代碼的,由於在wasm的運行環境下,常規的調試條件有點兒難用。

首先是tests目錄,顧名思義,是用於測試的,rust自帶測試功能。一樣,這個目錄並無用,裏面也沒有測試代碼。忽略它是目前最好的作法。

在代碼裏面,咱們使用了一個叫作wasm_bindgencrate(rust把庫/包叫作crate)。這個庫是頗有魔力的,它讓WebAssembly變得更加容易,如同代碼中:

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

#[wasm_bindgen]
pub fn greet() {
    alert("Hello, webassemblu!");
}
複製代碼

它把js環境中的alert綁定到了rust環境中,同時把greet函數導出到js環境中,一切看起來如此簡單。導出倒還好,導入就顯得比較麻煩了。每次都得像書寫頭文件定義同樣,這樣也太煩人了。難道沒有人把全部的函數都定義好嗎?那固然有了,出自quote做者的另兩個crateweb_sysjs_sys,他們分別提供了web環境和js環境的IDL綁定

emmmmn,事情彷佛變得困難了起來,這都是些什麼啊?實際上,這些東西是不須要咱們關心的,咱們只須要學會怎麼去用就好了。至於怎麼實現的,這個真的有點兒複雜了。不過若是感興趣,能夠去看看源代碼。

本文到此結束,或許你會問,這啥啊,什麼都沒講清楚。你得明白,WebAssembly這個標準十分龐大,涉及的點兒也十分多。其次,它須要一門靜態語言的支持,選擇rust意味着這個難度變得更高。若是妄想一篇文章講清楚,有點兒癡人說夢了。因此正如前文所說,這只是初探,進一步的解析,後面的文章會陸續解釋的。

相關文章
相關標籤/搜索