vpl

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 13, 2020 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 Render Content.
    Props:  props, // Props to Render Component.
}

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>

If the template is executed in order, it will take 3s. To parallel them, you can wrap them with parallel component.

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

Now it only takes 2s.

Docs

IntelliJ Plugin

Just use the Vuejs plugin.

Dependencies

  • github.com/robertkrimen/otto: 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 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 struct {
	// Props: 无论动态还是静态, 都是Props
	//
	//  如: <Menu :data="data" id="abc" :style="{left: '1px'}">
	//  其中 Props 值为: data, id
	//  其中 PropStyle 值为: style
	Props propsC
	// PropClass指动态class
	//  如 <Menu :class="['a','b']" class="c">
	//  那么PropClass的值是: ['a', 'b']
	VBind *vBindC

	Directives directivesC
	// 传递给这个组件的Slots
	Slots *SlotsC
}

组件的属性

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