Skip to content

mfrachet/go-vdom-wasm

Repository files navigation

go-vdom-wasm

This package allows to build frontend applications in Go targeting Webassembly. It provides a conveniant syntax close to the hyperscript one in the JavaScript lands and aims to provide efficient way to deal with the DOM. If you're not familiar the Virtual DOM notion, I suggest you take a look at this document.

⚠️ This package is not production ready. Use at your own risks. A Todos section is available if you want to take part of the projet and try to make it better. You can also check the Go + WASM wiki to get a better overview of the Webassembly support for the Go language.

This module has been inspired by the awesome work of @mbasso on https://github.com/mbasso/asm-dom.

Want to see go-vdom-wasm in action?

Ready to start?

Before starting

You have to make some little configuration that are well explained in the Go + WASM wiki - Getting started guide.

If you prefer a quickest solution, you can also rely on the go-wasm-cli utility. It allows to create a WASM application in one command line and to have hot reload while coding.

Install the module

In your favorite terminal

$ GOOS=js GOARCH=wasm go get github.com/mfrachet/go-vdom-wasm

Usage

Let's define the root of our go-vdom-wasm application. Everything that the library will work on is inside that div:

<!-- index.html-->

<div id="app"></div>

Let's now Patch that node with a virtual one to make something happen:

// main.go

rootNode := vn.H("div", "Hello world")

vn.Patch("#app", rootNode)

Passing attributes

In this example, we set a class="navbar" to the underlying ul element

vn.H("ul", &vn.Props{"class": "navbar"}, vn.Children{
	vn.H("li", "First item"),
	vn.H("li", "Second item"),
}),

The DOM representation of the previous snippet is:

<ul class="navbar">
  <li>First item</li>
  <li>Second item</li>
</ul>

Handling events

The Ev structure allows to pass functions to handle specific events.

In this example, we set a click event on of the the li:

func handleClick(args []js.Value){
	fmt.Println("I've been clicked!")
}

func main() {
    rootNode := vn.H("ul",  &vn.Props{"class": "navbar"}, vn.Children{
        vn.H("li", &vn.Ev{"click": handleClick}, "First item"),
        vn.H("li", "Second item"),
	})

	vn.patch("#app", rootNode)
}

This module binds the event using the addEventListener API

⚠️ While using event handler, it's necessary to add en empty select{} at the end of the main function

Using keys in list of item

For efficient diffing while you're relying a list of items, you can rely on a key parameter:

vn.H("div", vn.Children{
		vn.H("span", attrsChecked, "Some text"),
	}, &vn.Key{"UNIQUE KEY"})

The key aims to identifiy a Node. It has to be unique and will ensure a quick comparison before handling the computation. The idea is the same (less advanced actually, but here's the idea) as the React key system.

Todos

  • Add more tests 🤦‍♀️ 🤦‍♂️
  • Find a better solution to compare two virtual nodes
  • Ensure consistency and avoid unecessary rerendering that sometimes occur for the same DOMNodes ❓
  • Make a better abstraction concerning the reconciler (it's a really first step)
  • Rely on Go good practices (reference passing, better algorithms and so forth...)
  • Generating the API doc and adding decent comment in code
  • Find a way to abstract the []js.Value passed in every event handler