vpl

package module
v0.1.6 Latest Latest
Warning

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

Go to latest
Published: Dec 18, 2021 License: MIT Imports: 15 Imported by: 0

README

Vpl

Go Report Card

Vpl is a Vuejs-syntax like template-engine for Golang.

  • Componentization
  • Powerful template syntax for the modern html
  • Supports Js(Es5) expressions
  • A little faster (I tried my best to optimize :)

Installation

go get github.com/zbysir/vpl

Getting Started

Write the main.go file as follows

package main

import (
	"context"
	"github.com/zbysir/vpl"
)

func main() {
	v := vpl.New()

	err := v.ComponentTxt("app", `
<!DOCTYPE html>
<html :lang="lang">
<head>
  <meta charset="UTF-8">
  <title>{{title}}</title>
</head>
<body>

<div :id="id" style="font-size: 20px" :style="{color: color}">
  <span v-if="color=='red'">
    color is red
  </span>
  <span v-else>
    color is {{color}}
  </span>
</div>

</body>
</html>
`)
	if err != nil {
		panic(err)
	}

	props := vpl.NewProps()
	props.AppendMap(map[string]interface{}{
		"title": "hello vpl",
		"color": "red",
		"id": "content",
		"lang": "en",
	})

	html, err := v.RenderComponent("app", &vpl.RenderParam{
		Global: nil,
		Ctx:    context.Background(),
		Props:  props,
	})
	if err != nil {
		panic(err)
	}

	print(html)
	// Output: <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>hello vpl</title></head><body><div style="color: red; font-size: 20px;"><span>color is red</span></div></body></html>
}

Then run it.

More examples in /example and /test

Description of the parameters

You only need to understand a few parameters.

vpl.Props
props := vpl.NewProps()
// use Append to add a variable
props.Append("lang", "en")

// use AppendMap to add multiple variables 
props.AppendMap(map[string]interface{}{
    "title": "hello vpl",
    "color": "red",
})
vpl.RenderParam
vpl.RenderParam{
    Global: nil, // Defined Global Variable in this rendering.
    Props:  props, // Parameters of the rendering component.
}
Admonition

All data used by Vpl must be a golang base types, such as int64, int, float32, float64, []interface, map[string]interface{}.

The following example is wrong:

props.Append("list", [3]int{1, 2, 3})

You should use []interface type instead of [3]int:

props.Append("list", []interface{}{1, 2, 3})

For convenience, vpl provides vpl.Copy function to convert a complex structure to a structure containing only basic types.

props.Append("list", vpl.Copy([3]int{1, 2, 3}))

Don't worry too much about performance, it is only executed once in each render.

With Go features

Let's add some go features to vpl.

Parallel

The advantage of go is concurrency, can vpl use it?

YES! Use the <parallel> component.

Let's see this example:

<div>
    <div>
        <!-- Some things took 1s -->
        {{ sleep(1) }} 
    </div>
    <div>
        <!-- Some things took 2s -->
        {{ sleep(2) }} 
    </div>
</div>

It will take 3s if the template is executed in order. You can wrap them with parallel component to parallel them.

<div>
    <parallel>
        <div>
            <!-- Some things took 1s -->
            {{ sleep(1) }} 
        </div>
    </parallel>
    <parallel>
        <div>
            <!-- Some things took 2s -->
            {{ sleep(2) }} 
        </div>
    </parallel>
</div>

It only takes 2s now.

Docs

IntelliJ Plugin

Just use the Vuejs plugin.

Dependencies

  • github.com/robertkrimen/otto: It is used to parse Js expression.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultCanBeAttr = func(k string) bool {
	if k == "id" {
		return true
	}
	if strings.HasPrefix(k, "data-") {
		return true
	}

	return false
}

Functions

func Copy added in v0.1.2

func Copy(i interface{}) (dst interface{})

Copy convert a complex structure to a structure containing only basic types. Please refer to README.md#Admonition

func NicePrintStatement

