golang实现Closure模板规范的Soy模板引擎插件Soy的使用

golang实现Closure模板规范的Soy模板引擎插件Soy的使用

Go语言实现的Soy模板引擎是遵循Google Closure Templates规范的模板引擎。下面是一个完整的使用示例:

安装

go get github.com/robfig/soy

基本使用示例

package main

import (
	"fmt"
	"github.com/robfig/soy"
	"github.com/robfig/soy/data"
)

func main() {
	// 1. 创建模板集合
	templateSet := soy.NewSet()

	// 2. 添加模板内容
	err := templateSet.AddTemplate("example.soy", `
{namespace example}

/**
 * Greets a person using "Hello" by default.
 * @param name The person's name.
 * @param? greetingWord Optional greeting word to use instead of "Hello".
 */
{template .hello}
  {$greetingWord ?: 'Hello'}, {$name}!
{/template}
`)
	if err != nil {
		panic(err)
	}

	// 3. 编译模板集合
	if err := templateSet.Compile(); err != nil {
		panic(err)
	}

	// 4. 渲染模板
	// 创建模板参数
	params := data.Map{
		"name": data.String("World"),
	}

	// 渲染模板
	result, err := templateSet.Render("example.hello", params)
	if err != nil {
		panic(err)
	}

	fmt.Println(result) // 输出: Hello, World!
}

带参数的模板示例

package main

import (
	"fmt"
	"github.com/robfig/soy"
	"github.com/robfig/soy/data"
)

func main() {
	templateSet := soy.NewSet()
	
	err := templateSet.AddTemplate("user.soy", `
{namespace user}

/**
 * Displays user information
 * @param user The user object containing name and email
 */
{template .profile}
  <div class="profile">
    <h1>{$user.name}</h1>
    <p>Email: {$user.email}</p>
    {if $user.age}
      <p>Age: {$user.age}</p>
    {/if}
  </div>
{/template}
`)
	if err != nil {
		panic(err)
	}

	if err := templateSet.Compile(); err != nil {
		panic(err)
	}

	// 创建用户数据
	userData := data.Map{
		"user": data.Map{
			"name":  data.String("John Doe"),
			"email": data.String("john@example.com"),
			"age":   data.Int(30),
		},
	}

	result, err := templateSet.Render("user.profile", userData)
	if err != nil {
		panic(err)
	}

	fmt.Println(result)
	/* 输出:
	<div class="profile">
		<h1>John Doe</h1>
		<p>Email: john@example.com</p>
		<p>Age: 30</p>
	</div>
	*/
}

循环和条件示例

package main

import (
	"fmt"
	"github.com/robfig/soy"
	"github.com/robfig/soy/data"
)

func main() {
	templateSet := soy.NewSet()
	
	err := templateSet.AddTemplate("products.soy", `
{namespace products}

/**
 * Displays a list of products
 * @param products List of products to display
 */
{template .list}
  <ul>
    {foreach $product in $products}
      <li>
        {$product.name} - ${$product.price}
        {if $product.inStock}
          (In Stock)
        {else}
          (Out of Stock)
        {/if}
      </li>
    {ifempty}
      <li>No products available</li>
    {/foreach}
  </ul>
{/template}
`)
	if err != nil {
		panic(err)
	}

	if err := templateSet.Compile(); err != nil {
		panic(err)
	}

	// 创建产品列表数据
	products := data.List{
		data.Map{
			"name":    data.String("Laptop"),
			"price":   data.Float(999.99),
			"inStock": data.Bool(true),
		},
		data.Map{
			"name":    data.String("Phone"),
			"price":   data.Float(699.99),
			"inStock": data.Bool(false),
		},
	}

	result, err := templateSet.Render("products.list", data.Map{"products": products})
	if err != nil {
		panic(err)
	}

	fmt.Println(result)
	/* 输出:
	<ul>
		<li>Laptop - $999.99 (In Stock)</li>
		<li>Phone - $699.99 (Out of Stock)</li>
	</ul>
	*/
}

注意事项

  1. 项目需要Go 1.12或更高版本
  2. 使用前确保设置环境变量 GO111MODULE=on 以启用go mod依赖版本管理
  3. 模板语法遵循Google Closure Templates规范

更多关于golang实现Closure模板规范的Soy模板引擎插件Soy的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现Closure模板规范的Soy模板引擎插件Soy的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang实现Soy模板引擎插件

Soy模板(又称Closure模板)是Google开发的一种模板语言,主要用于JavaScript和Java环境。下面我将介绍如何在Golang中实现一个支持Soy模板规范的插件。

基本实现思路

在Golang中实现Soy模板引擎,我们需要:

  1. 解析Soy模板语法
  2. 构建模板AST(抽象语法树)
  3. 实现模板渲染逻辑
  4. 提供数据绑定接口

示例实现

以下是一个简化版的Soy模板引擎实现:

package soy

import (
	"bytes"
	"fmt"
	"regexp"
	"strings"
	"text/template"
)

// SoyEngine 是Soy模板引擎的核心结构
type SoyEngine struct {
	templates map[string]*template.Template
	funcMap   template.FuncMap
}

// New 创建一个新的Soy模板引擎
func New() *SoyEngine {
	return &SoyEngine{
		templates: make(map[string]*template.Template),
		funcMap:   make(template.FuncMap),
	}
}

// RegisterFunc 注册自定义函数
func (e *SoyEngine) RegisterFunc(name string, fn interface{}) {
	e.funcMap[name] = fn
}

// Parse 解析Soy模板
func (e *SoyEngine) Parse(name, content string) error {
	// 预处理Soy模板,转换为Go模板语法
	goTpl, err := soyToGoTemplate(content)
	if err != nil {
		return err
	}

	tpl := template.New(name).Funcs(e.funcMap)
	tpl, err = tpl.Parse(goTpl)
	if err != nil {
		return err
	}

	e.templates[name] = tpl
	return nil
}

// Render 渲染模板
func (e *SoyEngine) Render(name string, data interface{}) (string, error) {
	tpl, ok := e.templates[name]
	if !ok {
		return "", fmt.Errorf("template %s not found", name)
	}

	var buf bytes.Buffer
	if err := tpl.Execute(&buf, data); err != nil {
		return "", err
	}

	return buf.String(), nil
}

// soyToGoTemplate 将Soy模板转换为Go模板语法
func soyToGoTemplate(soy string) (string, error) {
	// 替换变量引用 {$.var} -> {{.var}}
	re := regexp.MustCompile(`\{\$([a-zA-Z0-9_.]+)\}`)
	goTpl := re.ReplaceAllString(soy, "{{.$1}}")

	// 替换if语句 {if $var} -> {{if .var}}
	re = regexp.MustCompile(`\{if \$([a-zA-Z0-9_.]+)\}`)
	goTpl = re.ReplaceAllString(goTpl, "{{if .$1}}")

	// 替换foreach {foreach $item in $list} -> {{range .list}}
	re = regexp.MustCompile(`\{foreach \$([a-zA-Z0-9_]+) in \$([a-zA-Z0-9_.]+)\}`)
	goTpl = re.ReplaceAllString(goTpl, "{{range .$2}}")

	// 替换模板调用 {call .template} -> {{template "template" .}}
	re = regexp.MustCompile(`\{call ([a-zA-Z0-9_.]+)\}`)
	goTpl = re.ReplaceAllString(goTpl, `{{template "$1" .}}`)

	return goTpl, nil
}

使用示例

package main

import (
	"fmt"
	"github.com/yourusername/soy"
)

func main() {
	// 创建引擎实例
	engine := soy.New()

	// 注册自定义函数
	engine.RegisterFunc("upper", strings.ToUpper)

	// 定义Soy模板
	templateContent := `
{namespace example}

/**
 * 欢迎模板
 * @param name 用户名
 */
{template .welcome}
<html>
  <head><title>Welcome</title></head>
  <body>
    <h1>Hello, {$name}!</h1>
    {if $showDetails}
      <p>Your name in uppercase: {upper($name)}</p>
    {/if}
  </body>
</html>
{/template}
`

	// 解析模板
	if err := engine.Parse("welcome", templateContent); err != nil {
		panic(err)
	}

	// 准备数据
	data := map[string]interface{}{
		"name":       "John Doe",
		"showDetails": true,
	}

	// 渲染模板
	result, err := engine.Render("welcome", data)
	if err != nil {
		panic(err)
	}

	fmt.Println(result)
}

高级功能实现

要实现更完整的Soy模板支持,还需要添加以下功能:

  1. 命名空间支持
// 在soyToGoTemplate中处理命名空间
re = regexp.MustCompile(`\{namespace ([a-zA-Z0-9_.]+)\}`)
goTpl = re.ReplaceAllString(goTpl, "")
  1. 模板继承
// 处理模板继承关系
re = regexp.MustCompile(`\{delpackage ([a-zA-Z0-9_.]+)\}`)
goTpl = re.ReplaceAllString(goTpl, "")
  1. 更复杂的数据访问
// 处理嵌套数据访问 {$.user.name} -> {{.user.name}}
re = regexp.MustCompile(`\{\$([a-zA-Z0-9_.]+)\}`)
goTpl = re.ReplaceAllString(goTpl, "{{.$1}}")
  1. 循环索引
// 处理foreach循环索引
re = regexp.MustCompile(`\{foreach \$([a-zA-Z0-9_]+) in \$([a-zA-Z0-9_.]+)\}`)
goTpl = re.ReplaceAllString(goTpl, "{{range $index, $1 := .$2}}")

性能优化建议

  1. 预编译模板
  2. 使用缓存机制
  3. 实现模板片段缓存
  4. 使用并发安全的模板引擎

完整实现建议

对于生产环境使用,建议考虑以下开源项目:

  1. github.com/robfig/soy - 一个完整的Soy模板引擎实现
  2. github.com/go-template/soy - 另一个Golang Soy模板实现

这些项目提供了更完整的Soy模板规范支持,包括:

  • 完整的语法解析
  • 模板继承
  • 国际化支持
  • 自动转义
  • 模板组合

以上实现提供了一个基础框架,你可以根据实际需求进行扩展和完善。

回到顶部