WebAssembly作為一種新興的Web技術(shù),,相關(guān)的資料和社區(qū)還不夠豐富,但其為web開發(fā)提供了一種嶄新的思路和工作方式,,未來是很有可能大放光彩的,。
使用WebAssembly,我們可以在瀏覽器中運行一些高性能,、低級別的編程語言,,可用它將大型的C和C++代碼庫比如游戲、物理引擎甚至是桌面應(yīng)用程序?qū)隬eb平臺,。
截至目前為止,,我們已經(jīng)可以在Chrome、Firefox中使用WebAssembly,,Edge和Safari對它的支持也基本完成,。這意味著很快,,就能在所有流行的瀏覽器中運行wasm了。
在這篇文章中,,我們將會演示如何將簡單的C代碼編譯為wasm,,并將其包含在網(wǎng)頁中。在此之前,,我們先來直觀的了解下WebAssembly是如何工作的,。
WebAssembly是如何工作的?
這里不涉及過多技術(shù)性的問題,。我們知道,,在今天的瀏覽器中,JavaScript是在虛擬機(VM)中執(zhí)行的,,該虛擬機能夠最大化地優(yōu)化代碼并壓榨每一絲的性能,,這也使得JavaScript稱為速度最快的動態(tài)語言之一。但盡管如此,,它還是無法與原生的C/C++代碼相媲美,。所以,WebAssembly就出現(xiàn)了,。
Wasm同樣在JavaScript虛擬機中運行,,但是它表現(xiàn)得更好,。兩者可以自由交互,、互不排斥,這樣你就同時擁有了兩者最大的優(yōu)勢——JavaScript巨大的生態(tài)系統(tǒng)和有好的語法,,WebAssembly接近原生的表現(xiàn)性能,。
大多數(shù)程序員會選擇使用C語言來編寫WebAssembly模塊,并將其編譯成.wasm文件,。這些.wasm文件并不能直接被瀏覽器識別,,所以它們需要一種稱為JavaScript膠接代碼(glue code,用于連接相互不兼容的軟件組件,,詳見:http://whatis./definition/glue-code)的東西來加載,。
隨著未來WebAssembly框架和本地wasm模塊支持的發(fā)展,這一過程可能會有所縮短,。
開發(fā)前準備
編寫WebAssembly需要不少的工具,,但作為一個程序員,下面的工具你應(yīng)該大部分都已經(jīng)有了,。
1,、支持WebAssembly的瀏覽器,新版的Chrome或者Firefox均可(可以在此查看各個瀏覽器對某項內(nèi)容的支持情況:http:///#feat=wasm),。
2,、C到WebAssembly的編譯器,,推薦使用Emscripten(https://kripken./emscripten-site/docs/getting_started/downloads.html),安裝這個工具費時費力費空間,,但沒辦法,,這是目前為止最好的選擇,請仔細閱讀安裝說明,,需占用約1GB的硬盤空間,。
3、一個C編譯器/開發(fā)環(huán)境,,比如Linux下的GCC,,OS X下的Xcode,Windows下的Visual Studio,。
4,、一個簡單的本地web服務(wù)器,Linux/OS X下使用python -m SimpleHTTPServer 9000 命令即可,,Windows下可安裝IIS服務(wù),。
一、編寫C代碼
下面我們編寫一個非常簡單的C語言例子,,它將會返回1-6的隨機數(shù),,在你所使用的工作目錄下,創(chuàng)建一個dice-roll.c文件,。
#include <emscripten/emscripten.h> // 一旦WASM模塊被加載,,main()中的代碼就會執(zhí)行 int main(int argc, char ** argv) { printf("WebAssembly module loaded\n"); int EMSCRIPTEN_KEEPALIVE roll_dice() {
當我們將其編譯為wasm并且在瀏覽器中加載時,main 函數(shù)會自動執(zhí)行,,其中的printf 將會被翻譯成console.log ,。
我們想要roll_dice 函數(shù)能夠在JavaScript中隨時調(diào)用,為此,,我們需要在函數(shù)名前添加EMSCRIPTEN_KEEPALIVE 標記以告訴Emscripten我們的意圖,。
二、將C編譯為WebAssembly
現(xiàn)在我們已經(jīng)有了C代碼,,接下來需要將它編譯成wasm,,不僅如此,我們還需要生成相應(yīng)的JavaScript膠接代碼以便能夠真正運行起來,。
這里我們必須使用Emscripten編譯器,,你會發(fā)現(xiàn)有大量的命令行參數(shù)和編譯方法可選,經(jīng)過實踐,,我們找到了下面這個最友好最實用的組合:
emcc dice-roll.c -s WASM=1 -O3 -o index.js
各個參數(shù)含義如下:
- emcc——代表Emscripten編譯器,;
- dice-roll.c——包含C代碼的文件;
- -s WASM=1——指定使用WebAssembly,;
- -O3——代碼優(yōu)化級別,,3已經(jīng)是很高的級別了,;
- -o index.js——指定生成包含wasm模塊所需的全部膠接代碼的JS文件;
需要注意的是,,盡管上面的emcc選項能夠很好地應(yīng)對我們這個例子,,但在更復雜的情況下,好需要使用不同的方法,,可查看官方文檔了解更多內(nèi)容:http://kripken./emscripten-site/docs/tools_reference/emcc.html#emccdoc,。
三、在瀏覽器中加載WebAssembly代碼
現(xiàn)在我們將回到熟悉的web開發(fā)領(lǐng)域,,在當前文件夾創(chuàng)建index.html文件,,引入相關(guān)的js文件與CSS文件。
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>WebAssembly 示例</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="CSS/styles.css"> <link rel="stylesheet" href="CSS/dice-1.0.min.css"> <div class="dice dice-6"></div> <!-- 引入JavaScript膠節(jié)文件 --> <!-- 這將會加載WebAssembly模塊并運行其main函數(shù) --> <script src="index.js"></script>
至此,,項目結(jié)構(gòu)已經(jīng)完整,,如下:
style.css簡單設(shè)置一下頁面樣式:
font: normal 16px sans-serif; display: block !important;
dice-1.0.min.js是來自Github(https://github.com/diafygi/dice-css)的一個微型CSS骰子樣式庫,包括了1-6的SVG矢量圖,,可作為內(nèi)聯(lián)圖標使用,,用法與font-awesome和glyphicons相同。其代碼如下:
* Code - MIT License - https://github.com/diafygi/dice-css * Images - Public Domain - https://openclipart.org/detail/105931/sixsided-dice-faces-lio-01 .dice{display:inline-block;min-height:1em;padding-left:1em;background-size:1em;background-repeat:no-repeat;} .dice-1{background-image: url("data:image/svg+xml,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' version='1.1' viewBox='0 0 76.5 76.5' height='21.6' width='21.6'%3E%3Cg transform='translate(113.25%2C-494.1)'%3E%3Cg transform='matrix(0.5%2C0%2C0%2C0.5%2C-406.5%2C374.7)'%3E%3Crect x='588' y='240.4' width='150' height='150' ry='50' rx='50' style='fill%3A%23fff%3Bstroke-width%3A3%3Bstroke%3A%23000'%2F%3E%3Ccircle transform='translate(337.5%2C87.5)' cx='325' cy='227.4' r='12.5' style='fill%3A%23000%3Bstroke-width%3A3%3Bstroke%3A%23000'%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E ");} .dice-2{background-image: url("data:image/svg+xml,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' version='1.1' viewBox='0 0 76.5 76.5' height='21.6' width='21.6'%3E%3Cstyle%3E.s0%7Bfill%3A%23000%3Bstroke-width%3A3%3Bstroke%3A%23000%3B%7D%3C%2Fstyle%3E%3Cg transform='translate(109.9%2C-505.1)'%3E%3Cg transform='matrix(0.5%2C0%2C0%2C0.5%2C-415.6%2C485.6)'%3E%3Crect x='613' y='40.4' width='150' height='150' ry='50' rx='50' style='fill%3A%23fff%3Bstroke-width%3A3%3Bstroke%3A%23000'%2F%3E%3Ccircle transform='translate(326.5%2C-148.5)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(398.5%2C-76.5)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E ");} .dice-3{background-image: url("data:image/svg+xml,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' version='1.1' viewBox='0 0 76.5 76.5' height='21.6' width='21.6'%3E%3Cstyle%3E.s0%7Bfill%3A%23000%3Bstroke-width%3A3%3Bstroke%3A%23000%3B%7D%3C%2Fstyle%3E%3Cg transform='translate(84.9%2C-515.5)'%3E%3Cg transform='matrix(0.5%2C0%2C0%2C0.5%2C-290.6%2C514.9)'%3E%3Crect x='413' y='2.9' width='150' height='150' ry='50' rx='50' style='fill%3A%23fff%3Bstroke-width%3A3%3Bstroke%3A%23000'%2F%3E%3Ccircle transform='translate(126.5%2C-186)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(198.5%2C-114)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(162.5%2C-150)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E ");} .dice-4{background-image: url("data:image/svg+xml,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' version='1.1' viewBox='0 0 76.5 76.5' height='21.6' width='21.6'%3E%3Cstyle%3E.s0%7Bfill%3A%23000%3Bstroke-width%3A3%3Bstroke%3A%23000%3B%7D%3C%2Fstyle%3E%3Cg transform='translate(90.7%2C-499.7)'%3E%3Cg transform='matrix(0.5%2C0%2C0%2C0.5%2C-302.7%2C367.8)'%3E%3Crect x='425.5' y='265.4' width='150' height='150' ry='50' rx='50' style='fill%3A%23fff%3Bstroke-width%3A3%3Bstroke%3A%23000'%2F%3E%3Ccircle transform='translate(139%2C76.5)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(139%2C148.5)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(211%2C76.5)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(211%2C148.5)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E ");} .dice-5{background-image: url("data:image/svg+xml,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' version='1.1' viewBox='0 0 76.5 76.5' height='21.6' width='21.6'%3E%3Cstyle%3E.s0%7Bfill%3A%23000%3Bstroke-width%3A3%3Bstroke%3A%23000%3B%7D%3C%2Fstyle%3E%3Cg transform='translate(89.2%2C-510.5)'%3E%3Cg transform='matrix(0.5%2C0%2C0%2C0.5%2C-194.9%2C372.3)'%3E%3Crect x='213' y='277.9' width='150' height='150' ry='50' rx='50' style='fill%3A%23fff%3Bstroke-width%3A3%3Bstroke%3A%23000'%2F%3E%3Ccircle transform='translate(-73.5%2C89)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(-73.5%2C161)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(-1.5%2C89)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(-1.5%2C161)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(-37.5%2C125)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E ");} .dice-6{background-image: url("data:image/svg+xml,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' version='1.1' viewBox='0 0 76.5 76.5' height='21.6' width='21.6'%3E%3Cstyle%3E.s0%7Bfill%3A%23000%3Bstroke-width%3A3%3Bstroke%3A%23000%3B%7D%3C%2Fstyle%3E%3Cg transform='translate(86.2%2C-500.6)'%3E%3Cg transform='matrix(0.5%2C0%2C0%2C0.5%2C-98.2%2C356.2)'%3E%3Crect x='25.5' y='290.4' width='150' height='150' ry='50' rx='50' style='fill%3A%23fff%3Bstroke-width%3A3%3Bstroke%3A%23000'%2F%3E%3Ccircle transform='translate(-261%2C101.5)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(-261%2C173.5)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(-261%2C137.5)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(-189%2C101.5)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(-189%2C173.5)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3Ccircle transform='translate(-189%2C137.5)' cx='325' cy='227.4' r='12.5' class='s0'%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E");}
由于跨源問題的存在,,我們需要一個本地服務(wù)器才能運行這個項目,。在Linux/OS X系統(tǒng)中,可以在項目目錄下運行如下命令:
python -m SimpleHTTPServer 9000
然后到瀏覽器中,,打開localhost:9000以查看這個小應(yīng)用,。按F12打開控制臺,即可看到我們在C代碼中使用printf輸出的問候語:
四,、調(diào)用WebAssembly函數(shù)
最后一步是連接JavaScript與WebAssembly,,由于膠接代碼的存在(index.js),,這項任務(wù)變得非常簡單,,它已經(jīng)為我們處理好了所有的接線任務(wù)。
在瀏覽器中處理WebAssembly有一個非常強大的API可以使用,,在此我們不會進行深入探討因為這已經(jīng)超出了入門的范疇,,我們只需要Module接口及其ccall方法這部分即可。該方法允許我們通過函數(shù)名從C代碼中調(diào)用一個函數(shù),,然后就向一般的JS函數(shù)一樣使用就行了,。
var result = Module.ccall(
調(diào)用此方法之后,result就將擁有對應(yīng)C函數(shù)的所有功能,,除函數(shù)名以外的所有參數(shù)都是可選的,。
我們也可以使用縮寫版:
// 通過在函數(shù)名前添加下劃線來調(diào)用C函數(shù) var result = _funcName();
roll_dice 函數(shù)無需任何參數(shù),在JavaScript代碼中調(diào)用十分簡單:
// 當HTML dice元素被點擊時,,其值將會被改變 var dice = document.querySelector('.dice'); dice.addEventListener('click', function(){ // 調(diào)用C代碼中的roll_dice函數(shù) var result = _roll_dice(); dice.className = "dice dice-" + result;
將上面這段代碼添加到index.html末尾,,即</body> 之前即可,。
此時運行項目,即可看到結(jié)果:
總結(jié)
雖然現(xiàn)在WebAssembly還在發(fā)展的初期,,但從公布的新標準來看,,潛力巨大。在瀏覽器中運行低級語言的能力,,將會帶來全新的應(yīng)用程序與web體驗,,而這,是僅僅通過JavaScript無法使用的,。
誠然,,使用WebAssembly在當前階段還十分繁瑣,文檔需要分為多個部分,,相應(yīng)的工具也不容易使用,,并且還需要JavaScript膠接代碼才能使用wasm模塊。但隨著越來越多的人進入這個平臺,,所有這些問題都將會被解決,。
|