使用 WebAssembly 和 Go 编写前端 Web 框架

JavaScript 前端框架无疑有助于突破以前在浏览器上下文中可能实现的界限。越来越复杂的应用程序已经出现在 React、Angular 和 VueJS 之类的基础之上,仅举几例,而且有一个众所周知的笑话是关于新的前端框架似乎每天都会出现。

然而,这种发展速度对于世界各地的开发者来说是一个非常好的消息。对于每一个新框架,我们都发现了更好的处理状态的方法,或者使用影子 DOM 之类的东西有效地渲染。

然而,最新的趋势似乎是朝着用户户 JavaScript 用另外的语言编写这些框架并将它们编译到 WebAssembly 中。多亏了Lin Clark 之类的人 ,我们开始看到 JavaScript 和 WebAssembly 通信方式的重大改进,而且随着 WebAssembly 开始在我们的生活中变得更加突出,我们无疑会看到更多重大的改进。

介绍

因此,在本教程中,我认为构建一个非常简单的前端框架的基础是一个好主意,该框架用 Go 编写,可编译成 WebAssembly。至少,这将包括以下功能:

我现在警告你,尽管这些将非常简单,而且离生产准备还差得很远。如果这篇文章有点受欢迎,我希望能继续推进它,并尝试构建一些满足半体面前端框架要求的东西。

Github:这个项目的完整源代码可以在这里找到: elliotforbes/go-webassembly-framework。如果您愿意为该项目做出贡献,请随意,我很乐意收到任何拉取请求!

初始点

好吧,让我们深入了解我们选择的编辑器并开始编码!我们要做的第一件事是创建一个非常简单的index.html作为前端框架的入口点:



  
    
    Go wasm
    
    
  
  
    

Oak WebAssembly Framework

你会注意到它们js在顶部导入了2 各种文件,这些文件允许我们执行我们完成的 WebAssembly 二进制文件。其中第一行大约 414 行,为了保持本教程的可读性,我建议您从这里下载:https : //github.com/elliotforbes/go-webassembly-framework/blob/master/examples/博客/静态/wasm_exec.js

第二个是我们的entrypoint.js文件。这将获取并运行lib.wasm 我们将很快构建的。

// static/entrypoint.js
const go = new Go();
WebAssembly.instantiateStreaming(fetch("lib.wasm"), go.importObject).then(
  result => {
    go.run(result.instance);
  }
);

最后,既然我们已经解决了这个问题,我们就可以开始研究一些 Go 代码了!创建一个名为的新文件main.go,该文件将包含我们 Oak Web 框架的入口点!

// main.go
package main
func main() {
    println("Oak Framework Initialized")
}

这很简单。我们创建了一个非常简单的 Go 程序,Oak Framework Initialized当我们打开我们的 web 应用程序时应该会打印出来。为了验证一切正常,我们需要使用以下命令编译它:

$ GOOS=js GOARCH=wasm go build -o lib.wasm main.go

我们应该构建我们的 Go 代码并输出我们在lib.wasm文件中引用的entrypoint.js文件。

太棒了,如果一切正常,那么我们就可以在浏览器中试用了!我们可以像这样使用一个非常简单的文件服务器:

// server.go
package main
import (
    "flag"
    "log"
    "net/http"
)
var (
    listen = flag.String("listen", ":8080", "listen address")
    dir    = flag.String("dir", ".", "directory to serve")
)
func main() {
    flag.Parse()
    log.Printf("listening on %q...", *listen)
    log.Fatal(http.ListenAndServe(*listen, http.FileServer(http.Dir(*dir))))
}

然后,您可以通过键入来服务您的应用程序,go run server.go并且您应该能够从:8080.

功能注册

好的,所以我们已经有了一个相当基本的打印语句,但是从总体上看,我不认为它可以作为一个 Web 框架。

让我们看看如何在 Go 中构建函数并注册这些函数,以便我们可以在index.html. 我们将创建一个新的实用程序函数,它将接受string作为我们函数名称的 a 以及它将映射到的 Go 函数。

将以下内容添加到现有main.go文件中:

// main.go
import "syscall/js"
// RegisterFunction
func RegisterFunction(funcName string, myfunc func(i []js.Value)) {
    js.Global().Set(funcName, js.NewCallback(myfunc))
}

所以,这就是事情开始变得更有用的地方。我们的框架现在允许我们注册函数,以便框架的用户可以开始创建他们自己的功能。

使用我们框架的其他项目可以开始注册他们自己的函数,这些函数随后可以在他们自己的前端应用程序中使用。

组件

所以,我想接下来我们需要考虑添加到我们的框架中的是组件的概念。基本上,我希望能够components/ 在使用它的项目中定义一个目录,并且在该目录中,我希望能够像一个home.go组件一样构建一个包含我主页所需的所有代码的组件。

那么,我们该怎么做呢?

好吧,React 倾向于提供具有功能的类,这些功能render()返回 HTML/JSX/您希望为所述组件呈现的任何代码。让我们窃取它并在我们自己的组件中使用它。

我本质上希望能够在使用这个框架的项目中做这样的事情:

package components
type HomeComponent struct{}
var Home HomeComponent
func (h HomeComponent) Render() string {
    return "

Home Component

" }

因此,在我的components包中,我定义了HomeComponent一个Render()返回 HTML的 方法。

为了将组件添加到我们的框架中,我们将保持简单,只需定义一个interface我们随后定义的任何组件都必须遵守的。components/comopnent.go在我们的 Oak 框架中创建一个名为的新文件:

// components/component.go
package component
type Component interface {
    Render() string
}

如果我们想为各种组件添加新功能会发生什么?好吧,这使我们能够做到这一点。我们可以oak.RegisterFunction在init组件的函数中使用调用来注册我们想要在组件中使用的任何函数!

package components
import (
    "syscall/js"
    "github.com/elliotforbes/go-webassembly-framework"
)
type AboutComponent struct{}
var About AboutComponent
func init() {
    oak.RegisterFunction("coolFunc", CoolFunc)
}
func CoolFunc(i []js.Value) {
    println("does stuff")
}
func (a AboutComponent) Render() string {
    return `

About Component Actually Works

` }

当我们将它与路由器结合使用时,我们应该能够看到我们HTML正在呈现到我们的页面,并且我们应该能够单击调用的那个按钮, coolFunc()它将does stuff在我们的浏览器控制台中打印出来!

太棒了,让我们看看我们现在如何构建一个简单的路由器。

构建路由器

好的,所以我们已经了解了components在我们的 Web 框架内的概念。我们差不多完成了吧?

不完全是,接下来我们可能需要的是一种在不同组件之间导航的方法。大多数框架似乎都有一个

特殊的属性 id,它们绑定到并在其中渲染所有组件,因此我们将在 Oak 中采取相同的策略。

让我们router/router.go在我们的橡木框架中创建一个文件,这样我们就可以开始破解了。

在此范围内,我们希望将string路径映射到组件,我们不会进行任何 URL 检查,我们现在将所有内容都保存在内存中以保持简单:

// router/router.go
package router
import (
    "syscall/js"
    "github.com/elliotforbes/go-webassembly-framework/component"
)
type Router struct {
    Routes map[string]component.Component
}
var router Router
func init() {
    router.Routes = make(map[string]component.Component)
}

因此,在此范围内,我们创建了一个新Router结构,其中包含Routes 字符串到我们在上一节中定义的组件的映射。

在我们的框架中,路由不是一个强制性的概念,我们希望用户选择他们希望何时初始化一个新的路由器。因此,让我们创建一个新函数,该Link函数将注册一个函数并将我们地图中的第一个路由绑定到我们的

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享