vugu

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2019 License: MIT Imports: 23 Imported by: 35

README

Vugu

Go Report Card Travis CI GoDoc stability-experimental

Vugu is an experimental library for web UIs written in Go and targeting webassembly. Guide and docs at https://www.vugu.org. Godoc at https://godoc.org/github.com/vugu/vugu.

If you've ever wanted to write a UI not in JS but pure Go... and run it in your browser, right now... That (experimental;) future is here!

Introducing Vugu (pronounced /ˈvuː.ɡuː/), a VueJS-inspired library in Go targeting wasm.

No node. No JS. No npm. No node_modules folder competing with your music library for disk space.

Updates ♨

  • 2019-09-08 Implemented ModTracker to keep track of changes to components and their data (this is also the beginning of Vuex-like functionality but without wrappers or events). Worked out the lifecycle of components in much more detail and work in progress on nested components implementation (component-refactor branch currently broken, but finally the core nested component functionality is going in - hopefully will finish next week).
  • 2019-09-07 Updated everything for Go 1.13, including both master and component-refactor branches, Vugu's js wrapper package, site documentation.
  • 2019-09-01 On component-refactor branch: Form element values and other related data now available on DOMEvent, .prop= syntax implemented, various cleanup, imports are deduplicated automatically now, started on nested component implementation and all of that craziness.
  • 2019-08-25 CSS now supported on component-refactor branch, including in full-HTML mode, working sample that pulls in Bootstrap CSS. Vugu's js wrapper package copied to master and made available.
  • 2019-08-18 Full HTML (root component can start with <html> tag) now supported on component-refactor branch, updated CSS and JS support figured out and implementation in-progress
  • 2019-08-12 Refactored DOM event listener code in-progress, event registration/deregistration works(-ish), filling out the remaining functionality to provide event summary, calls like preventDefault(), etc.
  • 2019-08-04 Some basic stuff in there on the DOM syncing rewrite and the new instruction workflow from VGNode -> binary encoded to raw bytes in Go -> read with DataView in JS -> DOM tree manipulation. With the pattern in place the rest should get easier.
  • 2019-07-28 Making some hard choices on how to do DOM syncing in a performant and reliable way. https://github.com/vugu/vugu/wiki/DOM-Syncing-Instructions
  • 2019-07-20 Some design info on how "data binding" (hashing actually) will work in Vugu: https://github.com/vugu/vugu/wiki/Data-Hashing-vs-Binding
  • 2019-07-16 Vugu has a logo! https://www.instagram.com/p/Bz3zmtYAYcM/ Good things are in the works, the plan is to get a bunch of much-awaited updates pushed to master before the end of the month.
  • 2019-05-19 Refactor still in progress - this is the cleaned-up architecture concept: https://github.com/vugu/vugu/wiki/Architecture-Overview
  • 2019-04-07 The Vugu Playground is up at: https://play.vugu.org/
  • 2019-04-05 Thanks to @erinpentecost, vugufmt is now available and provides gofmt-like functionality on your .vugu files. ("go get github.com/vugu/vugu/cmd/vugufmt && go install github.com/vugu/vugu/cmd/vugufmt")
  • 2019-04-05 The component playground should be available soon; followed by some internal work to properly handle nested components in a type-safe way; then probably a router...

Join the conversation: Gophers on Slack, channel #vugu

Highlights

  • Runs in-browser using WebAssembly
  • Single-file components
  • Vue-like markup syntax
  • Write idiomatic Go code
  • Rapid prototyping
  • ~3 minute setup
  • Standard Go build tools

Start

Get started: http://www.vugu.org/doc/start

Still a work in progress, but a lot of things are already functional. Some work really well.

Abbreviated Roadmap

  • Single-file components (looks similar to .vue); .vugu -> .go code generation.
  • Includes CSS in components.
  • Basic flow control with vg-if, vg-for and output with vg-html.
  • Dynamic attributes with <tag :prop='expr'>.
  • Nested components with dynamic properties <my-custom-component :prop='expr'>.
  • Efficiently syncs to browser DOM.
  • Static HTML output (great for tests).
  • DOM Events, click, etc.
  • Basic data hashing to avoid unnecessary computation where possible.
  • Basic dev and prod server tooling, easy to get started
  • Rewrite everything so it is not so terrible internally and stablize existing features to avoid breaking things moving forward as much as possible (big task, but well in-progress!)
  • URL Router
  • Server-side rendering (HTML generation works, needs URL Router to make it usable)
  • Performance optimizations
  • Go-only component events
  • And much more...

