Skip to content

Commit

Permalink
Merge pull request #11 from HuckRidgeSW/wasm
Browse files Browse the repository at this point in the history
WASM support
  • Loading branch information
HuckRidgeSW authored Sep 12, 2018
2 parents 176165e + 2bf8294 commit 50ee540
Show file tree
Hide file tree
Showing 38 changed files with 4,593 additions and 2,589 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
.*.sw?
*.bak
Session*.vim
build
tags
62 changes: 56 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
# Intro

hvue is a [GopherJS](https://github.com/gopherjs/gopherjs) wrapper for the
[Vue](https://vuejs.org/) Javascript framework.
hvue is a [GopherJS](https://github.com/gopherjs/gopherjs) and wasm wrapper
for the [Vue](https://vuejs.org/) Javascript framework.

WASM code is under development.

# Install

`go get github.com/huckridgesw/hvue`
## Install Go 1.11

See https://golang.org/dl/.

## Install hvue's wasm branch

```bash
cd path/to/github.com # in your $GOPATH
mkdir huckridgesw
cd huckridgesw
git clone git@github.com:HuckRidgeSW/hvue.git
cd hvue
git checkout --track origin/wasm
```

# Examples & Demos

Expand All @@ -29,15 +44,50 @@ And so on. Links are in the code.

## Running the examples

### GopherJS

```bash
cd path/to/github.com/huckridgesw/hvue
echo "var hvue_wasm = false;" > examples/maybe_wasm.js
gopherjs serve github.com/huckridgesw/hvue # listens on 8080
```
cd /path/to/github.com/huckridgesw/hvue
gopherjs serve github.com/huckridgesw/hvue
```

and then
- http://localhost:8080/examples/01-introduction/
- http://localhost:8080/examples/02-lifecycle/
- http://localhost:8080/examples/ for more

### WASM

```bash
cd path/to/github.com/huckridgesw/hvue
echo "var hvue_wasm = true;" > examples/maybe_wasm.js
go run examples/server/main.go # Listens on 8081
cd examples/??-???? # some examples directory
GOARCH=wasm GOOS=js go build -o ${PWD##*/}.wasm main.go # compile wasm
```

and then
- http://localhost:8081/examples/01-introduction/
- http://localhost:8081/examples/02-lifecycle/
- http://localhost:8081/examples/ for more

Remember to recompile after any changes. There's no facility yet to
auto-build yet (a-la `gopherjs build -w` or `gopherjs serve`).

### Switching from GopherJS to WASM and back

- Do the appropriate `"echo "var hvue_wasm = ?;" > examples/maybe_wasm.js`.
(See above.)
- Be sure to do "shift-cmd-R" (Chrome, macOS; other browsers / OSes will vary)
to reload without using the cache, to get the new `maybe_wasm.js` and/or new
wasm. (Actually I'm not sure you need that to get new wasm, since it's
loaded via an explicit `fetch()` call, but it's probably not a bad idea.)
Alternatively, in Chrome you can open the developer console, go to the
network tab, and check "disable cache". (AIUI only works while said console
window is open.)

# GoDoc

http://godoc.org/github.com/HuckRidgeSW/hvue

99 changes: 54 additions & 45 deletions component.go
Original file line number Diff line number Diff line change
@@ -1,54 +1,55 @@
package hvue

import "github.com/gopherjs/gopherjs/js"
import (
"github.com/gopherjs/gopherwasm/js"
)

// NewComponent defines a new Vue component. It wraps js{Vue.component}:
// https://vuejs.org/v2/api/#Vue-component.
func NewComponent(name string, opts ...ComponentOption) {
c := &Config{Object: o()}
c.Setters = o()
c := &Config{Value: NewObject()}
c.SetSetters(NewObject())
c.Option(opts...)

if c.Data == js.Undefined {
c.Object.Set("data", jsCallWithVM(func(vm *VM) interface{} {
obj := o()
// Get the parent data object ID, if it exists
dataID := vm.Get("$parent").Get("$data").Get("hvue_dataID")
if dataID != js.Undefined {
obj.Set("hvue_dataID", dataID)
}
return obj
}))
if c.DataType == js.TypeUndefined {
// wasm_new_data_func takes care of the hvue_dataID magic.
c.Set("data",
js.Global().Call("wasm_new_data_func",
NewObject(), // call wasm_new_data_func with a blank template
js.NewCallback(func([]js.Value) {}),
))
} else if c.DataType != js.TypeFunction {
panic("Cannot use Data() with NewComponent, must use DataFunc. Component: " + name)
}

js.Global.Get("Vue").Call("component", name, c.Object)
js.Global().Get("Vue").Call("component", name, c.Value)
}

// Component is used in NewVM to define a local component, within the scope of
// another instance/component.
// https://vuejs.org/v2/guide/components.html#Local-Registration
func Component(name string, opts ...ComponentOption) ComponentOption {
return func(c *Config) {
componentOption := &Config{Object: o()}
componentOption := &Config{Value: NewObject()}
componentOption.Option(opts...)

if c.Components == js.Undefined {
c.Components = o()
if c.Components() == js.Undefined() {
c.SetComponents(NewObject())
}

c.Components.Set(name, componentOption.Object)
c.Components().Set(name, componentOption.Value)
}
}

// Props defines one or more simple prop slots. For complex prop slots, use
// PropObj(). https://vuejs.org/v2/api/#props
func Props(props ...string) ComponentOption {
return func(c *Config) {
if c.Props == js.Undefined {
c.Props = NewArray()
if c.Props() == js.Undefined() {
c.SetProps(NewArray())
}
for i, prop := range props {
c.Props.SetIndex(i, prop)
c.Props().SetIndex(i, prop)
}
}
}
Expand All @@ -57,81 +58,89 @@ func Props(props ...string) ComponentOption {
// Default, DefaultFunc, and Validator.
func PropObj(name string, opts ...PropOption) ComponentOption {
return func(c *Config) {
if c.Props == js.Undefined {
c.Props = o()
if c.Props() == js.Undefined() {
c.SetProps(NewObject())
}
pO := &PropConfig{Object: o()}
pO := &PropConfig{Value: NewObject()}
pO.Option(opts...)
c.Props.Set(name, pO.Object)
c.Props().Set(name, pO.Value)
}
}

// Template defines a template for a component. It sets the js{template} slot
// of a js{Vue.component}'s configuration object.
func Template(template string) ComponentOption {
return func(c *Config) {
c.Template = template
c.SetTemplate(template)
}
}

// Types configures the allowed types for a prop.
// https://vuejs.org/v2/guide/components.html#Props.
func Types(types ...pOptionType) PropOption {
return func(p *PropConfig) {
arr := js.Global.Get("Array").New()
arr := NewArray()
for _, t := range types {
var newVal *js.Object
var newVal js.Value
switch t {
case PString:
newVal = js.Global.Get("String")
newVal = js.Global().Get("String")
case PNumber:
newVal = js.Global.Get("Number")
newVal = js.Global().Get("Number")
case PBoolean:
newVal = js.Global.Get("Boolean")
newVal = js.Global().Get("Boolean")
case PFunction:
newVal = js.Global.Get("Function")
newVal = js.Global().Get("Function")
case PObject:
newVal = js.Global.Get("Object")
newVal = js.Global().Get("Object")
case PArray:
newVal = js.Global.Get("Array")
newVal = js.Global().Get("Array")
}
arr.Call("push", newVal)
}
p.typ = arr
p.SetType(arr)
}
}

// Required specifies that the prop is required.
// https://vuejs.org/v2/guide/components.html#Props.
var Required PropOption = func(p *PropConfig) {
p.required = true
p.SetRequired(true)
}

// Default gives the default for a prop.
// https://vuejs.org/v2/guide/components.html#Props
func Default(def interface{}) PropOption {
return func(p *PropConfig) {
p.def = def
p.SetDefault(def)
}
}

// DefaultFunc sets a function that returns the default for a prop.
// https://vuejs.org/v2/guide/components.html#Props
func DefaultFunc(def func(*VM) interface{}) PropOption {
//
// FIXME: Right now, can only pass an object (not a function). The JS helper
// function copies it to a new object. Later, need to be able to pass a
// function, which returns a new value.
func DefaultFunc(def js.Value) PropOption {
return func(p *PropConfig) {
p.def = jsCallWithVM(def)
p.SetDefault(js.Global().Call("wasm_return_copy", def))
}
}

// Validator functions generate warnings in the JS console if using the
// vue.js development build. They don't panic or otherwise crash your code,
// they just give warnings if the validation fails.
func Validator(f func(vm *VM, value *js.Object) interface{}) PropOption {
//
// FIXME: Currently does nothing, because in 1.11 Go functions called from JS
// can't return values.
func Validator(f func(vm *VM, value js.Value) interface{}) PropOption {
return func(p *PropConfig) {
p.validator = js.MakeFunc(
func(this *js.Object, args []*js.Object) interface{} {
vm := &VM{Object: this}
return f(vm, args[0])
})
return
// p.SetValidator(NewCallback(
// func(this js.Value, args []js.Value) interface{} {
// vm := &VM{Value: this}
// return f(vm, args[0])
// })
}
}
62 changes: 39 additions & 23 deletions computed.go
Original file line number Diff line number Diff line change
@@ -1,39 +1,55 @@
package hvue

import "github.com/gopherjs/gopherjs/js"
import (
"github.com/gopherjs/gopherwasm/js"
)

// Computed defines name as a computed property. Note that name *must not* be
// set in data for this to work. It's probably best if it's not even a slot
// in the struct. Only access it via vm.Get. You could also create an
// accessor; see the 04-computed-with-setter example.
//
// Computed functions require synchronous functions that can return values to
// Vue. Go/wasm cannot support either of those things just yet. So currently
// this function just panics.
//
// Use a watcher instead.
func Computed(name string, f func(vm *VM) interface{}) ComponentOption {
return func(c *Config) {
if c.Computed == js.Undefined {
c.Computed = NewObject()
}
c.Computed.Set(name, jsCallWithVM(f))
}
panic("Computed not supported")
// return func(c *Config) {
// if c.Computed() == js.Undefined() {
// c.SetComputed(NewObject())
// }
// c.Computed().Set(name, jsCallWithVM(f))
// }
}

// ComputedWithGetSet defines name as a computed property with explicit get &
// set. Note that name *must not* be set in data for this to work. It's
// probably best if it's not even a slot in the struct. Only access it via
// vm.Get/Set. You could create an accessor; see the 04-computed-with-setter
// example.
func ComputedWithGetSet(name string, get func(vm *VM) interface{}, set func(vm *VM, newValue *js.Object)) ComponentOption {
return func(c *Config) {
if c.Computed == js.Undefined {
c.Computed = NewObject()
}
c.Computed.Set(name,
js.M{
"get": jsCallWithVM(get),
"set": js.MakeFunc(
func(this *js.Object, args []*js.Object) interface{} {
vm := &VM{Object: this}
set(vm, args[0])
return nil
})})
c.Setters.Set(name, true)
}
//
// Computed functions require synchronous functions that can return values to
// Vue. Go/wasm cannot support either of those things just yet. So currently
// this function just panics.
//
// Use a watcher instead.
func ComputedWithGetSet(name string, get func(vm *VM) interface{}, set func(vm *VM, newValue js.Value)) ComponentOption {
panic("ComputedWithGetSet not supported")
// return func(c *Config) {
// if c.Computed() == js.Undefined() {
// c.SetComputed(NewObject())
// }
// c.Computed().Set(name,
// map[string]interface{}{
// "get": jsCallWithVM(get),
// "set": NewCallback(
// func(this js.Value, args []js.Value) interface{} {
// vm := &VM{Value: this}
// set(vm, args[0])
// return nil
// })})
// c.Setters().Set(name, true)
// }
}
Loading

0 comments on commit 50ee540

Please sign in to comment.