这可能是世界上最简单的用Go来写WebAssembly的教程

2023-09-06 0 1,014

这可能是世界上最简单的用Go来写WebAssembly的教程
你指出 WebAssembly (WASM) 只用作绘图、繁杂的定量分析或是 Web 上的小小的应用领域吗?你与否时常将 WASM 与 Web Workers 和 Service Workers 的基本概念混为一谈?你对 WASM 不屑一顾,原因在于你指出那时的 Web 应用领域程序在今后 10 年里依然是 JavaScript 主导力量?你与否Dharmapuri用 JS 之外的词汇做 Web 前端开发?

如果你不该整本,你能瞧瞧我做的demo 网页或是间接瞧瞧 go-wasm-cat-game-on-canvas-with-docker 那个工程项目,就要讲的简约许多,尽可能不节约你的时间。下列是我那个工程项目的许多关键性的标识符导出。

故事情节开始了

他们的最终目标是给猫 做两个单纯的迷你游戏:做两个肿块在智能手机上时不时的终端,整座过程除了 HiFi 音乐创作 除了冲击波。整座工程项目他们要用 Golang(Go)尖萼词汇来同时实现,包括 DOM 操作方式、方法论除了有关的状态。

而而而而而且,由于狗狗不会采用滑鼠,他们还需要给斜升 做许多点选轻触的可视化。

说呵呵我的认知!

把 WASM 想像成两个 通用型软件包(UVM, Universal Virtual Machine)或是一个沙盒,你只需撰写一场任何人标识符,它便能在任何人地方运转。

WASM 是两个校对最终目标,而不是一种词汇。就像你要同时特别针对 Windows,Mac OS 和 Linux 进行校对一样!

我不指出 WASM 会废弃 JS,你能有其它优先选择而不必牺牲任何人牺牲。

想像呵呵采用 Go,Swift,Rust,Ruby,C ++,OCaml 或是其它词汇的开发者。那时,他们能采用自己讨厌的词汇来建立互动式,互联,加速,具有控制代码机能的中文网站和Web 应用领域。

你与否曾经参与过类似「两个工程项目是用两个标识符仓库管理还是多个标识符仓库管理?」问题的讨论?

好吧,不管你有没有,你那时也要想呵呵那时那个工程项目打算用一门词汇同时实现还是多门词汇同时实现了。

当大家能采用相同的技术栈时,一切都会变得更加容易,尤其是团队之间的沟通。

你能依然采用 React 或是 Vue,但你那时开始也能不必采用 JS 来开发了。

WASM 跟 Service Workers 除了 Web Workers 有什么区别?

Service Workers 除了 Web Workers 允许应用领域在后台运转,也能做到离线运转和缓存。它们模仿线程,无法访问DOM,并且不能共享数据(仅能通过消息传递),只能在单独的上下文中运转。咦,其实他们甚至能在其中运转 WASM 而不是 JS。对我来说,它们只提供许多具有特殊特权的抽象层,没有人说这些层必须执行 JS。

Service Workers 除了 Web Workers 是浏览器上的机能,不是 JS 的专有机能。

设置开发环境

他们将采用 WASM,Go,JS 和 Docker(那个是可选的) 来进行开发。

如果您不了解Go,但了解 JS,请点选这里学习 Go,然后再回来继续阅读。让他们从 Go WASM Wiki 开始。

你能采用安装在电脑本地的 go 版本,在这里我采用 Docker 的 golang:1.12-rc 镜像。只需在此处为 go 校对器设置两个 WASM 标志。在 main.go 中建立两个单纯的hello world 进行测试。

$ GOOS=js GOARCH=wasm go build -o game.wasm main.go build_go: docker run –rm \ -v `pwd`/src:/game \ –env GOOS=js –env GOARCH=wasm \ golang:1.12-rc \ /bin/bash -c “go build -o /game/game.wasm /game/main.go; cp /usr/local/go/misc/wasm/wasm_exec.js /game/wasm_exec.js”

那时,让他们利用好 Go 团队提供的 wasm_exec.js标识符。标识符里的全局变量

总而言之,它应该看起来像这样:

<!DOCTYPEhtml> <html> <head> <meta charset=“utf-8” /> <meta name=“viewport” content=“width=device-width,initial-scale=1.0” /> <style>body{height:100%;width:100%;padding:0;margin:0;background-color:#000000;color:#FFFFFF;font-family:Arial,Helvetica,sans-serif}</style> <script type=“text/javascript” src=“./wasm_exec.js”></script> <script type=“text/javascript”> async function run(fileUrl) { try { const file = awaitfetch(fileUrl);const buffer = await file.arrayBuffer(); const go = new Go(); const { instance } = awaitWebAssembly.instantiate(buffer, go.importObject); go.run(instance); }catch (err) { console.error(err); } } setTimeout(() => run(“./game.wasm”)); </script> </head> <body></body> </html>

放码过来!(当然是 Go 的码)

要渲染他们的那个迷你游戏,<canvas> 那个标签应该足够了。他们能间接从 Go 标识符建立 DOM 结构和元素!那个 syscall/js文件 (包含在标准 Go 库中)为他们处理了与 DOM 可视化的方法。

main() 方法

我敢打赌,你很久没见过 main() 方法了 。

package main import ( // https://github.com/golang/go/tree/master/src/syscall/js “syscall/js” ) var ( // js.Value 能是任意的 JS 对象、类型或是构造函数window, doc, body, canvas, laserCtx, beep js.Value windowSizestruct{ w, h float64 } ) func main() { setup() } func setup() { window = js.Global() doc = window.Get(“document”) body = doc.Get(“body”) windowSize.h = window.Get(“innerHeight”).Float() windowSize.w = window.Get(“innerWidth”).Float() canvas = doc.Call(“createElement”, “canvas”) canvas.Set(“height”, windowSize.h) canvas.Set(“width”, windowSize.w) body.Call(“appendChild”, canvas) // 那个是肿块 Canvas 对象laserCtx = canvas.Call(“getContext”, “2d”) laserCtx.Set(“fillStyle”, “red”) // http://www.iandevlin.com/blog/2012/09/html5/html5-media-and-data-uri/ beep = window.Get(“Audio”).New(“data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU2LjI1LjEwMQAAAAAAAAAAAAAA/+NAwAAAAAAAAAAAAFhpbmcAAAAPAAAAAwAAA3YAlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw////////////////////////////////////////////AAAAAExhdmYAAAAAAAAAAAAAAAAAAAAAACQAAAAAAAAAAAN2UrY2LgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jYMQAEvgiwl9DAAAAO1ALSi19XgYG7wIAAAJOD5R0HygIAmD5+sEHLB94gBAEP8vKAgGP/BwMf+D4Pgh/DAPg+D5//y4f///8QBhMQBgEAfB8HwfAgIAgAHAGCFAj1fYUCZyIbThYFExkefOCo8Y7JxiQ0mGVaHKwwGCtGCUkY9OCugoFQwDKqmHQiUCxRAKOh4MjJFAnTkq6QqFGavRpYUCmMxpZnGXJa0xiJcTGZb1gJjwOJDJgoUJG5QQuDAsypiumkp5TUjrOobR2liwoGBf/X1nChmipnKVtSmMNQDGitG1fT/JhR+gYdCvy36lTrxCVV8Paaz1otLndT2fZuOMp3VpatmVR3LePP/8bSQpmhQZECqWsFeJxoepX9dbfHS13/////aysppUblm//8t7p2Ez7xKD/42DE4E5z9pr/nNkRw6bhdiCAZVVSktxunhxhH//4xF+bn4//6//3jEvylMM2K9XmWSn3ah1L2MqVIjmNlJtpQux1n3ajA0ZnFSu5EpX////uGatn///////1r/pYabq0mKT//TRyTEFNRTMuOTkuNaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MQxNIAAANIAcAAAKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg==”) }

看起来是不是很像 JS 标识符?

是的,这就是与 DOM 可视化所需的全部内容!那时只需要几个 get 方法除了调用函数即可。

在这一点上,我问自己:在某种程度上,我仍然在写 JS … 这怎么算是升级?因为他们还不能间接访问 DOM,所以他们必须(通过 JS)调用 DOM 来做任何人事情。想像呵呵如何用 JSX / React 来抽象化它。

实际上,已经能做到了,请期待我的下篇文章 。

「渲染」除了事件处理

间接采用 syscall / js 库,那个写法看起来有点像 ES5 的回调。但他们能够监听 DOM 事件,而且那些静态类型看起来很干净!

func main() { setup() // 在校对时声明渲染器 var renderer js.Func // 没有错,看起来很像 JS 的回调 renderer = js.FuncOf(func(this js.Value, args []js.Value) interface{} { updateGame() // 同时实现 60FPS 的动画 window.Call(“requestAnimationFrame”, renderer) return nil }) window.Call(“requestAnimationFrame”, renderer) // 让他们处理下 滑鼠/手势 点选事件 var mouseEventHandler js.Func = js.FuncOf(func(this js.Value, args []js.Value) interface{} { updatePlayer(args[0]) return nil }) window.Call(“addEventListener”, “pointerdown”, mouseEventHandler) }func updatePlayer(event js.Value) {} func updateGame() {}

日志记录、音频播放以及「异步」执行

在 Go 中,有两个惯例是把所有的函数都写成同步的方式,由调用者决定函数的执行与否是异步的。异步运转函数非常单纯,只要在前面加上 go 就行了!它采用自己的上下文建立两个线程,你仍然能将父级上下文绑定给它,不要担心哈。

func updatePlayer(event js.Value) { mouseX := event.Get(“clientX”).Float() mouseY := event.Get(“clientY”).Float()// `go` 关键性字是主要用以同时实现线程、异步、并行的机能 // TODO 与 Web Workers 的区别 // TODO 与 Service Workers 的区别 // https://gobyexample.com/goroutines go log(“mouseEvent”, “x”, mouseX, “y”, mouseY) // 下两个关键性点 ifisLaserCaught(mouseX, mouseY, gs.laserX, gs.laserY) {go playSound() } } // 不要以为我用了什么黑魔法,这里间接采用了 HTML5 的 API // https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement#Basic_usage func playSound(){ beep.Call(“play”) window.Get(“navigator”).Call(“vibrate”, 300) } // 这里主要用了 JS 的解构赋值语法 // 这里的 `…interface{}` 有点像 TS 的 `any` 语法 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters#Description func log(args …interface{}) { window.Get(“console”).Call(“log”, args…) }

让游戏一直跑下去!

该标识符建立两个非缓冲通道,并尝试从该通道接收数据。因为没有人向它发送任何人东西,它本质上是两个永久的阻塞操作方式,允许他们永远运转他们的程序。

func main() { // https://stackoverflow.com/a/47262117 // 建立空通道 runGameForever := make(chan bool) setup() // 尝试从空通道接收 // 由于没有人向它发送任何人数据,它本质上是两个永久阻塞操作方式 // 他们有两个 daeomon / service / background 程序 // 在 WASM 里,他们的游戏会一直运转 <-runGameForever }

更新游戏状况并终端肿块

这里没有状况管理,只有两个单纯的声明类型的结构体,它不允许在内部传递任何人不正确的值。

import ( “math” ) typegameStatestruct{ laserX, laserY, directionX, directionY, laserSize float64 } var ( // gs 处于最高范围,小于那个范围肿块 都能都能访问gs = gameState{laserSize:35, directionX: 3.7, directionY: -3.7, laserX: 40, laserY: 40} ) func updateGame() { // 边界判断 ifgs.laserX+gs.directionX > windowSize.w-gs.laserSize || gs.laserX+gs.directionX < gs.laserSize { gs.directionX = -gs.directionX }ifgs.laserY+gs.directionY > windowSize.h-gs.laserSize || gs.laserY+gs.directionY < gs.laserSize { gs.directionY = -gs.directionY }// 终端肿块 gs.laserX += gs.directionX gs.laserY += gs.directionY r/>// 清除画布 laserCtx.Call(“clearRect”, 0, 0, windowSize.w, windowSize.h) r/>//画两个肿块 laserCtx.Call(“beginPath”) laserCtx.Call(“arc”, gs.laserX, gs.laserY, gs.laserSize,0, math.Pi*2, false) laserCtx.Call(“fill”) laserCtx.Call(“closePath”) }r/> // 判断点选的点是不是在肿块 内部 func isLaserCaught(mouseX, mouseY, laserX, laserY float64) bool {r/> // 直接这样返回是不行的r/> // return laserCtx.Call(“isPointInPath”, mouseX, mouseY).Bool() > // 所以这里我通过勾股定理 来同时实现r/> // 同时我给 laserSize 属性的值加上 15,让斜升更容易点选 return (math.Pow(mouseX-laserX, 2) + math.Pow(mouseY-laserY, 2)) < math.Pow(gs.laserSize+15, 2) }

总结

事实上,WASM 仍然被指出是两个 [MVP](

https://hacks.mozilla.org/2018/10/webassembly -post- MVP -future/) (MAP),你能不必撰写一行 JS,就能建立两个像这样的游戏。惊不惊讶!CanIUse上 WASM 的支持已经是一片绿色了,没有人能阻止你去建立基于 WASM 的中文网站和应用领域。

你能组合所有你想要的词汇,像是把 JS 转成 WASM。最后,它们都将校对成 WASM 字节码。如果你需要在他们之间分享任何人东西,也没问题,因为它们能共享原始内存。

我担微软正在开发 Chromium 浏览器 除了 Firefox市场份额低于9%。这使谷歌在 WASM 上有了致命的切换能力。如果他们不愿意配合,大众可能永远不会知道有那个特性。

那时都有谁在用 WASM?

awesome-wasm#web-frameworks-libraries

同时,也有相当多的工程项目已经上了 WASM 的车了。我对 Spotify、Twitch 和 FigmaEWASM 更感兴趣。

Web3 时代的 WASM

那时,如果你想在智能手机上采用以太坊钱包(Ethereum wallet),你必须从应用领域商店下载两个类似于 http://Status.im 的终端端钱包 App,并且信任所有商家。

如果有两个先进的 Web App,能运转 geth (Go Ethereum 客户端),并且能在 WebRTC 上光速同步,这会怎么样?它能采用 Service Worker 来更新它的 WASM 标识符并在后台运转,能托管在 IPFS/Dat 上。

一些有用的关于 WASM 的文章、资源除了学习资料

WebAssembly is more than the webWebAssembly and Go: A look at the future 除了 HN commentsMozilla HacksHacker News 发布的文章WebAssembly architecture for Goawesome-wasm , awesome-wasm-langs , gowasm-experiments , WasmWeekly , WasmRocks , SPA with C++ , better DOM bindings for Go

感谢 twifkak在 Android Chrome 上对 Go 的优化!

原标题:The world’s easiest introduction to WebAssembly

原文链接:The world’s easiest introduction to WebAssembly – freeCodeCamp.org – Medium作者:Martin Olsansky (olso)

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务