我们都知道在golang的模板语法中,我们可以使用template关键字嵌套其他模块, 如: {{template "模板文件名" .}} 然而,这里的 “模板文件名” 是不能使用变量的! 注意这里最后的的 . 这个实际上是template关键字的第二个参数, 在go的模板里面 . 它代表的是当前的所有变量数据,即你在执行渲染模板时的第二个data参数,函数原型 func (t *template.Template) Execute(wr io.Writer, data any) error 了解这点很重要!
底层源码分析
golang的template模板解析底层是通过一个叫做 lexer 的对象来实现的。在lexer里面这里的 template 被作为一个关键字, 和其他的 if else end, range ,with等一样的关键字。 而这里的模板名在go底层在解析前就被使用Quote解码了, 即 strconv.Quote(模板文件名),所以你在这里输入的任何变量都不会被解析!
go底层代码见源码文件 /src/text/template/parse/node.go
template这个关键字的第二个参数的处理代码:
func (t *TemplateNode) writeTo(sb *strings.Builder) {
sb.WriteString("{{template ")
sb.WriteString(strconv.Quote(t.Name))
if t.Pipe != nil {
sb.WriteByte(' ')
t.Pipe.writeTo(sb)
}
sb.WriteString("}}")
}
lexer模板渲染里面的关键字参考
可以看到这里有我们熟悉的 . if else range with 还有template 这些在golang的模板里面都被用作了关键字
var key = map[string]itemType{
".": itemDot,
"block": itemBlock,
"break": itemBreak,
"continue": itemContinue,
"define": itemDefine,
"else": itemElse,
"end": itemEnd,
"if": itemIf,
"range": itemRange,
"nil": itemNil,
"template": itemTemplate,
"with": itemWith,
}
lexer模板分隔符解析逻辑和默认分隔符参考
可见对于模板分隔符,如果我们传递的是一个空字符串,则lexer就会使用他自己 定义的常量 {{ }}来作为分隔符
// state functions
const (
leftDelim = "{{"
rightDelim = "}}"
leftComment = "/*"
rightComment = "*/"
)
// lex creates a new scanner for the input string.
func lex(name, input, left, right string) *lexer {
if left == "" {
left = leftDelim
}
if right == "" {
right = rightDelim
}
l := &lexer{
name: name,
input: input,
leftDelim: left,
rightDelim: right,
line: 1,
startLine: 1,
insideAction: false,
}
return l
}
总结:
在go的模板里面,实际上他所有的关键字最终编译器层面基本上都是以函数的方式运行的。 这里的template关键字 的第二个参数在递交编译器之前被 Quote 了一把, 所以这里任何的变量都不会被解析,从而导致了template模板嵌套语法 不能使用任何的变量!
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » golang template模板嵌套语法 为何不能使用变量 底层源码解析
发表评论 取消回复