您现在的位置是:网站首页> Go语言
Go开发wasm资料收集
- Go语言
- 2024-10-10
- 161人已阅读
Go开发wasm资料收集
Go进行wasm编程
wasm即webAssemble,是一种不针对特定平台的二进制格式文件。Go从1.11开始支持wasm,最初通过js.NewCallBack()注册函数,1.12开始换成了FuncOf()。
Go开发wasm需要一个go文件用于编写实现代码,编译成.wasm文件;需要一个wasm_exec.js文件,这个是Go提供的,可以从 Go 安装目录的 misc 子目录里找到,将它直接拷贝过来。它实现了和 WebAssembly 模块交互的功能;另外就是需要一个HTML文件用于加载wasm文件。当然为了工作起来,我们还要实现一个简单的HTTP服务。
golang wasm解密模块
func decode(this js.Value, args []js.Value) interface{} {
// 加密key
keys := ([]byte)("KEY_PREFIX_" + args[0].String())
buffer := make([]byte, args[1].Length())
// 从js读取
js.CopyBytesToGo(buffer, args[1])
// ^ 示例解密函数 ( a ^ b ^ b = a)
for i := range buffer {
buffer[i] = buffer[i] ^ keys[i%len(keys)]
}
// 拷贝到js
array := js.Global().Get("Uint8Array").New(len(buffer))
js.CopyBytesToJS(array, buffer)
return array
}
func main() {
done := make(chan int, 0)
js.Global().Set("decode", js.FuncOf(decode))
<-done
}
js 调用
<script src="wasm_exec.js"></script>
function decodeImage() {
var oReq = new XMLHttpRequest();
oReq.open("GET", "demo_encode.png", true);
oReq.responseType = "arraybuffer";
oReq.onload = function (oEvent) {
var arrayBuffer = oReq.response; // 注意:不是oReq.responseText
if (arrayBuffer) {
var byteArray = new Uint8Array(arrayBuffer);
var decodeData = decode('KEY_HERE', byteArray);
document.getElementById("decodeJavaImage").src = URL.createObjectURL(
new Blob([decodeData], { type: 'image/png' })
);
}
};
oReq.send(null);
}
一、用Go编写代码并编译成wasm文件
package main
import (
"fmt"
"math/rand"
"strconv"
"syscall/js"
"time"
)
const (
width = 400
height = 400
)
// 生成 0 - 1 的随机数
func getRandomNum() float32 {
rand.New(rand.NewSource(time.Now().UnixNano()))
n := float32(rand.Intn(10000))
return n / 10000.0
}
// 生成 0 - 10 的随机数
func getRandomNum2() float32 {
rand.New(rand.NewSource(time.Now().UnixNano()))
n := float32(rand.Intn(10000))
return n / 1000.0
}
// 使用 canvas 绘制随机图
func draw() {
var canvas js.Value = js.
Global().
Get("document").
Call("getElementById", "canvas")
var context js.Value = canvas.Call("getContext", "2d")
// reset
canvas.Set("height", height)
canvas.Set("width", width)
context.Call("clearRect", 0, 0, width, height)
// 随机绘制 50 条直线
var clineStyle = `rgba(%d, %d, %d, 0.5)`
for i := 0; i < 50; i++ {
lineStyle := fmt.Sprintf(clineStyle, 155+int(getRandomNum2()*10), 155+int(getRandomNum()*100), 155+int(getRandomNum()*100))
fmt.Println(lineStyle)
context.Call("beginPath")
context.Set("strokeStyle", lineStyle)
context.Call("moveTo", getRandomNum()*width, getRandomNum()*height)
context.Call("lineTo", getRandomNum()*width, getRandomNum()*height)
context.Call("stroke")
}
context.Set("font", "30px Arial")
context.Set("strokeStyle", "blue")
for i := 0; i < 10; i++ {
context.Call("strokeText", "hello wasm", (getRandomNum2()+1)*10+getRandomNum2()*10, (getRandomNum2()+1)*10+getRandomNum2()*50)
}
}
func registerCallbackFunc() {
cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
fmt.Println("button clicked")
num1 := getElementByID("num1").Get("value").String()
v1, err := strconv.Atoi(num1)
if nil != err {
fmt.Println("button clicked:", num1, err.Error())
jsAlert().Invoke(err.Error())
// panic(err)
return nil
}
num2 := getElementByID("num2").Get("value").String()
v2, err := strconv.Atoi(num2)
if nil != err {
fmt.Println("button clicked:", num2, err.Error())
// panic(err)
return nil
}
rlt := v1 + v2
getElementByID("rlt").Set("value", rlt)
return nil
})
getElementByID("compute").Call("addEventListener", "click", cb)
}
func getElementByID(id string) js.Value {
return js.Global().Get("document").Call("getElementById", id)
}
func jsAlert() js.Value {
return js.Global().Get("alert")
}
func main() {
fmt.Println("Hello, Go WebAssembly!")
draw()
// 通过js.Global().Get()拿到全局alert函数的引用
alert := js.Global().Get("alert")
// 调用alert.Invoke来调用alert函数
alert.Invoke("hello world")
done := make(chan struct{}, 0) // 创建无缓冲通道
registerCallbackFunc()
<-done // 阻塞
/*
创建一个通道,然后在从通道读取内容,因为通道没有内容,所以会阻塞(不占用CPU)
*/
}
Go wasm代码
将代码编译成Wasm文件,需要设置编译环境。我用的VsCode,用powershell设置环境变量始终不能生效,于是换成了Bash:
执行:go env 查看环境,注意GOOS和GOARCH,如果是win 系统的话,默认应该是windows和amd64,为了编译出wasm文件,需要修改如下:
export GOOS=js
export GOARCH=wasm
否则编译的时候会提示奇怪的信息(不是提示环境问题),如果还是不对,可以设置CGO:
export CGO_ENABLED=0
当然我设置的1是没问题的。
最后编译生成wasm文件:
go build -o lib.wasm main.go
-o 是编译参数,指定输出的文件。
在Go里面要引入:syscall/js
通过js.Global().Get()获取js对象,既可以获取函数、也可以获取DOM元素。类型是js.Value。
如:
js.Global().Get("alert")
js.Global().Get("document")
如果是设置元素的属性调用Set(),如果是呼叫(执行)方法,调用Call("函数名","参数")。
如:
js.Global().Get("document").Call("getElementById", id)
前面的代码演示了调用alert()、Input的读写、Canvas对象的操作。
二、编写HTML
<html>
<head>
<meta charset="utf-8">
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("lib.wasm"), go.importObject).then((result) => {
go.run(result.instance);
});
</script>
</head>
<body>
<canvas id='canvas'></canvas></br>
<input id="num1" type="number" />
+
<input id="num2" type="number" />
=
<input id="rlt" type="number" readonly="readonly" />
<button id="compute">compute</button>
</body>
</html>
HTML文件主要是定义界面元素,引入wasm_exec.js文件,调用刚才build的lib.wasm。
三、编写一个HTTP服务
Go 内置的 HTTP 服务器支持Content-Type 为 application/wasm。
package main
import (
"flag"
"log"
"net/http"
)
var (
listen = flag.String("listen", ":8087", "listen address")
dir = flag.String("dir", ".", "files directory to serve")
)
func main() {
flag.Parse()
log.Printf("listening on %q...", *listen)
err := http.ListenAndServe(*listen, http.FileServer(http.Dir(*dir)))
log.Fatalln(err)
}
这里要注意:之前为了编译wasm文件,修改了GOOS和GOARCH,现在为了运行http服务,我们必须恢复。
为了方便调试,我们可以在vscode里面新建一个终端,执行:
export GOOS=windows
export GOARCH=amd64
然后执行:
go run server.go
如果有防火墙提示网络访问,选择允许,然后会看到终端提示:
2020/03/10 09:27:12 listening on ":8087"...
这表示我们的HTTP服务启动好了。
四、测试效果
在浏览器里面输入:http://127.0.0.1:8087/
页面上还有一个计算的功能,我们输入数字,点击按钮
这就是用Go开发Wasm的基本套路了
上一篇:Go优秀的开源库及软件项目