text/template需处理字段导出、函数注册、路径定位三处:结构体字段首字母大写才可见;自定义函数如title须parse前用funcmap注册且签名严格匹配;parsefiles路径基于os.getwd()而非源码目录。

直接用 text/template 就行,不用换包、不需额外依赖——但它不是“写完模板就能跑”的傻瓜工具,字段导出、函数注册、路径定位这三处不处理好,Execute 一调就静默失败或 panic。
结构体字段必须首字母大写,否则模板里 {{.Name}} 永远是空
Go 的反射机制只暴露导出字段(即首字母大写),text/template 完全依赖反射读取数据。小写字段如 name string 在模板中求值结果为 <no value></no>,不会报错,只会渲染成空字符串或跳过整个块。
- 正确写法:
type User { Name string; Email string },模板中写{{.Name}} - 错误写法:
type User { name string; email string },即使传了值,{{.name}}也不生效 - 调试建议:在
Execute前加fmt.Printf("%#v\n", user),确认打印出的是带大写字母字段的结构体值,而不是main.User{}或nil - map 也能用,但得写
{{.Name}}(键名匹配),且声明为map[string]interface{};不过 IDE 无法提示、类型不安全,不推荐作为主力传参方式
自定义函数如 strings.Title 必须显式注册,且签名要严丝合缝
text/template 默认只带 len、print、printf、index 等极少数函数。title、toUpper、urlquery 全都不在其中——写了 {{.Name | title}} 不会报错,也不会执行,只是静默忽略管道右侧,最终输出原始 .Name 值(如果它非空)。
- 注册必须在
Parse之前:t := template.New("").Funcs(template.FuncMap{"title": strings.Title}) - 函数签名只能是两种之一:
func(in T) U或func(in T) (U, error);比如strings.Title接收string、返回string,完全匹配 - 常见 panic 场景:
func(*string) string却传入值类型;或func(string) (string, string)(第二返回值不是error) - 管道调用时,左侧值类型必须和函数入参类型一致;
{{.Age | title}}会 panic,因为Age是int,而title只接受string
从文件加载模板时,路径是相对于 os.Getwd(),不是源码目录
template.ParseFiles("tmpl/email.tmpl") 报 pattern matches no files,大概率不是模板语法问题,而是当前工作目录和你预期的不一致。Go 运行时不会自动切换到 main.go 所在目录,os.Getwd() 返回的是你执行 go run 命令时所在的路径。
- 临时解法:运行前
cd到项目根目录再执行,确保tmpl/路径可访问 - 可靠解法:改用
template.ParseGlob("templates/*.tmpl"),它对路径容错更强 - 生产推荐:用
//go:embed打包进二进制:var tmplFS embed.FS+template.ParseFS(tmplFS, "templates/*.tmpl") -
template.Must要用在Parse阶段,它会在解析失败时立刻 panic,避免把语法错误拖到Execute时才暴露
嵌套模板 define / template 不生效?注意名称作用域和执行上下文
{{define "header"}} 只是注册一个命名模板,不会自动渲染;必须显式用 {{template "header" .}} 调用,且点号 . 表示将当前上下文数据传进去。漏掉 . 或写成 {{template "header"}},子模板就收不到数据,字段访问全部失效。
-
{{define "header"}}Hi {{.Name}}{{end}}定义后,必须用{{template "header" .}}才能正确传入当前数据 - 若子模板需要不同数据,可传具体字段:
{{template "item" .User}},但注意.User不能是nil - 多个
define可共存,但名称不能重复;重复定义会导致Parse失败(template.Must会 panic) - 不支持跨文件
define,所有define必须在同一个Parse调用中加载的模板内容里
最易被忽略的是:字段未导出 + 函数未注册 + 路径错位,这三个问题叠加时,模板既不报错也不输出,只默默返回空内容——这时候别猜,先打 fmt.Printf 看传了什么、再查 os.Getwd() 是哪、最后确认 FuncMap 是不是链式调用到了同一个 template 实例上。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











