写点什么

使用 VUE 和 Go 触摸 WebAssembly

作者:devpoint
  • 2022 年 9 月 04 日
    广东
  • 本文字数:3487 字

    阅读完需:约 11 分钟

使用 VUE 和 Go 触摸 WebAssembly

本文将展示如何在 Go 中使用 WebAssembly。本文一起来学习如何从 Go 代码构建到 WebAssembly,通过 VUE 来展示使用 WebAssembly 的 API。


本文涉及的 Go 需要 Go1.11 或更高版本的 Go 开发环境,这里将忽略 Go 环境的配置。前端将使用 VUE2 来构建。


文章涉及代码:https://github.com/QuintionTang/Go-WebAssembly


什么是 WebAssembly?

WebAssembly(wasm)是指一种可以在浏览器及其外围技术和工具中运行的编程语言。它以二进制格式表示并由堆栈机器实现处理。与 JavaScript 一样,它由浏览器直接解释,但正在开发和规范,目标是在速度方面超越 JavaScript。


WebAssembly 大多由 C、C++、Rust 等各种高级语言编译而成,而不是程序员直接编写二进制代码。同样在 Go 中,将 Go 代码编译为 WebAssembly 的功能从 Go1.11 正式添加为 Go 的标准功能。


更多内容如下:


HelloWorld

Go 有一个叫做交叉编译的特性。不仅可以为正在编译的机器的体系结构和操作系统构建二进制文件,还可以为其他体系结构和操作系统构建二进制文件。


例如,在 macOS 上为 Windows 和 Linux 交叉编译二进制文件非常容易。可以通过指定以下环境 GOOS 变量来像往常一样进行交叉编译:GOARCH go bulid


# 为 Windows 编译(32 位)$ GOOS=windows GOARCH=386 go build
# 为 Linux 编译(64 位)$ GOOS=linux GOARCH=amd64 go build
复制代码


创建目录 go-webassembly,进入目录,再创建 helloworld ,进入 helloworld 目录,执行命令:


go mod init go-webassembly/helloworld
复制代码


创建文件 main.go ,代码如下:


package main
func main() { println("Hello, WebAssembly!")}
复制代码


前端实现将使用 VUE 框架来展示其调用效果,因此需要创建文件夹 vue ,将把 WebAssembly 生成的 wasmjs 文件存储到项目目录 public/wasms


现在以交叉方式编译 WebAssembly,在目录下并执行如下命令。请注意,此处将输出文件名指定为选项,但即使不指定也可以构建。GOOS js GOARCH wasm go build-o


GOOS=js GOARCH=wasm go build -o ../vue/public/wasms/helloworld/main.wasm
复制代码


执行完命令后,将在目录下生成文件 main.wasm,同时将在 GOROOT 目录下生成 wasm_exec.js 文件,完整路径为 /usr/local/go/misc/wasm,将文件复制到路径 /vue/public/wasms/helloworld/ 下,命令如下:


cp /usr/local/go/misc/wasm/wasm_exec.js .cp /usr/local/go/misc/wasm/wasm_exec_node.js .cp /usr/local/go/misc/wasm/wasm_exec.html .
复制代码


这样目录 /vue/public/wasms/helloworld 就有两个文件 jswasm,接下来执行命令:


node wasm_exec_node.js main.wasm
复制代码


输出的结果如下:


Hello, WebAssembly!
复制代码


接下来就是在 VUE 中来展示 Helloworld 的调用,在 public/index.html 中引入 JS,如下:


<script src="./wasms/helloworld/wasm_exec.js"></script>
复制代码


构建组件 Helloworld,完整代码如下:


<template>    <div class="card">        <div class="card-header">            <h4>Helloworld</h4>        </div>        <div class="card-body">            <p class="text-muted">                点击“运行”,在控制台输出日志 <code>Hello, WebAssembly!</code>            </p>            <div class="live-preview">                <button                    @click="run()"                    class="btn btn-success"                    id="runButton"                    disabled                >                    运行                </button>            </div>        </div>    </div></template>
<script>export default { name: "Helloworld", data() { return { go: null, mod: null, inst: null, }; }, mounted() { this.init(); }, methods: { init() { if (!WebAssembly.instantiateStreaming) { WebAssembly.instantiateStreaming = async ( resp, importObject ) => { const source = await (await resp).arrayBuffer(); return await WebAssembly.instantiate(source, importObject); }; }
const go = new window.Go(); this.go = go; WebAssembly.instantiateStreaming( fetch("/wasms/helloworld/main.wasm"), go.importObject ) .then((result) => { console.log(result); this.mod = result.module; this.inst = result.instance; document.getElementById("runButton").disabled = false; }) .catch((err) => { console.error(err); }); }, async run() { console.clear(); await this.go.run(this.inst); this.inst = await WebAssembly.instantiate( this.mod, this.go.importObject ); }, },};</script>
复制代码


点击按钮“运行”,在浏览器控制台输入如下:


处理 JavaScript 对象

接下来,学习如何在 JavaScript 中使用对象,为了更好的处理 Go 中的 JavaScript 对象,将使用 Go1.11 标准包中包含的包 syscall/js


syscall/js 里面定义了个新的类型 js.Value,它表示一个 JavaScript 值,它提供了一个简单的 API 来操纵任何类型的 JavaScript 值并与之交互。一个 js.ValueOf() 函数,它接受任何 Go 基本类型并返回相应的 js.Value


Go 值和 JavaScript 值对应关系如下:



JavaScript 中的 js.Type 类型表示为类型。js.Type 类型定义如下,可以从 js.Value 类型的方法中检索。


type Type intconst (  TypeUndefined Type = iota  TypeNull  TypeBoolean  TypeNumber  TypeString  TypeSymbol  TypeObject  TypeFunction)
复制代码


js.Value 类型将所有 JavaScript 值表示为单一类型,因此如果每个方法都调用了一个意外的值,panic 就会导致崩溃。例如,Int 方法可以 js.Value 将类型的值视为数字并将 int 值作为 Go 类型检索。但是,js.Value 类型也可以处理函数和字符串值,所以当调用一个不是数字的值时,panic 会发生错误。


因此,js.Type 通过使用 type 值,js.Value可以处理 type 值的具体类型,避免 panic。例如,对于Int 方法,最好只在 Type 方法返回 js.TypeNumber 时调用。如下:


func printNumber(v js.Value) {        if v.Type() == js.TypeNumber {                fmt.Printf("%d\n", v.Int())        }}
复制代码

DOM 操作

可以在 Go 中使用 js.Value 来更好的操作 HTML Dom 对象。接下来创建目录 docments ,创建文件 main.go ,代码如下:


package mainimport "syscall/js"
func main() { // 获取全局对象(网页浏览器为window) window := js.Global() // window.document.getElementById("helloresult") message := window.Get("document").Call("getElementById", "helloresult") // HTML message.Set("innerHTML", "Hello, WebAssembly")}
复制代码


接下来在前端创建一个 id="helloresult" 的 DOM 对象。


按照上面的流程,生成 wasm 文件:


GOOS=js GOARCH=wasm go build -o ../vue/public/wasms/document/main.wasm
复制代码

事件处理

上面介绍了如何操作 DOM,现在来实现时间的处理。


package main
import "syscall/js"
func main() { window := js.Global() // window.document.getElementById("clickresult") message := window.Get("document").Call("getElementById", "clickresult")
cb := js.FuncOf(func(this js.Value, args []js.Value) any { message.Set("innerHTML", "Go 事件触发") return nil }) // message.addEventListener("click", cb) message.Call("addEventListener", "click", cb)
select {} }
复制代码


按照上面的流程,生成 wasm 文件:


GOOS=js GOARCH=wasm go build -o ../vue/public/wasms/events/main.wasm
复制代码

总结

本文介绍了如何将 Go 代码构建为 WebAssembly、如果实现 Go 与 JavaScript 对象及回调函数。

发布于: 刚刚阅读数: 3
用户头像

devpoint

关注

细节的追求者 2011.11.12 加入

专注前端开发,用技术创造价值!

评论

发布
暂无评论
使用 VUE 和 Go 触摸 WebAssembly_Go_devpoint_InfoQ写作社区