[WebAssembly 入門] Hello, world!


title: [WebAssembly 入門] Hello, world!javascript

date: 2018-3-29 14:45:00html

categories: WebAssembly, 筆記java

tags: WebAssembly, JavaScript, Rust, LLVM toolchain編程

auther: Yiniauvim


[WebAssembly 入門] Hello, world!


進過一段時間的基礎知識學習,是時候正式開始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導入資源中添加環境屬性來實現,先來建立一個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();
}
複製代碼

這下就可以在函數體中調用了

WebAssembly函數體

實現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 查看

在JS中導入.wasm文件並使用

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

相關文章
相關標籤/搜索