func NicePrintStatement(st Statement, lev int) string

打印Statement, 方便调试

func ParseHtmlToStatement

func ParseHtmlToStatement(tpl string, options *parser.ParseVueNodeOptions) (Statement, *SlotsC, error)

func ShouldLookInterface

func ShouldLookInterface(data interface{}, keys ...string) (desc interface{}, rootExist bool, exist bool)

shouldLookInterface会返回interface(map[string]interface{})中指定的keys路径的值

Types

type AttrWay

type AttrWay uint8
const (
	MayBeAttr    AttrWay = 0 // 无法在编译时确定, 还需要在运行时判断
	CanBeAttr    AttrWay = 1 // 在编译时就确定能够当做attr
	CanNotBeAttr AttrWay = 2 // 在编译时就确定不能够当做attr
)

type ChanSpan

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

func NewChanSpan

func NewChanSpan() *ChanSpan

func (*ChanSpan) Done

func (p *ChanSpan) Done(s string)

func (*ChanSpan) Result

func (p *ChanSpan) Result() string

type Class

type Class = parser.Class

type ComponentStatement

type ComponentStatement struct {
	ComponentKey    string
	ComponentStruct ComponentStruct
}

调用组件

func (*ComponentStatement) Exec

调用组件语句 o是上层options. 根据组件attr拼接出新的scope, 再执行组件 处理slot作用域

type ComponentStruct

type ComponentStruct = tagStruct

组件的属性

type Directive

type Directive func(ctx *RenderCtx, nodeData *NodeData, binding *DirectivesBinding)

type DirectivesBinding

type DirectivesBinding struct {
	Value interface{}
	Arg   string
	Name  string
}

type EmptyStatement

type EmptyStatement struct {
}

func (*EmptyStatement) Exec

type ExecSlotOptions

type ExecSlotOptions struct {
	// <slot :abc="abc"> 语法传递的slot props.
	SlotProps *Props
}

type FuncStatement

type FuncStatement func(*StatementCtx, *StatementOptions) error

func (FuncStatement) Exec

type Function

type Function func(ctx *RenderCtx, args ...interface{}) interface{}

type ListWriter

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

支持同时写Span和string的Write 优化多个字符串拼接

func NewListWriter

func NewListWriter() *ListWriter

func (*ListWriter) Result

func (p *ListWriter) Result() string

func (*ListWriter) WriteSpan

func (p *ListWriter) WriteSpan(span Span)

func (*ListWriter) WriteString

func (p *ListWriter) WriteString(s string)

type MapStore

type MapStore map[string]interface{}

func (MapStore) Get

func (g MapStore) Get(key string) (interface{}, bool)

func (MapStore) Set

func (g MapStore) Set(key string, val interface{})

type NodeData

type NodeData struct {
	Props *Props // 给组件添加attr
	Slots *Slots
}

type Options

type Options func(o *Vpl)

func WithCanBeAttrsKey

func WithCanBeAttrsKey(canBeAttr func(k string) bool) Options

func WithSkipComment

func WithSkipComment(skip bool) Options

type Prop

type Prop struct {
	Key string
	Val interface{}
}

数值Prop 执行PropC会得到PropR

type PropKeys

type PropKeys struct {
	AttrWay AttrWay // 能否被当成Attr输出
	Key     string
	Last    *PropKeys // 如果LinkKey只有一个元素, 则last是自己
	Next    *PropKeys
}

func (*PropKeys) Append

func (l *PropKeys) Append(a *PropKeys)

type Props

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

func NewProps

func NewProps() *Props

func (*Props) Append

func (r *Props) Append(k string, v interface{})

func (*Props) AppendAttr

func (r *Props) AppendAttr(k, v string)

AppendAttr 向当前tag添加属性

func (*Props) AppendClass

func (r *Props) AppendClass(c ...string)

func (*Props) AppendMap

func (r *Props) AppendMap(mp map[string]interface{})

无序添加多个props

func (*Props) AppendStyle

