add WASM blazor-like thing
This commit is contained in:
86
go/wasmruntime/mount.go
Normal file
86
go/wasmruntime/mount.go
Normal file
@@ -0,0 +1,86 @@
|
||||
//go:build js && wasm
|
||||
|
||||
package wasmruntime
|
||||
|
||||
import (
|
||||
"syscall/js"
|
||||
|
||||
"kjol/vdom"
|
||||
)
|
||||
|
||||
var (
|
||||
rootRender func()
|
||||
renderScheduled bool
|
||||
disposed bool
|
||||
flushFunc js.Func
|
||||
flushInited bool
|
||||
)
|
||||
|
||||
// HasServerContent reports whether #app already holds server-rendered markup
|
||||
// (so the client should Hydrate rather than build from scratch).
|
||||
func HasServerContent() bool {
|
||||
return document.Call("getElementById", "app").Get("firstChild").Truthy()
|
||||
}
|
||||
|
||||
// Run mounts a component fresh (client-side rendering). The component returns a
|
||||
// VNode tree; any signal write re-renders and reconciles into the DOM.
|
||||
func Run(component func() *vdom.VNode) {
|
||||
vdom.Schedule = scheduleRender
|
||||
root := document.Call("getElementById", "app")
|
||||
var prev *vdom.VNode
|
||||
rootRender = func() {
|
||||
next := component()
|
||||
patchChildren(root, one(prev), one(next))
|
||||
prev = next
|
||||
}
|
||||
rootRender()
|
||||
finish(root)
|
||||
}
|
||||
|
||||
// Hydrate attaches to server-rendered DOM: it renders the same initial tree the
|
||||
// server did, adopts the existing nodes (wiring events, no re-creation), then
|
||||
// re-renders reactively from there.
|
||||
func Hydrate(component func() *vdom.VNode) {
|
||||
vdom.Schedule = scheduleRender
|
||||
root := document.Call("getElementById", "app")
|
||||
prev := component()
|
||||
hydrateNode(root.Get("firstChild"), prev)
|
||||
rootRender = func() {
|
||||
next := component()
|
||||
patchChildren(root, one(prev), one(next))
|
||||
prev = next
|
||||
}
|
||||
finish(root)
|
||||
}
|
||||
|
||||
func finish(root js.Value) {
|
||||
// Allow a JS loader to tear this instance down before an in-place hot swap:
|
||||
// snapshot signal state (so the next instance restores it), then clear #app.
|
||||
js.Global().Set("__gowasmDispose", js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
disposed = true
|
||||
saveState()
|
||||
root.Set("innerHTML", "")
|
||||
return nil
|
||||
}))
|
||||
select {} // keep the runtime alive for event callbacks
|
||||
}
|
||||
|
||||
func scheduleRender() {
|
||||
if disposed || rootRender == nil || renderScheduled {
|
||||
return
|
||||
}
|
||||
renderScheduled = true
|
||||
js.Global().Call("queueMicrotask", ensureFlush())
|
||||
}
|
||||
|
||||
func ensureFlush() js.Func {
|
||||
if !flushInited {
|
||||
flushFunc = js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
renderScheduled = false
|
||||
rootRender()
|
||||
return nil
|
||||
})
|
||||
flushInited = true
|
||||
}
|
||||
return flushFunc
|
||||
}
|
||||
Reference in New Issue
Block a user