Gin模板分隔符与Vue/React冲突时需调用Delims修改为{[ ]};静态资源路由应配合NoRoute返回index.html;HTML内容需用template.HTML类型绕过转义;embed资源须用ParseFS加载而非LoadHTMLGlob。

模板分隔符冲突:Vue/React 用 {{ }},Gin 也用 {{ }}
当 Gin 渲染 HTML 模板时,如果前端框架(比如 Vue 或 React)也用 {{ }} 作为插值语法,浏览器还没加载 JS,Gin 就先尝试解析这些标记——结果要么报错(template: xxx:12: unexpected unclosed action),要么把前端变量当成空值或 nil 渲染出来。
根本不是“谁该让步”,而是 Gin 的 html/template 在服务端就执行了渲染,而 Vue 的 {{ }} 是客户端运行时才生效。两者在同一份 HTML 文件里共存,必然打架。
- 最直接的解法是改 Gin 模板的分隔符:调用
Delims方法,在r.LoadHTMLGlob前设置非冲突符号,例如{{=和=}} - 代码示例:
r := gin.Default() r.Delims("{[", "]}") r.LoadHTMLGlob("templates/*") - 之后模板里写
{[ .Title ]},Vue 仍可自由用{{ title }},互不干扰 - 注意:修改分隔符后,所有已有模板都要同步替换,否则
LoadHTMLGlob会因语法错误 panic
静态资源路由劫持:/dashboard 匹配了 r.StaticFS,但其实是 Vue Router 路由
用 r.StaticFS("/","static/dist") 把整个前端打包目录挂载到根路径,看似方便,实则危险——Gin 会优先匹配静态文件,/dashboard 这种前端路由若在 static/dist 下没有对应物理文件(它本就不该有),Gin 直接返回 404,Vue Router 根本没机会启动。
这不是 Gin 的 bug,而是 StaticFS 的设计逻辑:它只管“有没有这个文件”,不管“前端要不要接管这个路径”。
- 正确做法是用
NoRoute中间件兜底:先让所有 API 路由(如/api/xxx)正常走,其余未匹配路径统一返回index.html - 必须确保
NoRoute在所有 API 路由注册之后调用,顺序错了就全被静态规则吃掉 - 示例关键段:
r.NoRoute(func(c *gin.Context) { c.Header("Content-Type", "text/html; charset=utf-8") c.File("static/dist/index.html") }) - 别用
c.String或c.Data返回 HTML 字符串,它们不触发浏览器的 JS 执行上下文;c.File才能保留下 CSS/JS 的相对路径解析
HTML 内容不转义:{{ .RawHTML }} 显示为纯文本而非渲染
你传入一个含 hello 的字符串,模板里写 {{ .RawHTML }},结果页面上原样输出标签字符,而不是加粗的 hello——这是因为 html/template 默认对所有变量做 HTML 转义,防 XSS。
这不是安全过度,而是默认行为。想绕过,必须显式声明内容可信。
- 在 Go 代码中,用
html/template包的template.HTML类型包装字符串:import "html/template" c.HTML(200, "page.tmpl", gin.H{ "content": template.HTML("<b>hello</b>"), }) - 模板里仍写
{{ .content }},不会额外加|safe或其他过滤器 - 切记:仅对完全可控、无用户输入的内容这么做;混入用户提交字段时,必须保留转义
- 如果数据来自
interface{}且类型不确定,先断言再转换:template.HTML(fmt.Sprintf("%v", v)),但要确保v本身不含恶意脚本
嵌入式资源(embed)与模板路径解析不一致
用 Go 1.16+ //go:embed 把 templates/ 目录打进二进制,本地跑得通,部署后却报 panic: html/template: pattern matches no files——常见于 GAE 或容器环境。
原因不是 embed 失效,而是 LoadHTMLGlob 底层仍依赖 filepath.Glob,而后者在 embed 场景下无法识别虚拟文件系统路径,它只认真实磁盘路径。
- embed 的资源不能直接喂给
LoadHTMLGlob;必须改用LoadHTMLFiles+embed.FS显式读取 - 示例:
import "embed" //go:embed templates/*.html var tplFS embed.FS func main() { r := gin.Default() tmpl := template.Must(template.New("").ParseFS(tplFS, "templates/*.html")) r.SetHTMLTemplate(tmpl) } - 注意:Gin 的
SetHTMLTemplate接收的是*template.Template,不是路径字符串;ParseFS是标准库函数,不是 Gin 提供的 - 一旦用了 embed,就别再混用
LoadHTMLGlob或LoadHTMLFiles("./templates/..."),路径语义已失效
真正容易被忽略的是:模板分隔符和 embed 加载这两件事,看起来是“配置项”或“小技巧”,实则决定了整个渲染链路的起点是否成立。一旦出错,错误信息往往指向下游(比如 NoRoute 不生效),但根子在初始化阶段的模板加载或分隔符设定。调试时别急着改路由,先确认 r.SetHTMLTemplate 是否成功、Delims 是否在 LoadHTMLGlob 前调用。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