func (r *Props) AppendStyle(st map[string]string)

func (*Props) ForEach

func (r *Props) ForEach(cb func(index int, k *PropKeys, v interface{}))

func (*Props) Get

func (r *Props) Get(key string) (interface{}, bool)

func (*Props) ToMap

func (r *Props) ToMap() map[string]interface{}

type RenderCtx

type RenderCtx struct {
	Scope *Scope // 当前作用域, 用于向当前作用域声明一个值
	Store Store  // 用于共享数据, 此Store是RenderParam中传递的Store
}

在渲染中的上下文, 用在function和directive

type RenderParam

type RenderParam struct {
	// 声明本次渲染的全局变量, 和vpl.Global()功能类似, 在所有组件中都有效.
	// 可以用来存放诸如版本号/作者等全部组件都可能需要访问的数据, 还可以存放方法.
	// Global设置的值会覆盖vpl.Global()设置的值.
	//
	// Q: 如何区分应该使用RenderParam.Global设置全局变量还是vpl.Global()设置全局变量?
	// A:
	//  根据这个"全局"变量的真正范围而定.
	//  如果这个变量是"整个程序"的全局变量, 如一个全局方法, 那么它应该使用vpl.Global()设置
	//  如果这个变量是"这一次渲染过程中"的全局变量, 如在渲染每个页面时的页面ID, 那么它应该使用RenderParam.Global设置.
	Global map[string]interface{}
	// 用于在整个运行环境共享变量, 如在一个方法/指令中读取另一个方法/指令里存储的数据
	Store Store

	// unused so far
	Ctx   context.Context
	Props *Props
	// 渲染组件时给组件传递slots
	Slots *SlotsC
}

type Scope

type Scope struct {
	Parent *Scope
	Value  map[string]interface{}
}

执行每一个块的上下文

func NewScope

func NewScope(outer *Scope) *Scope

func (*Scope) Extend

func (s *Scope) Extend(data map[string]interface{}) *Scope

func (*Scope) Get

func (s *Scope) Get(k string) interface{}

func (*Scope) GetDeep

func (s *Scope) GetDeep(k ...string) (v interface{})

获取作用域中的变量 会向上查找

func (*Scope) Set

func (s *Scope) Set(k string, v interface{})

设置暂时只支持在当前作用域设置变量 避免对上层变量造成副作用

type Slot

type Slot struct {
	*SlotC

	// 在运行时被赋值
	// Declarer 存储在当slot声明时的组件数据
	Declarer *StatementOptions
}

Slot的运行时

func (*Slot) Exec

func (s *Slot) Exec(ctx *StatementCtx, o *StatementOptions) error

type SlotC

type SlotC struct {
	Name string

	Children Statement
	// contains filtered or unexported fields
}

声明Slot的语句(编译时) <h1 v-slot:default="SlotProps"> </h1>

type Slots

type Slots struct {
	Default   *Slot // 大多数都是Default插槽,放入map里会有性能损耗,所以优化为单独一个字段
	NamedSlot map[string]*Slot
}

func (*Slots) Get

func (s *Slots) Get(key string) *Slot

type SlotsC

type SlotsC struct {
	Default   *SlotC
	NamedSlot map[string]*SlotC
}

https://cn.vuejs.org/v2/guide/components-slots.html SlotsC 存放传递给组件的所有Slot(默认与具名)(编译时), vue语法: <h1 v-slot:default="xxx"></h1>

func (*SlotsC) WrapScope

func (s *SlotsC) WrapScope(o *StatementOptions) (sr *Slots)

WrapScope 设置在slot声明时的scope, 用于在运行slot时使用声明slot时的scope

type Span

type Span interface {
	Result() string
}

type Statement

type Statement interface {
	Exec(ctx *StatementCtx, o *StatementOptions) error
}

vue语法会被编译成一组Statement 为了避免多次运行造成副作用, 所有的 运行时代码 都不应该修改 编译时

type StatementCtx

