Skip to content

Instantly share code, notes, and snippets.

@revenkroz
Created November 22, 2022 20:32
Show Gist options
  • Save revenkroz/dfad3553b1384f8d0678d2dfe8c557ec to your computer and use it in GitHub Desktop.
Save revenkroz/dfad3553b1384f8d0678d2dfe8c557ec to your computer and use it in GitHub Desktop.
Approaches to SSR with v8go

First Approach

Fastest one. The idea is to run SSR script, get function with sensitive args and run it.

iso object has Isolate *v8go.Isolate and RenderScript *v8go.UnboundScript (it was compiled before).

Go Code

// renderer.go
// ...

func (r *Renderer) Render(urlPath string) (string, error) {
	iso := r.pool.Get()
	defer r.pool.Put(iso)

	ctx := v8go.NewContext(iso.Isolate)
	defer ctx.Close()

	iso.RenderScript.Run(ctx)

	renderCmd := fmt.Sprintf(`ssrRender("%s")`, urlPath)
	val, err := ctx.RunScript(renderCmd, r.ssrScriptName)
	if err != nil {
		if jsErr, ok := err.(*v8go.JSError); ok {
			err = fmt.Errorf("%v", jsErr.StackTrace)
		}
		return "", nil
	}

	return val.String(), nil
}

JS Code

// entry-server.ts
// ...

function ssrRender(url: string) {
  return render(url).then((html) => {
    return html
  })
}

(globalThis as any).ssrRender = ssrRender

Second Approach

Create global object in Go with render function that recieves rendered html, concat string in Go and return.

Go Code

iso object has Isolate *v8go.Isolate and RenderScript *v8go.UnboundScript (it was compiled before).

// renderer.go
// ...

func (r *Renderer) Render(urlPath string) (string, error) {
	iso := r.pool.Get()
	defer r.pool.Put(iso)

	outputHTML := ""
	
	ssrObject := v8go.NewObjectTemplate(iso.Isolate)
	ssrObject.Set("href", urlPath)
	ssrObject.Set("render", v8go.NewFunctionTemplate(iso.Isolate, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
		args := info.Args()
		if len(args) > 0 {
			outputHTML = args[0].String()
		}
		return nil
	}))
	
	globalObject := v8go.NewObjectTemplate(iso.Isolate)
	globalObject.Set("ssr", ssrObject)
	
	ctx := v8go.NewContext(iso.Isolate, globalObject)
  defer ctx.Close()
	
	start := time.Now()
	iso.RenderScript.Run(ctx)
	//if _, err := ctx.RunScript(r.scriptSource, r.Path); err != nil {
	//	return "", err
	//}
	fmt.Println("Script run:", time.Since(start))
	
	return outputHTML, nil
}

JS Code

// entry-server.ts
// ...

if (typeof ssr !== 'undefined') {
  render(ssr.href).then((html) => {
    ssr.render(html)
  })
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment