preface

As we know, Go has become the preferred back-end development language for containerized applications in the current era of containerization. Today, the most egregious container-managed choreography system, K8S, is used by almost every major cloud vendor. K8s is developed by Google using the GO language. Now, go can be used to develop front-end languages, and there is a sense that everything that can be done in GO will eventually be done in Go. This article is designed to introduce you to front-end development using go.

Install the GO development environment

First, you need to download and install Go. After the installation is complete, the console executes the following command to confirm whether the go installation is successful.

go version
Copy the code

Isn’t it easy to prove that your environment has been installed if it works?

Why can GO be used for front-end development

Go has added support for WebAssembly experience in version 1.11. The current version of Go has reached version 1.14, which can be said that WebAssembly support is very good. For more information about WebAssembly in Go, see the official wiki: github.com/golang/go/w… .

Because the code written by Go can be converted to WebAssembly, which is a binary format language that runs in any modern browser, it is possible to develop front-end applications using Go.

A simple demo start

Look directly at the code:

For example, your HTML page would look like this:


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    <button id="test">test</button>
</body>
</html>
Copy the code

In the page, there is a button element whose ID is “test”.

Let’s see how we get this element in Go.

package main

import (
	"syscall/js"
)

js.Global().Get("document").Call("getElementById"."test")

Copy the code

In the code above, we call the methods provided in the syscall/js package to get the Document object and call the Document getElementById method to get the Button element in our HTML page. At this point, nothing is visible. After we try to get the button element, change the button text to “changed by Go”.

btn := js.Global().Get("document").Call("getElementById"."test")
btn.Set("innerHTML"."changed by go")
Copy the code

After you write it, the code will look something like this. So now that I’ve done a basic demo, how do I test it?

First of all, we need to compile the go code into WebAssembly, and then we need to use a JS library provided by Go. This is used to call the WebAssembly generated by GO compilation in JS, and then execute the code logic inside.

First let’s copy the JS library provided by Go into the directory.

In the project root directory, run the following command:

cp $(go env GOROOT)/misc/wasm/wasm_exec.js .
Copy the code

When it’s done, it looks something like this.

Then we need to compile the GO code into WASM format.

Use the following command to compile the GO code into WASM format.

GOOS=js GOARCH=wasm go build -o main.wasm main.go
Copy the code

GOOS and GOARCH are two environment variables that need to be explained here. In GO, you can compile the GO code into target results for each platform. For example, GOOS can be Windows or Linux. In this case, you can also specify js.

GOARCH represents the system architecture, which can be specified as AMD64 or 386, etc. In this case, you can also specify wASM.

After executing the command above, we can see that there is a wASM file in the directory.

That’s it. That’s it. We need to import the JS library provided by Go into the HTML, and then use the main.wasm we just compiled.

Modify the HTML as follows:


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    <button id="test"></button>
</body>
<script src="./wasm_exec.js"></script>
<script>
    const go = new Go()
    WebAssembly.instantiateStreaming(fetch('app.wasm'), go.importObject)
        .then(result= > go.run(result.instance))
</script>
</html>
Copy the code

The above code, WebAssembly instantiateStreaming method directly from streaming the underlying source compilation and instantiate WebAssembly module. This is a very efficient optimization for loading WASM code.

Fetch goes without saying.

Go.importobject is an object that is imported into wASM modules so that js objects can be accessed in WASM modules.

In this case, go.importobject looks something like this:

Look at the source code in the JS library provided by Go, which has comments.

ImportObject is used mainly to call JS code in WASM files (calling jS-provided methods in WASM) and to handle SP (Stack Pointer) changes in GO.

With the above code ready, we can start an HTTP service. Http-server is recommended, github.com/http-party/…

Once the startup is complete, visit http://127.0.0.1:8080/

As you can see, the correct file is still in our WASM file, and the code we wrote with GO is executed, changing the text of button to “changed by Go”.

Add click event handling to buttons

In the code above, we just changed the text of the button when we accessed it, and we didn’t do anything else. Now let’s see if we can add a click event to the button.

First we need to declare a function that acts as a callback for the click event.

func main() {
	btn := js.Global().Get("document").Call("getElementById"."test")

	callback := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		fmt.Println(this)
		fmt.Println(args)
		fmt.Println("button clicked")
		return nil
	})

	btn.Set("innerHTML"."changed by go")
	btn.Call("addEventListener"."click", callback)

}

Copy the code

The code above, first of all, by calling the js package FuncOf created a used to call the function in js, inside FuncOf parameters, we can see that define the callback function, this function has two parameters, the first parameter represents your js invoked when this object, call when the parameters of the second parameter said.

After adding the code above, regenerate the WASM file, refresh the page, click the button, and see if the “Button clicked” string is output.

After clicking “Finish”, I found an error, indicating that the go program has exited, why?

FuncOf: FuncOf: FuncOf: FuncOf: FuncOf: FuncOf: FuncOf: FuncOf: FuncOf: FuncOf: FuncOf; FuncOf: FuncOf; FuncOf: FuncOf; FuncOf: FuncOf; FuncOf: FuncOf; FuncOf; FuncOf; FuncOf; FuncOf; FuncOf; FuncOf; FuncOf; FuncOf; FuncOf; FuncOf; FuncOf; FuncOf

To resolve this error, we need to ensure that the main thread does not exit. Modify the code as follows:

func main() {
	btn := js.Global().Get("document").Call("getElementById"."test")

	signal := make(chan int)

	var callback js.Func
	callback = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		fmt.Println(this)
		fmt.Println(args)
		fmt.Println("button clicked")
		return nil
	})

	btn.Set("innerHTML"."changed by go")
	btn.Call("addEventListener"."click", callback)

	<- signal
}
Copy the code

Here we add a variable of type channel. For more information about channel, you can check the official documentation, or read my previous go study notes (juejin.cn/post/684490…

Here use channel is mainly used to prevent the main thread of the go out, in the last sentence, < – signal, said to get the data from the signal channel, but we can see that there is no place for this channel into data, so the main thread will always be blocked here, in this way, our event callback will perform properly.

Take a look at the result of normal execution:

As you can see, the click event we registered for the button is firing normally, and the callback function is executing normally.

If you look closely at the code above, you will find that using Go to manipulate the DOM is a bit cumbersome. For example, each time you retrieve a DOM element, you need to:

js.Global().Get("document").Call("getElementById"."test")
Copy the code

Also, we can only call dom methods like this:

btn.Call("addEventListener"."click", callback)
Copy the code

Here, the method name is used as an argument, which is easy to miswrite.

Therefore, the community has someone to encapsulate these operations, such as: godoc.org/honnef.co/g…

This library.

Look at the document, this time found that we usually use JS to operate dom writing is more consistent.

conclusion

Go has become more and more popular in China in recent years, especially after the development of Cloud and container. Critically, Go not only performs well, but also consumes very little memory, etc., and most new backend projects are currently being rewritten using Go.

Note: First of all, there is absolutely no need for a common front-end application to develop WASM using Go, because your project scenario may not need WASM, so imposing wASM will bring no benefit other than increased complexity. But for special scenarios where you need to use WASM, you’ll feel better if you use Go. Wasm usage scenarios are as follows:

Webassembly.org.cn/docs/use-ca…

Blog.logrocket.com/webassembly…

References:

Developer.mozilla.org/zh-CN/docs/…

Dev. To/talentlessg…

Github.com/golang/go/w…

Golang.org/pkg/syscall…