本文档详细描述了模板引擎的所有 API 接口,包括多主题功能的完整使用方法。
Engine 是模板引擎的核心结构体,负责管理模板加载、渲染和主题切换。
type Engine struct {
// 私有字段...
}func NewEngine(templatesDir string, loadFunc LoadTemplateFunc, funcMap FuncMap, opts ...Option) (*Engine, error)创建新的模板引擎实例。
参数:
templatesDir(string): 模板目录路径loadFunc(LoadTemplateFunc): 模板加载函数funcMap(FuncMap): 自定义模板函数映射opts(...Option): 可变配置选项
返回值:
*Engine: 引擎实例error: 错误信息
示例:
// 基本使用
engine, err := template.NewEngine("./templates", template.DefaultLoadTemplate, nil)
// 带配置选项
engine, err := template.NewEngine("./templates", template.DefaultLoadTemplate, funcMap,
template.EnableMultiTheme(true),
template.DefaultTheme("default"),
template.Theme("dark"),
template.GlobalConstant(map[string]interface{}{
"siteName": "我的网站",
"version": "1.0.0",
}),
)func NewEngineWithEmbedFS(tmplFS *embed.FS, subDir string, loadFunc LoadEmbedFSTemplateFunc, funcMap FuncMap, opts ...Option) (*Engine, error)使用嵌入式文件系统创建模板引擎。
参数:
tmplFS(*embed.FS): 嵌入式文件系统subDir(string): 模板子目录路径loadFunc(LoadEmbedFSTemplateFunc): 嵌入式文件系统加载函数funcMap(FuncMap): 自定义模板函数映射opts(...Option): 可变配置选项
示例:
//go:embed templates/*
var templatesFS embed.FS
engine, err := template.NewEngineWithEmbedFS(&templatesFS, "templates",
template.DefaultLoadEmbedFSTemplate, funcMap,
template.EnableMultiTheme(true),
template.Theme("default"),
)func EnableMultiTheme(enable bool) Option启用或禁用多主题模式。
参数:
enable(bool): 是否启用多主题模式
示例:
template.EnableMultiTheme(true) // 启用多主题
template.EnableMultiTheme(false) // 禁用多主题(默认)func Theme(themeName string) Option设置当前使用的主题。
参数:
themeName(string): 主题名称
示例:
template.Theme("default") // 使用默认主题
template.Theme("dark") // 使用深色主题func DefaultTheme(themeName string) Option设置默认主题,当指定的主题不存在时会回退到此主题。
参数:
themeName(string): 默认主题名称
示例:
template.DefaultTheme("default")func GlobalConstant(constant map[string]interface{}) Option设置全局常量,在所有模板中可通过 {{ .constant.key }} 访问。
参数:
constant(map[string]interface{}): 常量映射
示例:
template.GlobalConstant(map[string]interface{}{
"siteName": "我的网站",
"version": "1.0.0",
"author": "开发者",
})func GlobalVariable(variable map[string]interface{}) Option设置全局变量,在所有模板中可通过 {{ .variable.key }} 访问。
参数:
variable(map[string]interface{}): 变量映射
示例:
template.GlobalVariable(map[string]interface{}{
"year": time.Now().Year(),
"timestamp": time.Now().Unix(),
})func (e *Engine) Init() error初始化模板引擎,加载模板文件。
返回值:
error: 初始化错误
示例:
if err := engine.Init(); err != nil {
log.Fatal("引擎初始化失败:", err)
}func (e *Engine) Close()关闭模板引擎,清理资源。
示例:
defer engine.Close()func (e *Engine) Watching() error启动文件监听功能,自动重载模板文件。
返回值:
error: 监听启动错误
示例:
if err := engine.Watching(); err != nil {
log.Fatal("文件监听启动失败:", err)
}
// 监听错误
go func() {
for err := range engine.Errors {
log.Printf("文件监听错误: %v", err)
}
}()func (e *Engine) RenderPage(w io.Writer, name string, data interface{}) error渲染页面模板。
参数:
w(io.Writer): 输出写入器name(string): 模板名称data(interface{}): 模板数据
返回值:
error: 渲染错误
示例:
data := template.H{
"title": "文章列表",
"posts": posts,
}
err := engine.RenderPage(w, "posts/list", data)func (e *Engine) RenderSingle(w io.Writer, name string, data interface{}) error渲染单页模板(不使用布局)。
参数:
w(io.Writer): 输出写入器name(string): 模板名称data(interface{}): 模板数据
示例:
data := template.H{"title": "登录"}
err := engine.RenderSingle(w, "login", data)func (e *Engine) RenderError(w io.Writer, name string, data interface{}) error渲染错误页面模板。
参数:
w(io.Writer): 输出写入器name(string): 错误模板名称(如 "404", "500")data(interface{}): 模板数据
示例:
data := template.H{
"title": "页面未找到",
"message": "请求的页面不存在",
"path": r.URL.Path,
}
err := engine.RenderError(w, "404", data)func (e *Engine) GetAvailableThemes() []string获取所有可用主题的名称列表。
返回值:
[]string: 主题名称列表
示例:
themes := engine.GetAvailableThemes()
fmt.Printf("可用主题: %v\n", themes)
// 输出: 可用主题: [default dark colorful]func (e *Engine) GetCurrentTheme() string获取当前激活的主题名称。
返回值:
string: 当前主题名称
示例:
current := engine.GetCurrentTheme()
fmt.Printf("当前主题: %s\n", current)
// 输出: 当前主题: darkfunc (e *Engine) SwitchTheme(themeName string) error切换到指定的主题。
参数:
themeName(string): 目标主题名称
返回值:
error: 切换错误
示例:
// 切换到深色主题
if err := engine.SwitchTheme("dark"); err != nil {
log.Printf("主题切换失败: %v", err)
return
}
log.Println("主题切换成功")type H map[string]interface{}H 是 map[string]interface{} 的别名,用于简化模板数据的创建。
示例:
data := template.H{
"title": "页面标题",
"user": User{Name: "张三", Age: 25},
"items": []string{"item1", "item2", "item3"},
}type FuncMap map[string]interface{}FuncMap 用于定义自定义模板函数。
示例:
funcMap := template.FuncMap{
"formatDate": func(t time.Time) string {
return t.Format("2006-01-02 15:04:05")
},
"upper": strings.ToUpper,
"add": func(a, b int) int {
return a + b
},
}type ThemeManager interface {
DiscoverThemes() error
LoadTheme(name string) (*Theme, error)
GetAvailableThemes() []string
GetCurrentTheme() string
ThemeExists(name string) bool
GetThemeMetadata(name string) (*ThemeMetadata, error)
SwitchTheme(name string) error
GetRender() Render
ReloadCurrentTheme() error
}type Theme struct {
Name string `json:"name"`
Path string `json:"path"`
IsDefault bool `json:"is_default"`
IsEmbedded bool `json:"is_embedded"`
Metadata ThemeMetadata `json:"metadata"`
}type ThemeMetadata struct {
DisplayName string `json:"display_name"`
Description string `json:"description"`
Version string `json:"version"`
Author string `json:"author"`
Tags []string `json:"tags"`
Custom map[string]any `json:"custom"`
}type ThemeError struct {
Type ThemeErrorType `json:"type"`
Theme string `json:"theme"`
Message string `json:"message"`
Cause error `json:"-"`
}type ThemeErrorType int
const (
ErrThemeNotFound ThemeErrorType = iota
ErrThemeInvalid
ErrThemeLoadFailed
ErrThemeSwitchFailed
ErrThemeConfigInvalid
)错误类型说明:
ErrThemeNotFound: 主题不存在ErrThemeInvalid: 主题结构无效ErrThemeLoadFailed: 主题加载失败ErrThemeSwitchFailed: 主题切换失败ErrThemeConfigInvalid: 主题配置无效
package main
import (
"net/http"
"github.com/nilorg/template"
)
func main() {
engine, err := template.NewEngine("./templates", template.DefaultLoadTemplate, nil)
if err != nil {
panic(err)
}
defer engine.Close()
engine.Init()
engine.Watching()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
data := template.H{"title": "首页"}
engine.RenderPage(w, "home", data)
})
http.ListenAndServe(":8080", nil)
}package main
import (
"net/http"
"github.com/nilorg/template"
)
func main() {
engine, err := template.NewEngine("./templates", template.DefaultLoadTemplate, nil,
template.EnableMultiTheme(true),
template.DefaultTheme("default"),
template.Theme("default"),
)
if err != nil {
panic(err)
}
defer engine.Close()
engine.Init()
engine.Watching()
// 主题管理
http.HandleFunc("/themes", func(w http.ResponseWriter, r *http.Request) {
data := template.H{
"availableThemes": engine.GetAvailableThemes(),
"currentTheme": engine.GetCurrentTheme(),
}
engine.RenderPage(w, "themes", data)
})
// 主题切换
http.HandleFunc("/switch-theme", func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", 405)
return
}
themeName := r.FormValue("theme")
if err := engine.SwitchTheme(themeName); err != nil {
http.Error(w, err.Error(), 500)
return
}
http.Redirect(w, r, "/themes", 302)
})
http.ListenAndServe(":8080", nil)
}package main
import (
"embed"
"net/http"
"github.com/nilorg/template"
)
//go:embed templates/*
var templatesFS embed.FS
func main() {
engine, err := template.NewEngineWithEmbedFS(&templatesFS, "templates",
template.DefaultLoadEmbedFSTemplate, nil,
template.EnableMultiTheme(true),
template.Theme("default"),
)
if err != nil {
panic(err)
}
defer engine.Close()
engine.Init()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
data := template.H{"title": "嵌入式应用"}
engine.RenderPage(w, "home", data)
})
http.ListenAndServe(":8080", nil)
}// 渲染时的错误处理
func renderPage(w http.ResponseWriter, r *http.Request, engine *template.Engine) {
data := template.H{"title": "页面"}
if err := engine.RenderPage(w, "page", data); err != nil {
log.Printf("渲染失败: %v", err)
// 渲染错误页面
errorData := template.H{
"title": "服务器错误",
"message": "页面渲染失败",
}
if renderErr := engine.RenderError(w, "500", errorData); renderErr != nil {
http.Error(w, "Internal Server Error", 500)
}
}
}func switchTheme(engine *template.Engine, themeName string) error {
// 验证主题名称
availableThemes := engine.GetAvailableThemes()
found := false
for _, theme := range availableThemes {
if theme == themeName {
found = true
break
}
}
if !found {
return fmt.Errorf("主题 '%s' 不存在", themeName)
}
// 执行切换
return engine.SwitchTheme(themeName)
}// 缓存引擎实例
var engineInstance *template.Engine
var engineOnce sync.Once
func getEngine() *template.Engine {
engineOnce.Do(func() {
var err error
engineInstance, err = template.NewEngine("./templates",
template.DefaultLoadTemplate, nil,
template.EnableMultiTheme(true),
)
if err != nil {
panic(err)
}
engineInstance.Init()
})
return engineInstance
}多主题功能在 v2.0 中引入,完全向后兼容 v1.x 版本:
// v1.x 代码无需修改
engine, err := template.NewEngine("./templates", template.DefaultLoadTemplate, nil)
// v2.x 新功能(可选)
engine, err := template.NewEngine("./templates", template.DefaultLoadTemplate, nil,
template.EnableMultiTheme(true), // 新增选项
)- 所有 v1.x 的 API 在 v2.x 中保持不变
- 新增的多主题 API 使用独立的方法名
- 配置选项采用可选参数模式,不影响现有代码