title: [WebAssembly 入門] Hello, world!javascript
date: 2018-3-29 14:45:00html
categories: WebAssembly, 筆記java
tags: WebAssembly, JavaScript, Rust, LLVM toolchain編程
auther: Yiniauvim
進過一段時間的基礎知識學習,是時候正式開始WebAssembly編程了!數組
我花了2個月左右的時間入門Rust,拿它寫了一個sudoku遊戲。 所以選擇 Rust
-> LLVM toolchain
-> WebAssembly
對我而言是比較天然的。bash
第一 經過 Cargo 新建一個 lib 項目閉包
cargo new hello_world --lib && cd hello_world
複製代碼
第二 須要一個HTML承載JS代碼並顯示效果app
vim index.html
複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebAssembly</title>
</head>
<body>
<script src="main.js"></script>
</body>
</html>
複製代碼
第三 建立JS文件dom
vim main.js
複製代碼
開始以前。確認一下如今的目錄狀況
ll .
-rw-r--r-- 1 yiniau staff 52B Mar 25 22:14 Cargo.lock
-rw-r--r-- 1 yiniau staff 112B Mar 25 22:14 Cargo.toml
-rw-r--r-- 1 yiniau staff 165B Mar 29 15:00 index.html
-rw-r--r-- 1 yiniau staff 743B Mar 29 15:00 main.js
drwxr-xr-x 3 yiniau staff 96B Mar 29 14:58 src
ll src
-rw-r--r-- 1 yiniau staff 494B Mar 29 14:35 lib.rs
複製代碼
經過以前的幾篇翻譯, 咱們知道可以經過 fetch
獲取.wasm文件並經過轉換成類型化數組或數組緩衝區並傳入 WebAssembly.instantiate
進行編譯 可是咱們尚未一個.wasm文件,當務之急是在lib.rs中編輯Rust代碼並經過 rustc
編譯輸出 .wasm 文件
rustup 工具鏈請自行搜索安裝,十分簡單。
千軍萬馬,註釋先行
//! hello world with WebAssembly
複製代碼
做爲開始的一小步,先從最簡單的開始————在WebAssembly中調用JS傳遞過來的函數及其閉包,主要的邏輯在JS函數中封裝好
咱們能夠經過 在imports導入資源中添加環境屬性來實現,先來建立一個imports對象
const imports = {
env: {}
}
複製代碼
在env
屬性中添加想要傳遞的函數
env: {
function hello_world() {
const h1 = document.createElement('h1');
h1.innerHTML = 'Hello, world';
const body = document.querySelector('body');
body.appendChild(h1);
}
}
複製代碼
而後讓咱們轉戰 lib.rs
要使用JS的函數,咱們須要利用ffi,先在extern中聲明外部函數
//! a WebAssembly module with Rust
extern {
fn hello_world();
}
複製代碼
這下就可以在函數體中調用了
實現js中調用的函數接口
/// call js function to access DOM
#[no_mangle]
pub extern fn hello_call_js() { // equal pub extern "C" fn ...
unsafe {
hello_insert_dom();
}
}
複製代碼
ok, 目前lib.rs
應該長這樣了
//! a WebAssembly module with Rust
extern {
fn hello_insert_dom();
}
/// return "Hello, world"'s bytes array
#[no_mangle]
pub extern fn hello_call_js() { // equal pub extern "C" fn ...
unsafe {
hello_insert_dom();
}
}
複製代碼
到此,rust的準備工做已經就緒,下一步就是編譯了
在zsh中輸入
mkdir build
rustc +nightly --target=wasm32-unknown-unknown -O --crate-type=cdylib src/lib.rs -o build/hello.wasm
# +nightly 指明使用nightly版本
# --target 指定編譯器後
# -O == -C opt-level=2
# -C opt-level=2 指定優化級別爲2,範圍是 0-3
# --create-type=cdylib 添加一個由編譯器接受的crate類型,稱爲cdylib,它對應於從Rust動態庫中導出的C接口。
# -o 指定輸出文件名
複製代碼
// TODO:記錄第一個問題,動態庫是啥,使用這個庫有什麼用。
DONE!!
咱們能夠在 build/
目錄中找到 hello.wasm 文件
若是想看看長什麼樣子,能夠用
hexdump
查看
fetch('build/hello.wasm') // 獲取到的是以16進製表達的二進制文件
.then(res => res.arrayBuffer()) // 放入數組緩衝區
.then(bytes => WebAssembly.instantiate(bytes, imports)) // 將二進制數據傳入,交給JIT處理,同時攜帶imports導入資源
.then(results => { // 返回的結果中有,有兩個屬性
// 一個是`module`, 這是已編譯的 `WebAssembly.module`
// 一個是`instance`, `WebAssembly.Instance`, 也就是WebAssembly.module的第一個實例
const exports = results.instance.exports; // instance.exports 攜帶了 rust 中 pub extern 聲明的函數(即導出的函數)
exports.hello_call_js(); // 調用
})
複製代碼
如今咱們的main.js
應該長這樣:
const imports = {
env: {
hello_insert_dom: () => {
const h1 = document.createElement('h1');
h1.innerHTML = 'Hello, world';
const body = document.querySelector('body');
body.appendChild(h1);
}
}
};
fetch('build/hello.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes, imports))
.then(results => {
console.log(results);
const exports = results.instance.exports;
exports.hello_call_js();
// exports.hello_call_js_pass_data();
});
複製代碼
首先開啓一個本地服務, 我用的lighttpd, 這個隨意
localhost!
hello_world並不會就此結束,爲了進一步瞭解WebAssembly 我準備經過各類方式實現hello_world來實踐WebAssembly
e.g. 經過在WebAssembly中向JS函數傳遞參數來實現Hello,world