
在 Go 的 text/template 中,{{range}} 会将当前迭代项设为 .(即“dot”),导致无法直接访问传入模板的原始数据(如 .lang);解决方法是通过 $ 显式引用初始数据上下文。
在 go 的 `text/template` 中,`{{range}}` 会将当前迭代项设为 `.`(即“dot”),导致无法直接访问传入模板的原始数据(如 `.lang`);解决方法是通过 `$` 显式引用初始数据上下文。
Go 模板的执行模型基于“作用域上下文”(scope context),其中 . 是当前作用域的游标(cursor),而 $ 始终指向模板执行时传入的根数据对象(即 Execute(data) 中的 data)。当你写 {{range .users}} 时,. 被临时重置为每个 User 实例,因此 {{.lang}} 会失败——因为 User 结构体没有 lang 字段。
✅ 正确做法:用 $.lang 显式访问根数据中的 lang 键:
{{ range .users }}
{{ end }}
? 提示:
$是模板内置的只读全局变量,在任意嵌套层级中都稳定指向初始数据,无需额外声明或捕获。
进阶技巧:多层嵌套时的安全访问
若存在多层 range(例如 range .Groups → range .Users),$ 依然有效,但可进一步提升可读性与健壮性:
{{ range $group := .groups }}
<h3>Group: {{ $group.Name }}</h3>
{{ range $user := $group.Users }}
{{ end }} {{ end }}
此处 $group 和 $user 是显式命名的局部变量(通过 := 赋值),既避免了 . 的歧义,又保留了对 $.lang 的清晰引用。
⚠️ 注意事项与常见陷阱
-
结构体字段必须导出:若你传入的是结构体而非
map[string]interface{},确保Lang、Users等字段首字母大写(Lang,Users✅;lang,users❌),否则模板反射无法访问; -
不要混淆
.与$:{{.}}在range内是当前元素,{{$.users}}才是原始用户切片; -
避免滥用
with替代$:with会改变.并屏蔽$的直观性,仅在需临时切换作用域且逻辑明确时使用; -
类型安全建议:相比
map[string]interface{},推荐定义强类型模板数据结构(如type PageData struct { Lang string; Users []User }),获得编译期检查与 IDE 自动补全支持。
掌握 $ 的语义,是写出清晰、可维护 Go 模板的关键一步——它让数据流始终可追溯,杜绝“作用域丢失”类 panic。











