reduce allocs by re-using string builder
This commit is contained in:
parent
faf33ef93d
commit
93bb1d64ba
7 changed files with 40 additions and 48 deletions
|
|
@ -5,17 +5,18 @@ import (
|
|||
"net/http"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Headers = map[string]string
|
||||
|
||||
type Partial struct {
|
||||
Headers *Headers
|
||||
Root string
|
||||
Root *Element
|
||||
}
|
||||
|
||||
func (p *Partial) Render() string {
|
||||
return p.Root
|
||||
func (p *Partial) Render(builder *strings.Builder) {
|
||||
p.Root.Render(builder)
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
|
|
@ -30,23 +31,23 @@ func NewPage(root Ren) *Page {
|
|||
}
|
||||
}
|
||||
|
||||
func NewPageWithHttpMethod(httpMethod string, root Ren) *Page {
|
||||
func NewPageWithHttpMethod(httpMethod string, root *Element) *Page {
|
||||
return &Page{
|
||||
HttpMethod: httpMethod,
|
||||
Root: root,
|
||||
}
|
||||
}
|
||||
|
||||
func NewPartialWithHeaders(headers *Headers, root Ren) *Partial {
|
||||
func NewPartialWithHeaders(headers *Headers, root *Element) *Partial {
|
||||
return &Partial{
|
||||
Headers: headers,
|
||||
Root: root.Render(),
|
||||
Root: root,
|
||||
}
|
||||
}
|
||||
|
||||
func NewPartial(root Ren) *Partial {
|
||||
func NewPartial(root *Element) *Partial {
|
||||
return &Partial{
|
||||
Root: root.Render(),
|
||||
Root: root,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,12 +69,12 @@ func Increment(amount int) JsCommand {
|
|||
|
||||
func SetInnerHtml(r Ren) JsCommand {
|
||||
// language=JavaScript
|
||||
return JsCommand{Command: fmt.Sprintf("this.innerHTML = `%s`", r.Render())}
|
||||
return JsCommand{Command: fmt.Sprintf("this.innerHTML = `%s`", Render(r))}
|
||||
}
|
||||
|
||||
func SetOuterHtml(r Ren) JsCommand {
|
||||
// language=JavaScript
|
||||
return JsCommand{Command: fmt.Sprintf("this.outerHTML = `%s`", r.Render())}
|
||||
return JsCommand{Command: fmt.Sprintf("this.outerHTML = `%s`", Render(r))}
|
||||
}
|
||||
|
||||
func AddAttribute(name, value string) JsCommand {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@ package h
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Render(node Ren) string {
|
||||
start := time.Now()
|
||||
html := node.Render()
|
||||
builder := &strings.Builder{}
|
||||
node.Render(builder)
|
||||
duration := time.Since(start)
|
||||
fmt.Printf("render took %d microseconds\n", duration.Microseconds())
|
||||
return html
|
||||
return builder.String()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,13 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
func (node *Element) Render() string {
|
||||
builder := &strings.Builder{}
|
||||
|
||||
func (node *Element) Render(builder *strings.Builder) {
|
||||
// some elements may not have a tag, such as a Fragment
|
||||
if node.tag != "" {
|
||||
builder.WriteString("<" + node.tag)
|
||||
builder.WriteString(" ")
|
||||
for name, value := range node.attributes {
|
||||
builder.WriteString(NewAttribute(name, value).Render())
|
||||
NewAttribute(name, value).Render(builder)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,9 +32,9 @@ func (node *Element) Render() string {
|
|||
for _, child := range node.children {
|
||||
switch child.(type) {
|
||||
case *AttributeMap:
|
||||
builder.WriteString(child.(*AttributeMap).Render())
|
||||
child.Render(builder)
|
||||
case *LifeCycle:
|
||||
builder.WriteString(child.(*LifeCycle).Render())
|
||||
child.Render(builder)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -53,52 +51,42 @@ func (node *Element) Render() string {
|
|||
case *LifeCycle:
|
||||
continue
|
||||
default:
|
||||
builder.WriteString(child.Render())
|
||||
child.Render(builder)
|
||||
}
|
||||
}
|
||||
|
||||
if node.tag != "" {
|
||||
builder.WriteString("</" + node.tag + ">")
|
||||
}
|
||||
|
||||
str := builder.String()
|
||||
return str
|
||||
}
|
||||
|
||||
func (a *AttributeR) Render() string {
|
||||
return fmt.Sprintf(`%s="%s"`, a.Name, a.Value)
|
||||
func (a *AttributeR) Render(builder *strings.Builder) {
|
||||
builder.WriteString(fmt.Sprintf(`%s="%s"`, a.Name, a.Value))
|
||||
}
|
||||
|
||||
func (t *TextContent) Render() string {
|
||||
return t.Content
|
||||
func (t *TextContent) Render(builder *strings.Builder) {
|
||||
builder.WriteString(t.Content)
|
||||
}
|
||||
|
||||
func (r *RawContent) Render() string {
|
||||
return r.Content
|
||||
func (r *RawContent) Render(builder *strings.Builder) {
|
||||
builder.WriteString(r.Content)
|
||||
}
|
||||
|
||||
func (c *ChildList) Render() string {
|
||||
builder := &strings.Builder{}
|
||||
func (c *ChildList) Render(builder *strings.Builder) {
|
||||
for _, child := range c.Children {
|
||||
builder.WriteString(child.Render())
|
||||
child.Render(builder)
|
||||
}
|
||||
str := builder.String()
|
||||
return str
|
||||
}
|
||||
|
||||
func (m *AttributeMap) Render() string {
|
||||
builder := &strings.Builder{}
|
||||
func (m *AttributeMap) Render(builder *strings.Builder) {
|
||||
m2 := m.ToMap()
|
||||
|
||||
for k, v := range m2 {
|
||||
builder.WriteString(NewAttribute(k, v).Render())
|
||||
NewAttribute(k, v).Render(builder)
|
||||
}
|
||||
}
|
||||
|
||||
str := builder.String()
|
||||
return str
|
||||
}
|
||||
|
||||
func (l *LifeCycle) Render() string {
|
||||
func (l *LifeCycle) Render(builder *strings.Builder) {
|
||||
m := make(map[string]string)
|
||||
|
||||
for event, commands := range l.handlers {
|
||||
|
|
@ -114,8 +102,7 @@ func (l *LifeCycle) Render() string {
|
|||
children = append(children, Attribute(event, js))
|
||||
}
|
||||
|
||||
result := Children(children...).Render()
|
||||
return result
|
||||
Children(children...).Render(builder)
|
||||
}
|
||||
|
||||
func (m *AttributeMap) ToMap() map[string]string {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ type Element struct {
|
|||
children []Ren
|
||||
}
|
||||
|
||||
func (node *Element) AppendChild(child Ren) Ren {
|
||||
func (node *Element) AppendChild(child Ren) *Element {
|
||||
node.children = append(node.children, child)
|
||||
return node
|
||||
}
|
||||
|
|
@ -363,7 +363,7 @@ func List[T any](items []T, mapper func(item T, index int) *Element) *Element {
|
|||
return node
|
||||
}
|
||||
|
||||
func Fragment(children ...Ren) Ren {
|
||||
func Fragment(children ...Ren) *Element {
|
||||
return &Element{
|
||||
tag: "",
|
||||
children: children,
|
||||
|
|
@ -541,11 +541,11 @@ func NewSwap(selector string, content *Element) SwapArg {
|
|||
}
|
||||
}
|
||||
|
||||
func OobSwap(ctx *RequestContext, content *Element) Ren {
|
||||
func OobSwap(ctx *RequestContext, content *Element) *Element {
|
||||
return OobSwapWithSelector(ctx, "", content)
|
||||
}
|
||||
|
||||
func OobSwapWithSelector(ctx *RequestContext, selector string, content *Element) Ren {
|
||||
func OobSwapWithSelector(ctx *RequestContext, selector string, content *Element) *Element {
|
||||
if ctx == nil || ctx.Get("HX-Request") == "" {
|
||||
return Empty()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ import (
|
|||
"encoding/json"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Ren interface {
|
||||
Render() string
|
||||
Render(builder *strings.Builder)
|
||||
}
|
||||
|
||||
func Ternary[T any](value bool, a T, b T) T {
|
||||
|
|
|
|||
|
|
@ -215,7 +215,8 @@ func UpdateName(ctx *h.RequestContext) *h.Partial {
|
|||
return h.NewPartial(h.Div(h.Text("failed to update")))
|
||||
}
|
||||
|
||||
return h.NewPartial(h.OobSwap(ctx, Task(task, false)))
|
||||
return h.NewPartial(
|
||||
h.OobSwap(ctx, Task(task, false)))
|
||||
}
|
||||
|
||||
func EditNameForm(ctx *h.RequestContext) *h.Partial {
|
||||
|
|
|
|||
Loading…
Reference in a new issue