Notes

It's built more like a library than a framework. While Vugu does do code generation for your .vugu component files, (and will even output a default main_wasm.go for a new project and build your program automatically upon page refresh), fundamentally you are still in control. Overall program flow, application wiring and initialization, the render loop that keeps the page in sync with your components - you have control over all of that. Frameworks call your code. Vugu is a library, your code calls it (even if Vugu generates a bit of that for you in the beginning to make things easier). One of the primary goals for Vugu, when it comes to developers first encountering it, was to make it very fast and easy to get started, but without imposing unnecessary limitations on how a project is structured. Go build tooling (and now the module system) is awesome. The idea is to leverage that to the furthest extent possible, rather than reprogramming the wheel.

So you won't find a vugu command line tool that runs a development server, instead you'll find in the docs an appropriate snippet of code you can paste in a file and go run yourself. For the code generation while there is an http.Handler that can do this upon page refresh, you also can (and should!) run vugugen via go generate. There are many small decisions in Vugu which follow this philosophy: wherever reasonably possible, just use the existing mechanism instead of inventing anew. And keep doing that until there's proof that something else is really needed. So far it's been working well. And it allows Vugu to focus on the specific things it brings to the table.

Documentation

Overview

Package vugu provides core functionality including vugu->go codegen and in-browser DOM syncing running in WebAssembly. See http://www.vugu.org/

Since Vugu projects can have both client-side (running in WebAssembly) as well as server-side functionality many of the items in this package are available in both environments. Some however are either only available or only generally useful in one environment.

Common functionality includes the ComponentType interface, and ComponentInst struct corresponding to an instantiated componnet. VGNode and related structs are used to represent a virtual Document Object Model. It is based on golang.org/x/net/html but with additional fields needed for Vugu. Data hashing is performed by ComputeHash() and can be customized by implementing the DataHasher interface.

Client-side code uses JSEnv to maintain a render loop and regenerate virtual DOM and efficiently synchronize it with the browser as needed. DOMEvent is a wrapper around events from the browser and EventEnv is used to synchronize data access when writing event handler code that spawns goroutines. Where appropriate, server-side stubs are available so components can be compiled for both client (WebAssembly) and server (server-side rendering and testing).

Server-side code can use ParserGo and ParserGoPkg to parse .vugu files and code generate a corresponding .go file. StaticHTMLEnv can be used to generate static HTML, similar to the output of JSEnv but can be run on the server. Supported features are approximately the same minus event handling, unapplicable to static output.

Index

Examples

Constants

View Source
const (
	ErrorNode    = VGNodeType(html.ErrorNode)
	TextNode     = VGNodeType(html.TextNode)
	DocumentNode = VGNodeType(html.DocumentNode)
	ElementNode  = VGNodeType(html.ElementNode)
	CommentNode  = VGNodeType(html.CommentNode)
	DoctypeNode  = VGNodeType(html.DoctypeNode)
)

Available VGNodeTypes.

Variables

View Source
var DOMEventStub = &DOMEvent{}

DOMEventStub is used as a value to mean "replace this with the actual event that came in". In .vugu files, "event" points to this.

Functions

func ComputeHash

func ComputeHash(i interface{}) uint64

ComputeHash performs data hashing. It walks your data structure and hashes the information as it goes. It uses xxhash internally and returns a uint64. It is intended to be both fast and have good hash distribution to avoid collision-related bugs. Maps are sorted by key and then both keys and values are hashed. Nil values are skipped. Behavior is undefined for circular references via interface or pointer. Will call DataHash() method if present (see DataHasher) on a type. Otherwise will walk the data and find primitive values and hash them byte for byte. No guarantee is made about the exact hash algorithm used or how type information is or is not hashed - only that the same data structure should consistently hash to the same value and changing any value in the data tree should change the hash.

Example
type D struct {
	A string
}
data1 := &D{A: "test1"}
data2 := &D{A: "test2"}

fmt.Println(ComputeHash(data1) == ComputeHash(data1))
fmt.Println(ComputeHash(data1) == ComputeHash(data2))
Output:

true
false

func RegisterComponentType

func RegisterComponentType(tagName string, ct ComponentType)

RegisterComponentType can be called during init to register a component and make it available by default to the rest of the application. A program may retrieved these by calling RegisteredComponentTypes() or it may choose to form its own set. RegisterComponentType() just makes it available in case that's useful. It is good practice for components to register themselves in an init function.

Types

type ComponentInst

type ComponentInst struct {
	Type ComponentType // type of component
	Data interface{}   // data as returned by NewData
}

ComponentInst corresponds to a ComponentType that has been instantiated.

func New

func New(ct ComponentType, props Props) (*ComponentInst, error)

New instantiates a component based on its ComponentType and a set of Props. It essentially just calls NewData and returns a ComponentInst.

type ComponentType

type ComponentType interface {
	BuildVDOM(data interface{}) (vdom *VGNode, css *VGNode, reterr error) // based on the given data, build the VGNode tree
	NewData(props Props) (interface{}, error)                             // initial data when component is instanciated
}

ComponentType is implemented by any type that wants to be a component. The BuildVDOM method is called to generate the virtual DOM for a component; and this method is usually code generated (by ParserGo) from a .vugu file. NewData provides for specific behavior when a component is initialized.

type ComponentTypeMap

type ComponentTypeMap map[string]ComponentType

ComponentTypeMap is a map of the component tag name to a ComponentType.

func RegisteredComponentTypes

func RegisteredComponentTypes() ComponentTypeMap

RegisteredComponentTypes returns a copy of the map of registered component types.

type DOMEvent

type DOMEvent struct {
}

DOMEvent is an event originated in the browser. It wraps the JS event that comes in. It is meant to be used in WebAssembly but some methods exist here so code can compile server-side as well (although DOMEvents should not ever be generated server-side).

func (*DOMEvent) EventEnv

func (e *DOMEvent) EventEnv() EventEnv

EventEnv returns the EventEnv for the current environment and allows locking and unlocking around modifications. See EventEnv struct. Non-wasm implementation returns nil.

func (*DOMEvent) JSEvent

func (e *DOMEvent) JSEvent() interface{}

JSEvent this returns a js.Value in wasm that corresponds to the event object. Non-wasm implementation returns nil.

func (*DOMEvent) JSEventThis

func (e *DOMEvent) JSEventThis() interface{}

JSEventThis returns a js.Value in wasm that corresponds to the "this" variable from the event callback. It is the DOM element the event was attached to. Non-wasm implementation returns nil.

func (*DOMEvent) PreventDefault

func (e *DOMEvent) PreventDefault()

PreventDefault calls preventDefault() on the JS event object. Non-wasm implementation is nop.

func (*DOMEvent) RequestRender

func (e *DOMEvent) RequestRender()

RequestRender tells the environment it should be re-rendered as soon as possible (as soon as it can obtain a read lock). Non-wasm implementation is nop.

type DOMEventHandler

type DOMEventHandler struct {
	ReceiverAndMethodHash uint64        // hash value corresponding to the method and receiver, so we get a unique value for each combination of method and receiver
	Method                reflect.Value // method to be called, with receiver baked into it if needed (see reflect/Value.MethodByName)
	Args                  []interface{} // arguments to be passed in when calling (special case for eventStub)
}

DOMEventHandler is created in BuildVDOM to represent a method call that is performed to handle an event.

type DataHasher

type DataHasher interface {
	DataHash() uint64
}

DataHasher can be implemented by types to override the hashing behavior. ComputeHash() will call DataHash() on an instance of a type if the method is present. Useful for providing fast and stable hashing for structures that are large but only require a small amount data to be examined to determine if changed, or are already hashed internally, etc.

type Env

type Env interface {
	RegisterComponentType(tagName string, ct ComponentType)
	Render() error
}

Env specifies the common methods for environment implementations. See JSEnv and StaticHtmlEnv for implementations.

type EventEnv

type EventEnv interface {
	Lock()         // acquire write lock
	UnlockOnly()   // release write lock
	UnlockRender() // release write lock and request re-render

	RLock()   // acquire read lock
	RUnlock() // release read lock
}

EventEnv provides locking mechanism to for rendering environment to events so data access and rendering can be synchronized and avoid race conditions.

type JSEnv

type JSEnv struct {
	MountParent string    // query selector
	DebugWriter io.Writer // write debug information about render details to this Writer if not nil
}

JSEnv is responsible for rendering (efficiently synchronizing) HTML in the browser in a WebAssembly application. Browser calls are performed using syscall/js. Various internal hashing and other optimizations are performed to attempt to make rendering as effience as possible. If used outside of a wasm program all methods will panic.

func NewJSEnv

func NewJSEnv(mountParent string, rootInst *ComponentInst, components ComponentTypeMap) *JSEnv

NewJSEnv returns a new instance with the specified mountParent (CSS selector of where in the HTML the ouptut will go), the root component instance, and any other component types available.

func (*JSEnv) EventWait

func (e *JSEnv) EventWait() bool

EventWait blocks until an event occurs and re-rendering is needed. Returns true unless the browser has gone away and the program should exit.

func (*JSEnv) RegisterComponentType

func (e *JSEnv) RegisterComponentType(tagName string, ct ComponentType)

RegisterComponentType assigns a component type to a tag name.

func (*JSEnv) Render

func (e *JSEnv) Render() error

Render is where the magic happens and your root component instance has its virtual DOM rendered and synchronized to the browser DOM. Render attempts to perform as few operations as possible and uses hashing to avoid unnecessary work. Component instances of children of the root component (referenced with HTML tags that have the components tag name), are managed as rendering occurs. Child components are created according to a hash of their properties and are recreated if their position in the DOM changes OR if their properties (HTML attributes on the referencing tag) change. Render is normally called in a loop with EventWait() being used to block until re-rendering is needed.

type ParserGo

type ParserGo struct {
	PackageName   string // name of package to use at top of files
	ComponentType string // just the struct name, no "*"
	DataType      string // just the struct name, no "*"
	OutDir        string // output dir
	OutFile       string // output file name with ".go" suffix
}

ParserGo is a template parser that emits Go source code that will construct the appropriately wired VGNodes.

func (*ParserGo) Parse

func (p *ParserGo) Parse(r io.Reader) error

Parse will parse a .vugu file and write out a Go source code file to OutFile.

type ParserGoPkg

type ParserGoPkg struct {
	// contains filtered or unexported fields
}

ParserGoPkg knows how to perform source file generation in relation to a package folder. Whereas ParserGo handles converting a single template, ParserGoPkg is a higher level interface and provides the functionality of the vugugen command line tool. It will scan a package folder for .vugu files and convert them to .go, with the appropriate defaults and logic.

func NewParserGoPkg

func NewParserGoPkg(pkgPath string, opts *ParserGoPkgOpts) *ParserGoPkg

NewParserGoPkg returns a new ParserGoPkg with the specified options or default if nil. The pkgPath is required and must be an absolute path.

func (*ParserGoPkg) Run

func (p *ParserGoPkg) Run() error

Run does the work and generates the appropriate .go files from .vugu files. It will also create a go.mod file if not present and not SkipGoMod. Same for main.go and SkipMainGo (will also skip if package already has file with package name something other than main). Per-file code generation is performed by ParserGo.

type ParserGoPkgOpts

type ParserGoPkgOpts struct {
	SkipRegisterComponentTypes bool // indicates func init() { vugu.RegisterComponentType(...) } code should not be emitted in each file
	SkipGoMod                  bool // do not try and create go.mod if it doesn't exist
	SkipMainGo                 bool // do not try and create main_wasm.go if it doesn't exist in a main package
}

ParserGoPkgOpts is the options for ParserGoPkg.

type Props

type Props map[string]interface{}

Props is a map that corresponds to property names and values. The name can correspond to an HTML attribute, or a property being passed in during component instantiation, depending on the context.

func (Props) Clone

func (p Props) Clone() Props

Clone makes a copy of the Props map, distinct from the original.

func (Props) Merge

func (p Props) Merge(p2 Props) Props

Merge will copy everything from p2 into p and return p for ease of use. Does not copy p.

func (Props) OrderedKeys

func (p Props) OrderedKeys() []string

OrderedKeys returns the keys sorted alphabetically.

type StaticHTMLEnv

type StaticHTMLEnv struct {
	// contains filtered or unexported fields
}

StaticHTMLEnv is an environment that renders to static HTML. Can be used to implement "server side rendering" as well as during testing.

func NewStaticHTMLEnv

func NewStaticHTMLEnv(out io.Writer, rootInst *ComponentInst, components ComponentTypeMap) *StaticHTMLEnv

NewStaticHTMLEnv returns a new instance of StaticHTMLEnv initialized properly. The out and rootInst are required, components may be nil.

func (*StaticHTMLEnv) RegisterComponentType

func (e *StaticHTMLEnv) RegisterComponentType(tagName string, ct ComponentType)

RegisterComponentType sets a component type with its tag name.

func (*StaticHTMLEnv) Render

func (e *StaticHTMLEnv) Render() error

