
在 Go 模板中,. 代表传入的顶层数据;若直接传入切片(如 []Item),则 . 就是该切片本身,无法通过 .resItems 访问——必须将多个切片封装进结构体或 map 后统一传递,再通过字段名(如 .Items、.Categories)分别遍历。
在 go 模板中,`.` 代表传入的顶层数据;若直接传入切片(如 `[]item`),则 `.` 就是该切片本身,无法通过 `.resitems` 访问——必须将多个切片封装进结构体或 map 后统一传递,再通过字段名(如 `.items`、`.categories`)分别遍历。
当你在 tmpl.ExecuteTemplate(w, "HomeTemplate", resItems) 中直接传入 []Item 类型的切片时,模板上下文中的 . 就等价于这个切片本身。因此 {{ range . }} 能正常工作——它逐个迭代切片元素;而 {{ range .resItems }} 会失败,因为 . 是一个切片(非结构体/映射),不支持点号访问字段,Go 模板会报错:cannot evaluate .resItems。
✅ 正确做法:使用结构体或 map 封装多个数据源。
推荐方案一:使用自定义结构体(类型安全、语义清晰)
// 定义模板数据结构
type HomeData struct {
Items []Item
Categories []string
Tags []string
}
// 在 handler 中构造并传递
func Index(w http.ResponseWriter, r *http.Request) {
db := database.DbConn()
defer db.Close() // 注意:defer 应在函数开头后立即声明,避免因 panic 跳过关闭
selDB, err := product.ByID()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer selDB.Close()
var resItems []Item
for selDB.Next() {
var id int
var name, typ string
if err := selDB.Scan(&id, &name, &typ); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
resItems = append(resItems, Item{Id: id, Name: name, Type: typ})
}
// 构造复合数据
data := HomeData{
Items: resItems,
Categories: []string{"Electronics", "Clothing", "Books"},
Tags: []string{"new", "sale", "featured"},
}
tmpl.ExecuteTemplate(w, "HomeTemplate", data)
}对应模板(tmpl/HomeTemplate.html):
<h2>Products</h2>
{{ range .Items }}
<div>ID: {{ .Id }}, Name: {{ .Name }}, Type: {{ .Type }}</div>
{{ end }}
<h2>Categories</h2>
{{ range .Categories }}
<span>{{ . }}</span>
{{ end }}
<h2>Tags</h2>
{{ range .Tags }}
<span class="tag">{{ . }}</span>
{{ end }}推荐方案二:使用 map[string]interface{}(灵活但无编译期检查)
data := map[string]interface{}{
"Items": resItems,
"Categories": []string{"Electronics", "Clothing"},
"Tags": []string{"hot", "trending"},
}
tmpl.ExecuteTemplate(w, "HomeTemplate", data)模板语法完全一致(仍用 {{ range .Items }}),适用于快速原型或动态字段场景。
⚠️ 注意事项:
- 避免在模板中做复杂逻辑(如嵌套循环判断),应提前在 Go 层组织好数据;
- 使用
defer时注意作用域:defer db.Close()应紧随db := database.DbConn()之后,否则可能在panic后未执行; - 若需传递单个对象 + 多个切片,结构体比 map 更易维护和测试;
- 模板中所有字段名(如
.Items)区分大小写,且首字母必须大写(Go 导出规则)才能被模板访问。
通过合理封装数据结构,你不仅能轻松支持两个或更多切片的并行渲染,还能提升代码可读性与模板复用性。

















