模板引擎必须先注册函数再解析文件,否则报“function not defined”;自定义函数须首字母大写、签名匹配、无副作用,fiber中html.new()需在parseglob前调funcs()。

模板引擎必须先注册函数再解析文件
直接 template.ParseFiles() 后再调 .Funcs() 是无效的——函数不会被任何已解析的模板识别,渲染时必报 function "xxx" not defined。Fiber 本身不绑定模板引擎,你得自己用 html.New() 或 text/template 初始化,并严格遵守“注册 → 解析 → 渲染”顺序。
常见错误写法:tmpl := template.Must(template.ParseFiles("404.html"))tmpl.Funcs(myFuncs) —— 这行完全白做,myFuncs 对 404.html 毫无作用。
- ✅ 正确链式调用:
t, err := template.New("404.html").Funcs(myFuncs).ParseFiles("404.html") - ✅ 若用
html.New()(Fiber 推荐),也一样:engine := html.New("./views", ".html").Funcs(myFuncs),且必须在ParseGlob前完成 - ⚠️
template.New()的名字参数建议和文件名一致(如"404.html"),否则嵌套{{template "xxx"}}时可能找不到子模板
自定义函数必须是首字母大写的导出函数
模板引擎只认导出函数,func formatTime(t time.Time, layout string) string 可以,func formattime(...) 或匿名函数会被静默忽略,报错时只说“function not defined”,不提示“未导出”。
- 导出函数签名必须明确:参数类型、返回值数量/类型要和模板中调用方式严格匹配,比如
{{.CreatedAt | formatTime "2006-01-02"}}要求.CreatedAt是time.Time,不是字符串 - 别在
FuncMap里传闭包或带状态的函数——模板执行是无状态的,运行时无法捕获外部变量 - 函数返回
error时,模板不会中断渲染,也不会暴露错误;想处理失败,得函数内部转成字符串或配合{{if}}判断
Fiber 的 html.Engine 注册方式稍有不同
如果你用的是 Fiber 官方推荐的 html.New() 模板引擎(而非原生 text/template),注册函数要走 .AddFunc() 或 .Funcs(),但注意调用时机:
-
.AddFunc("upper", strings.ToUpper)可随时调用,但仅对后续新解析的模板生效 -
.Funcs(map[string]interface{}{"upper": strings.ToUpper})必须在.ParseGlob()或.Load()之前调用,否则无效 - 若你已调过
engine.Load(),再补.Funcs(),新加的函数不会进已加载的模板——得重启服务或重新Load() - Fiber 的
c.Render("404", nil)是同步阻塞调用,所以可以在NotFoundhandler 中安全使用,前提是模板和函数都已就绪
容易被忽略的类型断言和多返回值陷阱
模板传参一律是 interface{},函数内部必须自己做类型检查,不能依赖自动转换。
- 例如
{{index .Items 0 | myFunc}},myFunc签名若为func myFunc(s string) string,但index返回的是interface{},就会 panic:需写成func myFunc(v interface{}) string并手动v.(string)断言 - 函数返回多个值(如
func split(s, sep string) (string, string)),模板里只能拿到第一个,其余丢弃,且无法解构赋值 - 所有函数逻辑应尽量无副作用:不读写全局变量、不发起 HTTP 请求、不操作文件——模板渲染可能被并发复用,IO 类操作极易引发竞态或超时
Funcs 都没用;而函数未导出或签名不匹配,错误信息又极其含糊。