type StatementCtx struct {
	Global *Scope

	Store Store

	Ctx context.Context
	W   Writer

	Components    map[string]Statement
	Directives    map[string]Directive
	CanBeAttrsKey func(k string) bool
}

整个渲染期间的上下文. Ctx贯穿整个渲染流程, 意味着每一个组件/方法/指令都可以拿到同一个ctx, 只有其中的值会变化.

func (*StatementCtx) Clone

func (c *StatementCtx) Clone() *StatementCtx

func (*StatementCtx) Get

func (c *StatementCtx) Get(k string) (v interface{}, exist bool)

func (*StatementCtx) NewScope

func (c *StatementCtx) NewScope() *Scope

func (*StatementCtx) Set

func (c *StatementCtx) Set(k string, v interface{})

type StatementOptions

type StatementOptions struct {
	Slots *Slots

	// 渲染组件时, 组件上的props
	// 如
	//   <Menu :data="data">
	//   <slot name="default">
	Props *Props

	// 如果是渲染tag, scope是当前组件的scope(如果在For语句中, 则也有For的scope).
	// 如果渲染其他组件, scope也是当前组件的scope.
	Scope *Scope

	// 上一层参数, 用于:
	// - 渲染slot时获取声明slot时的scope.
	// - 渲染slot时获取上一层的slots, 从中取出slot渲染. (slot组件自己的slot是备选内容)
	Parent *StatementOptions
}

执行语句(组件/Tag)所需的参数

type Store

type Store interface {
	Get(key string) (interface{}, bool)
	Set(key string, val interface{})
}

type StrStatement

type StrStatement struct {
	Str string
}

静态字符串块

func (*StrStatement) Exec

func (s *StrStatement) Exec(ctx *StatementCtx, _ *StatementOptions) error

type StringSpan

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

func (*StringSpan) Result

func (s *StringSpan) Result() string

type Styles

type Styles = parser.Styles

type Vpl

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

常驻实例, 一个程序只应该有一个实例. 在运行期间是无副作用的

func New

func New(options ...Options) *Vpl

New return a Vpl instance, This instance should be shared in multiple renderings. The recommended practice is to have only one Vpl instance for the whole program.

func (*Vpl) Component

func (v *Vpl) Component(name string, c Statement) (err error)

func (*Vpl) ComponentFile

func (v *Vpl) ComponentFile(name string, path string) (err error)

Declare a component by file

func (*Vpl) ComponentTxt

func (v *Vpl) ComponentTxt(name string, txt string) (err error)

Declare a component by txt

func (*Vpl) Directive

func (v *Vpl) Directive(name string, val Directive)

Directive 声明一个指令

func (*Vpl) Function

func (v *Vpl) Function(name string, val Function)

Function 是对 Global 方法设置全局方法的再次封装

func (*Vpl) Global

func (v *Vpl) Global(name string, val interface{})

Global 设置全局变量, 在所有的组件中都生效 也可用于设置全局方法

func (*Vpl) NewScope

func (v *Vpl) NewScope() *Scope

func (*Vpl) RenderComponent

func (v *Vpl) RenderComponent(component string, p *RenderParam) (html string, err error)

渲染一个已经编译好的组件

func (*Vpl) RenderTpl

func (v *Vpl) RenderTpl(tpl string, p *RenderParam) (html string, err error)

tpl e.g.: <main v-bind="$props"></main>

type Writer

type Writer interface {
	// 如果需要实现异步计算, 则需要将span存储, 在最后统一计算出string.
	WriteSpan(Span)
	// 如果是同步计算, 使用WriteString会将string结果直接存储或者拼接
	WriteString(string)
	Result() string
}

Directories

Path Synopsis
example
internal
lib/parse/html
Package html is an HTML5 lexer following the specifications at http://www.w3.org/TR/html5/syntax.html.
Package html is an HTML5 lexer following the specifications at http://www.w3.org/TR/html5/syntax.html.

Jump to

Keyboard shortcuts

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