Jet模板引擎渲染方法

渲染模板在Jet中称之为“执行”,在准备好即将渲染的模板文件和变量渲染为最终的HTML字符。

下面这段代码是将名称为home.jet的模板渲染为网页的使用方式。

  templateName := "home.jet"
  t, err := set.GetTemplate(templateName)
  if err != nil {
      // template could not be loaded
  }
  var w bytes.Buffer // needs to conform to io.Writer interface (like gin's context.Writer for example)
  vars := make(jet.VarMap)
  if err = t.Execute(&w, vars, nil); err != nil {
      // error when executing template
  }

需要注意上面代码中的vars和第三个参数,此处为nil,接下来解释参数的作用。

加载模板

当实例化模板对象的时候,Set可以返回一个模板对象。但此时模板引擎并不会立刻搜索并加载模板文件。模板文件目录树如下:

|- main.go
|- README.go
|- views
   |- common
   |  |- _footer.jet
   |  |- _menu.jet
   |- auth
   |  |- _logo.jet
   |  |- login.jet
   |- home.jet
   |- layouts
      |- application.jet

Set初始化时你应该指定模板所在的目录位置,下面这行代码写在main.go中。

var viewSet = jet.NewHTMLSet("./views")

Jet的模板加载是需要制定模板文件路径的,如果你要使用login.jet模,那应该像下面这样使用。

t, err := viewSet.GetTemplate("auth/login.jet")

Jet加载模板并解析后会缓存解析结果,所以模板的解析只会执行一次。

开发模式中重新加载模板

当你在开发过程中,需要反复调试模板内容。所以模板缓存功能需要关闭,你可以设置开发模式来关闭模板缓存。

viewSet.SetDevelopmentMode(true)
Jet建议在线上业务中,关闭开发者模块,开启模板缓存来提高性能。

模板渲染中使用变量

当模板引擎渲染后,需要一个io.Writer对象以及变量和上下文。变量jet.VarMap是一个map对象,里面存储着模板中所需要使用的变量名以及对应的值。你可以使用Set(key, value)来添加对应的数据。

vars := make(jet.VarMap)
vars.Set("user", &User{})

值得注意的是,jet.VarMapmap[string]reflect.Value的重命名。如果在多个goroutines中使用同一个jet.VarMap对象,则是不安全的。你应该来自行处理或者增加竞争锁来保证业务正常。

下面是一个互斥的基本实现,你可以参考来使用它。

// VarMapMx defines a mutex-protected jet.VarMap that's usable concurrently.
type VarMapMx struct {
  mx     sync.RWMutex
  varMap jet.VarMap
}

// NewVarMapMx returns an initialized VarMapMx instance that is ready to be used.
func NewVarMapMx() *VarMapMx {
  return &VarMapMx{varMap: jet.VarMap{}}
}

// Get returns the value for a key in the embedded jet.VarMap.
// The returned reflect.Value is not valid if the key does not exist.
func (scope *VarMapMx) Get(name string) reflect.Value {
  v, _ := scope.Lookup(name)
  return v
}

// Lookup returns the value for a key in the embedded jet.VarMap
// as well as whether the key was found or not.
func (scope *VarMapMx) Lookup(name string) (reflect.Value, bool) {
  scope.mx.RLock()
  defer scope.mx.RUnlock()
  v, ok := scope.varMap[name]
  return v, ok
}

// Set adds the key value pair in the embedded jet.VarMap.
func (scope *VarMapMx) Set(name string, v interface{}) *VarMapMx {
  scope.mx.Lock()
  defer scope.mx.Unlock()

  scope.varMap.Set(name, v)
  return scope
}

// SetFunc adds a jet.Func to the embedded jet.VarMap.
func (scope *VarMapMx) SetFunc(name string, v jet.Func) *VarMapMx {
  scope.mx.Lock()
  defer scope.mx.Unlock()

  scope.varMap.Set(name, v)
  return scope
}

// SetWriter adds a jet.SafeWriter to the embedded jet.VarMap.
func (scope *VarMapMx) SetWriter(name string, v jet.SafeWriter) *VarMapMx {
  scope.mx.Lock()
  defer scope.mx.Unlock()

  scope.varMap.Set(name, v)
  return scope
}

// GetVarMap returns the embedded jet.VarMap for use in template executions.
func (scope *VarMapMx) GetVarMap() jet.VarMap {
  return scope.varMap
}

使用如下:

vars := NewVarMapMx()
vars.Set("user", &User{}).Set("userID", "1234")

if err = t.Execute(&w, vars.GetVarMap(), nil); err != nil {
    // error when executing template
}

最后一个参数是上下文,Jet允许使用任意对象作为上下文,并使用.在模板中访问上下文内容。例如:

<form action="/user" method="post">
  <input name="firstname" value="{{ .Firstname }}" />
</form>

模板中的块在复用是也可以滴啊用上下文,他们在模板中进行传递并保持不变。