Render performs redering to static HTML. The logic used is similar to JSEnv.Render however it will discard DOM events and is less careful about managing component lifecycle: components are simply created when needed and discarded after.

type VGAtom

type VGAtom uint32

VGAtom is an integer corresponding to golang.org/x/net/html/atom.Atom. Note that this may be removed for simplicity and to remove the dependency on the package above. Suggest you don't use it.

type VGAttribute

type VGAttribute struct {
	Namespace, Key, Val string
}

VGAttribute is the attribute on an HTML tag.

type VGNode

type VGNode struct {
	Parent, FirstChild, LastChild, PrevSibling, NextSibling *VGNode

	Type      VGNodeType
	DataAtom  VGAtom
	Data      string
	Namespace string
	Attr      []VGAttribute

	Props Props // dynamic attributes, used as input for components or converted to attributes for regular HTML elements

	InnerHTML string // indicates that children should be ignored and this raw HTML is the children of this tag

	DOMEventHandlers map[string]DOMEventHandler // describes invocations when DOM events happen
	// contains filtered or unexported fields
}

VGNode represents a node from our virtual DOM with the dynamic parts wired up into functions.

func (*VGNode) AppendChild

func (n *VGNode) AppendChild(c *VGNode)

AppendChild adds a node c as a child of n.

It will panic if c already has a parent or siblings.

func (*VGNode) InsertBefore

func (n *VGNode) InsertBefore(newChild, oldChild *VGNode)

InsertBefore inserts newChild as a child of n, immediately before oldChild in the sequence of n's children. oldChild may be nil, in which case newChild is appended to the end of n's children.

It will panic if newChild already has a parent or siblings.

func (*VGNode) RemoveChild

func (n *VGNode) RemoveChild(c *VGNode)

RemoveChild removes a node c that is a child of n. Afterwards, c will have no parent and no siblings.

It will panic if c's parent is not n.

func (*VGNode) SetDOMEventHandler

func (n *VGNode) SetDOMEventHandler(name string, h DOMEventHandler)

SetDOMEventHandler will assign a named event to DOMEventHandlers (will allocate the map if nil). Used during VDOM construction and during render to determine browser events to hook up.

func (*VGNode) Walk

func (vgn *VGNode) Walk(f func(*VGNode) error) error

Walk will walk the tree under a VGNode using the specified callback function. If the function provided returns a non-nil error then walking will be stopped and this error will be returned. Only FirstChild and NextSibling are used while walking and so with well-formed documents should not loop. (But loops created manually by modifying FirstChild or NextSibling pointers could cause this function to recurse indefinitely.) Note that f may modify nodes as it visits them with predictable results as long as it does not modify elements higher on the tree (up, toward the parent); it is safe to modify self and children.

type VGNodeType

type VGNodeType uint32

VGNodeType is one of the valid node types (error, text, docuemnt, element, comment, doctype). Note that only text, element and comment are currently used.

Directories

Path Synopsis
cmd
vugugen
vugugen is a command line tool to convert .vugu files into Go source code.
vugugen is a command line tool to convert .vugu files into Go source code.
Package distutil has some useful functions for building your Vugu application's distribution Rather than introducing third party build tools.
Package distutil has some useful functions for building your Vugu application's distribution Rather than introducing third party build tools.
examples
full-test-data
test1 Module
test2 Module
test3 Module
internal
htmlx
Package htmlx is a fork of https://github.com/golang/net/html.
Package htmlx is a fork of https://github.com/golang/net/html.
htmlx/atom
Package atom provides integer codes (also known as atoms) for a fixed set of frequently occurring HTML strings: tag names and attribute keys such as "p" and "id".
Package atom provides integer codes (also known as atoms) for a fixed set of frequently occurring HTML strings: tag names and attribute keys such as "p" and "id".
htmlx/charset
Package charset provides common text encodings for HTML documents.
Package charset provides common text encodings for HTML documents.
Package js is a drop-in replacement for syscall/js that provides identical behavior in a WebAssembly environment, and useful non-functional behavior outside of WebAssembly.
Package js is a drop-in replacement for syscall/js that provides identical behavior in a WebAssembly environment, and useful non-functional behavior outside of WebAssembly.
Package simplehttp provides an http.Handler that makes it easy to serve Vugu applications.
Package simplehttp provides an http.Handler that makes it easy to serve Vugu applications.
Package vugufmt provides gofmt-like functionality for vugu files.
Package vugufmt provides gofmt-like functionality for vugu files.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL