// main.go
package main
import "fmt"
func main() {
fmt.Println("Hello, WebAssembly!")
}
GOOS=js GOARCH=wasm go build -o main.wasm .
# 拷贝必要的js
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
# 安装web工具
go get -u github.com/shurcooL/goexec
# 启动web服务
goexec 'http.ListenAndServe(`:8080`, http.FileServer(http.Dir(`.`)))'
<!-- index.html -->
<html>
<head>
<meta charset="utf-8" />
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(
fetch("main.wasm"),
go.importObject
).then((result) => {
go.run(result.instance);
});
</script>
</head>
<body></body>
</html>
// main.go
package main
import "syscall/js"
func fib(i int) int {
if i == 0 || i == 1 {
return i
}
return fib(i-1) + fib(i-2)
}
func fibFunc(this js.Value, args []js.Value) interface{} {
return js.ValueOf(fib(args[0].Int()))
}
func main() {
done := make(chan int)
js.Global().Set("fibFunc", js.FuncOf(fibFunc))
<-done
}
<!-- index.html -->
<html>
<head>
<meta charset="utf-8" />
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(
fetch("main.wasm"),
go.importObject
).then((result) => {
go.run(result.instance);
});
</script>
</head>
<body>
<input id="num" type="number" />
<button id="btn" onclick="ans.innerHTML=fibFunc(num.value * 1)">
Click
</button>
<p id="ans">1</p>
</body>
</html>
// main.go
package main
import (
"strconv"
"syscall/js"
)
func fib(i int) int {
if i == 0 || i == 1 {
return i
}
return fib(i-1) + fib(i-2)
}
var (
document = js.Global().Get("document")
numEle = document.Call("getElementById", "num")
ansEle = document.Call("getElementById", "ans")
btnEle = js.Global().Get("btn")
)
func fibFunc(this js.Value, args []js.Value) interface{} {
v := numEle.Get("value")
if num, err := strconv.Atoi(v.String()); err == nil {
ansEle.Set("innerHTML", js.ValueOf(fib(num)))
}
return nil
}
func main() {
done := make(chan int)
btnEle.Call("addEventListener", "click", js.FuncOf(fibFunc))
<-done
}
<!-- index.html -->
<html>
<head>
<meta charset="utf-8" />
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(
fetch("main.wasm"),
go.importObject
).then((result) => {
go.run(result.instance);
});
</script>
</head>
<body>
<input id="num" type="number" />
<button id="btn">Click</button>
<p id="ans">1</p>
</body>
</html>
// main.go
package main
import (
"syscall/js"
"time"
)
func fib(i int) int {
if i == 0 || i == 1 {
return i
}
return fib(i-1) + fib(i-2)
}
func fibFunc(this js.Value, args []js.Value) interface{} {
callback := args[len(args)-1]
// 异步执行
go func() {
time.Sleep(3 * time.Second)
v := fib(args[0].Int())
callback.Invoke(v) // 执行完成,调用回调函数
}()
js.Global().Get("ans").Set("innerHTML", "Waiting 3s...")
return nil
}
func main() {
done := make(chan int)
js.Global().Set("fibFunc", js.FuncOf(fibFunc))
<-done
}
<!-- index.html -->
<html>
<head>
<meta charset="utf-8" />
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(
fetch("main.wasm"),
go.importObject
).then((result) => {
go.run(result.instance);
});
</script>
</head>
<body>
<input id="num" type="number" />
<button id="btn" onclick="fibFunc(num.value * 1, (v)=> ans.innerHTML=v)">
Click
</button>
<p id="ans"></p>
</body>
</html>
package main
import "syscall/js"
func fib(i int) int {
if i == 0 || i == 1 {
return i
}
return fib(i-1) + fib(i-2)
}
func fibFunc(this js.Value, args []js.Value) interface{} {
return js.ValueOf(fib(args[0].Int()))
}
func main() {
done := make(chan int)
js.Global().Set("fibFunc", js.FuncOf(fibFunc))
<-done
}
<html>
<head>
<meta charset="utf-8" />
<script src="./wasm_exec.js"></script>
<script type="module">
function fibonacciInJs(n) {
if (n <= 1) return n;
return fibonacciInJs(n - 1) + fibonacciInJs(n - 2);
}
async function run() {
const go = new Go();
const result = await WebAssembly.instantiateStreaming(
fetch("main.wasm"),
go.importObject
);
go.run(result.instance);
const num = 20;
console.time("Fibonnaci in go");
const fibGo = fibFunc(num);
console.timeEnd("Fibonnaci in go");
console.time("Fibonnaci in JS");
const fibJS = fibonacciInJs(num);
console.timeEnd("Fibonnaci in JS");
document.body.textContent = `Fib ${num}: Go ${fibGo} - JS ${fibJS}`;
}
run();
</script>
</head>
<body></body>
</html>
# 优化前命令
GOOS=js GOARCH=wasm go build -o ./main.wasm ./main.go
# 利用 tinygo
tinygo build -o ./main.wasm -target wasm ./main.go
tinygo build -o ./main.wasm -target wasm -no-debug ./main.go
参考: https://developer.mozilla.org/zh-CN/docs/WebAssembly/Rust_to_wasm
cargo install wasm-pack
# 创建一个lib项目
cargo new --lib hello-wasm
# 修改对应文件
# 构建包
# --target bundler - for bundlers like Webpack, Parcel, or Rollup.
# --target web - for the web as ECMAScript module.
# --target no-modules - for the web without ECMAScript module.
# --target nodejs - for Node.js
# wasm-pack build --scope mynpmusername
wasm-pack build --target bundler
# 发布包(可选)
cd pkg
npm publish --access=public
// src/lib.rs
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
# Cargo.toml
[package]
name = "hello-wasm"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
description = "A sample project with wasm-pack"
license = "MIT/Apache-2.0"
repository = "https://github.com/yourgithubusername/hello-wasm"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
// package.json
{
"scripts": {
"serve": "webpack-dev-server"
},
// "dependencies": {
// "@mynpmusername/hello-wasm": "^0.1.0"
// },
"devDependencies": {
"webpack": "^4.25.1",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10"
}
}
// webpack.config.js
const path = require("path");
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "index.js",
},
mode: "development",
};
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>hello-wasm example</title>
</head>
<body>
<script src="./index.js"></script>
</body>
</html>
// index.js
const js = import("../pkg/hello_wasm.js");
js.then((js) => {
js.greet("WebAssembly");
});
npm install
npm run serve
opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
library: 'digital envelope routines',
reason: 'unsupported',
code: 'ERR_OSSL_EVP_UNSUPPORTED'
#For Windows, use the below command in cmd:
set NODE_OPTIONS=--openssl-legacy-provider
#For Unix, use:
export NODE_OPTIONS=--openssl-legacy-provider
参考: https://rustwasm.github.io/wasm-bindgen/examples/index.html
wasm-pack build --target web --debug
# npm install --global http-server
# http-server
npx serve .
// src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn helloworld() -> String {
String::from("Hello world from Rust!")
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>"Hello world" in Rust + Webassembly</title>
<script type="module">
import init, { helloworld } from "./pkg/hello_wasm.js";
async function run() {
await init();
document.body.textContent = helloworld();
}
run();
</script>
</head>
<body></body>
</html>
// use the function console.log inside Rust
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[wasm_bindgen]
pub fn example() {
log("Log from rust");
}
import init, { example } from "./pkg/helloworld.js";
async function run() {
await init();
example(); // This will log "Log from rust" to the console
}
run();
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u32 {
match n {
0 | 1 => n,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
import init, { fibonacci } from "./pkg/helloworld.js";
function fibonacciInJs(n) {
if (n <= 1) return n;
return fibonacciInJs(n - 1) + fibonacciInJs(n - 2);
}
async function run() {
await init();
const num = 20;
console.time("Fibonnaci in rust");
const fibRust = fibonacci(num);
console.timeEnd("Fibonnaci in rust");
console.time("Fibonnaci in JS");
const fibJS = fibonacciInJs(num);
console.timeEnd("Fibonnaci in JS");
document.body.textContent = `Fib ${num}: Rust ${fibRust} - JS ${fibJS}`;
}
run();
# They work the same way as with npm pack and npm publish
wasm-pack pack myproject/pkg
wasm-pack publish
use wasm_bindgen::prelude::*;
// Called by our JS entry point to run the example
#[wasm_bindgen(start)]
pub fn run() -> Result<(), JsValue> {
// Use `web_sys`'s global `window` function to get a handle on the global
// window object.
let window = web_sys::window().expect("no global `window` exists");
let document = window.document().expect("should have a document on window");
let body = document.body().expect("document should have a body");
// Manufacture the element we're gonna append
let val = document.create_element("p")?;
val.set_text_content(Some("Hello from Rust!"));
body.append_child(&val)?;
Ok(())
}
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
[dependencies.web-sys]
version = "0.3.4"
features = [
'Document',
'Element',
'HtmlElement',
'Node',
'Window',
]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>"Hello world" in Rust + Webassembly</title>
<script type="module">
import init from "./pkg/helloworld.js";
async function run() {
await init();
}
run();
</script>
</head>
<body></body>
</html>