我想在 Go 中运行一个返回纯 HTML 的应用程序,
我决定使用 Cloudflare Workers,它可以将其转换为 Wasm 并部署它。
我推荐 sumai/workers 的模板,用于将 Go 应用程序部署到 Cloudflare Workers。
sumai/workers:Go 包以在 Cloudflare Workers 上运行 HTTP 服务器。
首先,让我们以简单的方式使用文本/模板进行构建。
❯ ls -lh . /build total 15656 -rwxr-xr-x 1 ergofriend staff 7.6M 8 8 20:12 app.wasm -rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:12 shim.mjs -rw-r--r-- 1 ergofriend staff 16K 8 8 20:12 wasm_exec.js -rw-r--r-- 1 ergofriend staff 160B 8 8 20:12 worker.mjs
差不多 8 MB。
https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits
Cloudflare Workers 根据计划有工作人员大小限制。
免费插槽为1MB,付费插槽($5~)为10MB。
即使有基于压缩后大小的最终限制,也很难满足从 8MB 开始的免费配额。
所以我决定切换到 TinyGo,它应该是为 WebAssembly (Wasm) 设计的。
构建后,大小约为 0.75 MB,似乎适合免费框架。
❯ ls -lh . /build total 1160 -rwxr-xr-x 1 ergofriend staff 556K 8 8 20:23 app.wasm -rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:23 shim.mjs -rw-r--r-- 1 ergofriend staff 15K 8 8 20:23 wasm_exec.js -rw-r--r-- 1 ergofriend staff 160B 8 8 20:23 worker.mjs
但这就是悲剧发生的地方。
当我尝试访问构建的应用程序时,出现错误。
[wrangler:inf] GET / 200 OK (35ms) ✘ [ERROR] Uncaught (in response) RuntimeError: unreachable at main.runtime._panic (wasm://wasm/main-0022bc46:wasm-function[35]:0x2b4a) at main.(*text/template.state).evalField (wasm://wasm/main-0022bc46:wasm-function[540]:0x6c5f4) at main.(*text/template.state).evalFieldChain (wasm://wasm/main-0022bc46:wasm-function[531]:0x697fe) at main.(*text/template.state).evalFieldNode (wasm://wasm/main-0022bc46:wasm-function[530]:0x6959a) at main.(*text/template.state).evalPipeline (wasm://wasm/main-0022bc46:wasm-function[535]:0x6a1d2) at main.(*text/template.state).walk (wasm://wasm/main-0022bc46:wasm-function[569]:0x72cdd) at main.(*text/template.state).walk (wasm://wasm/main-0022bc46:wasm-function[569]:0x730b0) at main.main (wasm://wasm/main-0022bc46:wasm-function[261]:0x2ac52) at main.(net/http.HandlerFunc).ServeHTTP (wasm://wasm/main-0022bc46:wasm-function[463]:0x5973a) at main.interface:{ServeHTTP:func:{named:net/http.ResponseWriter,pointer:named:net/http.Request}{}}.ServeHTTP$invoke (wasm://wasm/main-0022bc46:wasm-function[459]:0x56f72) panic: unimplemented: (reflect.Value).MethodByName()
在 template.ExecuteTemplate() 中传递模板变量时调用的方法 MethodByName 尚未实现。
由于 TinyGo 是原始版本的子集,因此有许多不支持的功能,
我发现文本/模板正在调用不受支持的方法。
https://github.com/tinygo-org/tinygo/blob/1154212c15e6e97048e122068730dab5a1a9427f/src/reflect/type.go#L1086-L1088
现在,我想冷静地为 TinyGo 说声好话,但这一次我要寻找一个快速的替代方案。
所以我正在寻找另一个模板引擎来替换文本/模板,并遇到了templ。
请参阅 https://templ.guide 上的用户文档
构建本地版本。
❯ ls -lh . /build total 15656 -rwxr-xr-x 1 ergofriend staff 7.6M 8 8 20:12 app.wasm -rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:12 shim.mjs -rw-r--r-- 1 ergofriend staff 16K 8 8 20:12 wasm_exec.js -rw-r--r-- 1 ergofriend staff 160B 8 8 20:12 worker.mjs
❯ ls -lh . /build total 15656 -rwxr-xr-x 1 ergofriend staff 7.6M 8 8 20:12 app.wasm -rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:12 shim.mjs -rw-r--r-- 1 ergofriend staff 16K 8 8 20:12 wasm_exec.js -rw-r--r-- 1 ergofriend staff 160B 8 8 20:12 worker.mjs
构建并安装当前版本。
❯ ls -lh . /build total 1160 -rwxr-xr-x 1 ergofriend staff 556K 8 8 20:23 app.wasm -rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:23 shim.mjs -rw-r--r-- 1 ergofriend staff 15K 8 8 20:23 wasm_exec.js -rw-r--r-- 1 ergofriend staff 160B 8 8 20:23 worker.mjs
使用 goreleaser 使用 goreleaser 构建命令行二进制文件。
❯ ls -lh . /build total 15656 -rwxr-xr-x 1 ergofriend staff 7.6M 8 8 20:12 app.wasm -rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:12 shim.mjs -rw-r--r-- 1 ergofriend staff 16K 8 8 20:12 wasm_exec.js -rw-r--r-- 1 ergofriend staff 160B 8 8 20:12 worker.mjs
使用本地版本运行 templ generated。
❯ ls -lh . /build total 1160 -rwxr-xr-x 1 ergofriend staff 556K 8 8 20:23 app.wasm -rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:23 shim.mjs -rw-r--r-- 1 ergofriend staff 15K 8 8 20:23 wasm_exec.js -rw-r--r-- 1 ergofriend staff 160B 8 8 20:23 worker.mjs
运行 Go 测试。
❯ ls -lh . /build total 15656 -rwxr-xr-x 1 ergofriend staff 7.6M 8 8 20:12 app.wasm -rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:12 shim.mjs -rw-r--r-- 1 ergofriend staff 16K 8 8 20:12 wasm_exec.js -rw-r--r-- 1 ergofriend staff 160B 8 8 20:12 worker.mjs
运行 Go 测试。
❯ ls -lh . /build total 1160 -rwxr-xr-x 1 ergofriend staff 556K 8 8 20:23 app.wasm -rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:23 shim.mjs -rw-r--r-- 1 ergofriend staff 15K 8 8 20:23 wasm_exec.js -rw-r--r-- 1 ergofriend staff 160B 8 8 20:23 worker.mjs
跑…
这是 TEMPL 的摘要。
编写一个独特的程序并不难,但是可以像 templ ≈ Go JSX 那样写。
❯ ls -lh . /build total 15656 -rwxr-xr-x 1 ergofriend staff 7.6M 8 8 20:12 app.wasm -rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:12 shim.mjs -rw-r--r-- 1 ergofriend staff 16K 8 8 20:12 wasm_exec.js -rw-r--r-- 1 ergofriend staff 160B 8 8 20:12 worker.mjs
看起来很熟悉的写作风格。
❯ ls -lh . /build total 1160 -rwxr-xr-x 1 ergofriend staff 556K 8 8 20:23 app.wasm -rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:23 shim.mjs -rw-r--r-- 1 ergofriend staff 15K 8 8 20:23 wasm_exec.js -rw-r--r-- 1 ergofriend staff 160B 8 8 20:23 worker.mjs
使用它时,您可以逐个组件地调用由 templ generated 从 DSL 生成的 Go 函数。
[wrangler:inf] GET / 200 OK (35ms) ✘ [ERROR] Uncaught (in response) RuntimeError: unreachable at main.runtime._panic (wasm://wasm/main-0022bc46:wasm-function[35]:0x2b4a) at main.(*text/template.state).evalField (wasm://wasm/main-0022bc46:wasm-function[540]:0x6c5f4) at main.(*text/template.state).evalFieldChain (wasm://wasm/main-0022bc46:wasm-function[531]:0x697fe) at main.(*text/template.state).evalFieldNode (wasm://wasm/main-0022bc46:wasm-function[530]:0x6959a) at main.(*text/template.state).evalPipeline (wasm://wasm/main-0022bc46:wasm-function[535]:0x6a1d2) at main.(*text/template.state).walk (wasm://wasm/main-0022bc46:wasm-function[569]:0x72cdd) at main.(*text/template.state).walk (wasm://wasm/main-0022bc46:wasm-function[569]:0x730b0) at main.main (wasm://wasm/main-0022bc46:wasm-function[261]:0x2ac52) at main.(net/http.HandlerFunc).ServeHTTP (wasm://wasm/main-0022bc46:wasm-function[463]:0x5973a) at main.interface:{ServeHTTP:func:{named:net/http.ResponseWriter,pointer:named:net/http.Request}{}}.ServeHTTP$invoke (wasm://wasm/main-0022bc46:wasm-function[459]:0x56f72) panic: unimplemented: (reflect.Value).MethodByName()
go run ./get-version > .version cd cmd/templ go build
生成的组件的函数继承了模板中定义的参数类型,因此可以安全地调用。
与某些 ExecuteTemplate 不同,这是安全的。
https://marketplace.visualstudio.com/items?itemName=a-h.templ
语法突出显示和 LSP 补全将非常有用。
为了确定,我查了一下,发现它只依赖于reflect的TypeOf,它已经在TinyGo中实现了。
https://github.com/tinygo-org/tinygo/blob/1154212c15e6e97048e122068730dab5a1a9427f/src/reflect/type.go#L494-L500
现在让我们尝试使用 templ。
该建筑似乎有足够的空间。
gomod2nix
这是实际应用。
goworkers-demo.ergofriend.workers.dev
访问时可以正常返回HTML。
<span class="pl-c"># Remove templ from the non-standard ~/bin/templ path</span> <span class="pl-c"># that this command previously used.</span> rm -f ~/bin/templ <span class="pl-c"># Clear LSP logs.</span> rm -f cmd/templ/lspcmd/*.txt <span class="pl-c"># Update version.</span> go run ./get-version > .version <span class="pl-c"># Install to $GOPATH/bin or $HOME/go/bin</span> cd cmd/templ && go install
最终部署的大小也是187.91 KiB,因此有足够的空间来扩展应用程序。
此验证保留在此存储库中。
ergofriend/goworkers-demo
本文是日文翻译。
https://ergofriend.hatenablog.com/entry/2024/08/08/230603
以上是Go 的模板引擎'templ”很方便(它也适用于 TinyGo)的详细内容。更多信息请关注PHP中文网其他相关文章!