前一篇我們探索了用Emscripten編譯C
程式碼到asm.js
和WebAssembly
,使前端程式碼執行速度大大提升,但是實際專案中由於C
語言缺乏很多高階特性,不利於開發大型專案(說C
可以開發作業系統kernel這種大型專案的同學不好意思,我沒那麼nb),而C++
我又覺得太複雜,也沒有用過C++
做過大型專案,所以我最後選擇了Rust。
一開始也糾結過要用Go
還是Rust
或者Swift
的,後來發現Go
目前還不支援編譯到WebAssembly
,Swift按理說應該可以支援的,因為都是用LLVM做的編譯器,不過沒有找到好的資料,好像說要自己編譯LLVM去支援https://stackoverflow.com/questions/46572144/compile-swift-to-webassembly 。另外對Rust的一些特性很是喜歡,聽說Rust很複雜,比較像Scala和Haskell
,而偏偏我對Scala還算熟悉,也學過一下Haskell,所以決定嘗試一下Rust。
https://github.com/ChristianMurphy/compile-to-web 這裡可以檢視目前能編譯到WebAssembly的語言。
PS, 話說asm.js和Rust都是Mozilla搞的呢。
安裝Rust的管理工具rustup
rustup用於安裝管理Rust的相關工具,包括編譯器rustc、包管理工具cargo等,支援安裝不同版本比如stable, beta, nightly等以及在不同版本之間切換,類似於nvm。
1 |
curl https://sh.rustup.rs -sSf | sh |
安裝Emscripten Rust編譯器
用rustup安裝最新體驗版(Nightly Version):
1 2 |
rustup toolchain add nightly rustup target add wasm32-unknown-emscripten --toolchain nightly |
安裝cmake
根據平臺自行選擇:
1 2 3 |
brew install cmake <span># MacOS, brew</span> sudo port install cmake <span># MacOS, MacPorts</span> sudo apt-get install cmake <span># Debian Linux</span> |
安裝 Emscripten
參考前一篇,或者直接執行下面命令:
1 2 3 4 5 |
wget https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz tar -xvf emsdk-portable.tar.gz cd emsdk-portable ./emsdk update ./emsdk install sdk-incoming-64bit |
這一步花的時間比較久,據說要2個多小時,我是執行完命令就出去跟朋友吃飯了,所以具體時間不知道。
新增下列路徑到PATH
中:
1 2 3 |
~/emsdk-portable ~/emsdk-portable/clang/fastcomp/build_incoming_64/bin ~/emsdk-portable/emscripten/incoming |
終端執行emcc -v
檢查是否安裝成功。
用Webpack執行Rust
新建一個Rust/Javascript混合專案:
1 2 3 4 |
cargo new webasm --bin --vcs none cd webasm npm init rustup override set nightly |
安裝Webpack, webpack-dev-server, rust-wasm-loader,
1 |
npm i -D webpack webpack-dev-server rust-wasm-loader |
增加package.json
指令碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
{ "name": "webasm", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "compile": "webpack --progress", "serve": "http-server", "start": "webpack-dev-server --content-base ./build" }, "author": "magicly", "license": "ISC", "devDependencies": { "http-server": "^0.10.0", "rust-wasm-loader": "^0.1.2", "webpack": "^3.6.0", "webpack-dev-server": "^2.8.2" } } |
在build
目錄下新建檔案index.html
:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<!DOCTYE html> <html> <head> <title>Hello WebAssembly</title> </head> <body> < div id="container"></div> <script src="/bundle.js"></script> </body> </html> |
配置webpack.config.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: __dirname + '/build', }, module: { rules: [ { test: /\.rs$/, use: { loader: 'rust-wasm-loader', options: { <span>// The path to the webpack output relative to the project root</span> path: '', release: true <span>// 沒有的話效能巨差,差不多隻有1/10</span> } } } ] }, <span>// The .wasm 'glue' code generated by Emscripten requires these node builtins,</span> <span>// but won't actually use them in a web environment. We tell Webpack to not resolve those</span> <span>// require statements since we know we won't need them.</span> externals: { 'fs': true, 'path': true, } } |
新建src/main.rs
檔案,新增我們要從js中呼叫的函式:
1 2 3 4 5 6 7 8 9 10 |
fn main() { println!("Hello, world!"); } <span>// Functions that you wish to access from Javascript</span> <span>// must be marked as no_mangle</span> #[no_mangle] pub fn add(a: i32, b: i32) -> i32 { return a + b } |
新建src/index.js
,寫程式碼載入WebAssembly模組:
1 2 3 4 5 6 7 8 9 |
const wasm = require('./main.rs') wasm.initialize({ noExitRuntime: true }).then(module => { <span>// Create a Javascript wrapper around our Rust function</span> const add = module.cwrap('add', 'number', ['number', 'number']) console.log('Calling rust functions from javascript!') console.log(add(1, 2)) }) |
然後執行npm start
,訪問http://localhost:8080/就可以看到呼叫rust程式碼的效果了。並且還支援熱更新哦,直接修改rust程式碼,儲存,頁面就能看到最新效果。
測試了一下前一篇裡的程式碼,直接執行rust優化過的程式碼只需要300多ms,這個基本跟C程式碼一樣,但是用wasm執行,居然要2.7s左右,不知道是哪裡沒有配置好,還是說現在Rust編譯成wasm沒有優化好。Rust支援WebAssembly應該還不是特別成熟,可以關注https://github.com/rust-lang/rust/issues/38804 跟進。
另外Rust有一個包https://crates.io/crates/webplatform, 可以用來操作DOM,不過我目前用不到(感覺沒啥用)。
Refers
- https://medium.com/@ianjsikes/get-started-with-rust-webassembly-and-webpack-58d28e219635
- http://zcfy.cc/article/get-started-with-rust-webassembly-and-webpack-ian-j-sikes-medium-3345.html
- Compiling to the web with Rust and emscripten
- Rust ⇋ JavaScript
- http://www.hellorust.com/emscripten/
- http://asquera.de/blog/2017-04-10/the-path-to-rust-on-the-web/
- https://github.com/mrdziuban/rust-emscripten-loader
- https://github.com/ballercat/wasm-loader
- https://hackernoon.com/compiling-rust-to-webassembly-guide-411066a69fde
- https://github.com/mbasso/awesome-wasm
- https://github.com/rust-lang/rust/issues/38805
- https://davidmcneil.github.io/the-rusty-web/#benchmarks
- http://asmjs.org/
- http://webassembly.org/
- https://kripken.github.io/emscripten-site/index.html
- https://developer.mozilla.org/en-US/docs/WebAssembly
- http://www.codepool.biz/emscripten-compile-cc-javascript.html
- http://www.ruanyifeng.com/blog/2017/09/asmjs_emscripten.html
- https://zhuanlan.zhihu.com/p/25865972
- https://zhuanlan.zhihu.com/p/24632251