Gopher 的 WebAssembly 实践
Posted WebAssembly 中文社区
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gopher 的 WebAssembly 实践相关的知识,希望对你有一定的参考价值。
本文内容来自微信公众号 一手代码一首诗 !
原标题《Go 的 WebAssembly 初见》
介 绍
在 go 1.11 的时候 go 官方向 go 实验性的添加了 WebAssembly 支持,也就是说 golang 可以编译 为 wasm 以供 javascript 进行调用。
在这里我想说一下我的感受,在编写代码时应该是主要编写 go 的代码供 JavaScript 调用,而不是在 go 中调用 JavaScript 代码。
目前 go 还只是实验性的支持 wasm 以后可能会变更,后面我会尽量跟进。
另外如果感兴趣的话,推荐一本柴树杉、丁尔男两位大佬的《WebAssembly 标准入门
》,估计也快发售了。
WebAssembly 简介
WebAssembly 是一种新兴的网页虚拟机标准,它的设计目标包括高可移植性、高安全性、高效率(包括载入效率和运行效率)、尽可能小的程序体积。本书详尽介绍了 WebAssembly 程序在 JavaScript 环境下的使用方法、WebAssembly 汇编语言和二进制格式,给出了大量简单易懂的示例,同时以 C/C++/Rust/Go 语言开发环境为例,介绍了如何使用其他高级语言开发 WebAssembly 模块。另外WebAssembly有好几个干爹:万维网联盟、 Mozilla、微软、谷歌、 苹果。
Hello Wasm
由于目前只是实验性的支持,在编写代码时 引用 syscall/js
会爆红 需要在goland
中将变量环境 指定为 OS=js ARCH=wasm goland
的设置在 seting -> Go -> Build Tags && Vendoring
中进行更改。
我使用的是 chrome 浏览器 , 包括 js 部分演示我也是在 chrome 下进行。
-
创建一个项目 hello-wasm-go
-
创建 main.go 并写入以下内容
package main
import "fmt"
func main() {
fmt.Println("Hello, WebAssembly!")
}
-
然后将 main.go 编译为 main.wasm
GOOS=js GOARCH=wasm go build -o main.wasm
-
创建 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>
-
将 JavaScript 支持文件移动到本目录
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
下载 go 版的 简单 web 服务器
go get -v -u github.com/shurcooL/goexec
然后运行它
goexec 'http.ListenAndServe(`:8080`, http.FileServer(http.Dir(`.`)))'
然后打开 chrome 访问 http://127.0.0.1:8080/
并按下 F12 打开 DevTools 不出意外的话你应该会看到 控制台上输出 Hello, WebAssembly!
接下来就正式开始了,在每次更改 go 代码时 你需要对 go 代码进行重新编译,重启 goexec
并对网页进行刷新。
在js里面调用go的wasm
接下来我们玩一点不一样的,go 调用 JavaScript 函数并且回显到前端页面,也就是在前端调用我们使用go写的代码逻辑。
-
首先我们创建一个 main.go
和index.html
// Copyright (c) 2020 HigKer
// Open Source: MIT License
// Author: SDing <deen.job@qq.com>
// Date: 2021/1/7 - 4:26 下午 - UTC/GMT+08:00
package main
import (
"fmt"
"syscall/js"
)
// 通过Go代码操作页面dom节点
var (
document = js.Global().Get("document")
nameEle = document.Call("getElementById", "name")
helloEle = document.Call("getElementById", "hello")
btnEle = js.Global().Get("btn")
)
func sayHello(this js.Value, args []js.Value) interface{} {
name := nameEle.Get("value").String()
if len(name) == 0 {
name = "github.com/higker/hello-wasm-go"
}
str := fmt.Sprintf("Hello,%s", name)
helloEle.Set("innerHTML", js.ValueOf(str))
return nil
}
func main() {
done := make(chan int, 0)
btnEle.Call("addEventListener", "click", js.FuncOf(sayHello))
<-done
}
下面是index.html内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello WebAssembly For Golang</title>
</head>
<body style="text-align: center">
<p>You Name:</p>
<input id="name" type="text">
<button id="btn">Running</button>
<h1 id="hello"></h1>
</body>
<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>
</html>
-
编译代码
GOOS=js GOARCH=wasm go build -o main.wasm
-
复制依赖文件
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
-
启动静态服务器
goexec 'http.ListenAndServe(`:8080`, http.FileServer(http.Dir(`.`)))'
-
你也可以通过其他方式启动一个服务器来浏览。
效 果
以上是关于Gopher 的 WebAssembly 实践的主要内容,如果未能解决你的问题,请参考以下文章
在 Rust 程序和嵌入式 WebAssembly 运行时之间进行通信的最佳实践是啥?