一、四种模板方案的核心区别
| 方案 | 核心机制 | 类型安全 | 性能 | 组件化 | 学习成本 | 推荐度 |
|---|---|---|---|---|---|---|
html/template |
运行时模板执行 | ★★ | ★★ | ★★ | ★★★★★ | ★★★★ |
templ |
编译为 Go 代码 | ★★★★★ | ★★★★★ | ★★★★★ | ★★★★ | ★★★★★ |
| Jet | 模板引擎运行时执行 | ★★★ | ★★★ | ★★★★ | ★★★★ | ★★★ |
| quicktemplate | 编译为 Go 代码 | ★★★ | ★★★★★ | ★★★ | ★★★ | ★★★ |
如果是新建 Go SSR 项目,优先考虑 templ。
如果是已有大量模板的传统 Go 项目,继续使用 html/template 通常更加合理。
二、html/template:Go 官方标准方案
html/template 是 Go 标准库的一部分,不需要第三方依赖。
tmpl := template.Must(
template.ParseFiles("index.html"),
)
tmpl.Execute(w, data)
HTML 模板:
<h1>{{.Title}}</h1>
{{if .IsLogin}}
<span>已登录</span>
{{end}}
{{range .Products}}
<div>{{.Name}}</div>
{{end}}
2.1 工作方式
大致流程如下:
HTML 模板
↓
Parse
↓
模板内部结构
↓
Execute
↓
HTML
模板需要在运行时进行执行。
它需要处理:
- 变量
- 条件
- 循环
- 模板嵌套
- 函数调用
- HTML escaping
- 上下文相关的安全处理
因此相比直接生成 Go 代码,会产生更多运行时开销。
三、templ:把 HTML 直接变成 Go 代码
templ 最大的区别是:
模板不是运行时解释执行,而是提前生成 Go 代码。
例如:
templ User(name string) {
<div>
<h1>Hello { name }</h1>
</div>
}
执行:
templ generate
之后生成 Go 代码,再通过 Go 编译器进行编译。
整体流程:
.templ
↓
templ generate
↓
Go 代码
↓
go build
↓
最终二进制
↓
运行时直接执行 Go 代码
因此运行时不需要再解释模板语法。
四、templ 为什么性能更高?
传统模板的执行过程大致是:
请求
↓
模板执行
↓
解析模板节点
↓
处理变量
↓
处理条件
↓
HTML escaping
↓
输出
templ 更接近:
请求
↓
执行已经编译好的 Go 代码
↓
输出 HTML
因此 templ 的运行时工作更少。
典型 benchmark 中,templ 的渲染速度可以达到 html/template 的数倍,同时内存分配次数也明显减少。
但不能简单理解为 templ 永远比 html/template 快固定的 5 倍或 10 倍。
实际性能取决于:
- HTML 复杂程度
- 数据量
- 循环数量
- 函数调用
- HTML escaping
- ResponseWriter
- 网络传输
- 数据库查询
对于大多数 Web 应用而言,模板渲染通常并不是整个请求的主要耗时。
五、templ 最大优势其实不是性能
templ 真正有价值的地方是:
Go 类型系统 + HTML 组件化。
例如:
templ ProductCard(product Product) {
<article class="product-card">
<img src={ product.Image }>
<h3>{ product.Name }</h3>
<span>{ product.Price }</span>
</article>
}
调用:
templ ProductList(products []Product) {
<div>
for _, product := range products {
@ProductCard(product)
}
</div>
}
这里的 ProductCard 是一个真正的 Go 类型安全组件。
如果传入错误的数据类型,编译阶段就可以发现问题。
六、html/template 的类型安全较弱
例如:
<h1>{{.Name}}</h1>
Go 代码:
type User struct {
Username string
}
模板实际上访问的是不存在的 .Name。很多问题需要到运行时才暴露。
而 templ:
<h1>{ user.Name }</h1>
本质上就是 Go 代码的一部分。
因此:
html/template
↓
模板
↓
运行时发现问题
而 templ:
templ
↓
生成 Go
↓
Go 编译器检查
↓
编译失败
对于大型项目,这个区别非常重要。
七、组件化能力对比
传统 html/template 通常通过模板调用实现复用:
{{template "header" .}}
大型项目容易逐渐形成:
templates/
├── layout.html
├── header.html
├── footer.html
├── product.html
├── product-list.html
└── user.html
随着项目增长,模板之间的数据依赖关系可能越来越难维护。
templ 更接近现代前端组件:
components/
├── Header.templ
├── Footer.templ
├── Navbar.templ
├── ProductCard.templ
├── Pagination.templ
└── Modal.templ
组件直接定义参数:
templ ProductCard(product Product) {
...
}
调用:
@ProductCard(product)
组件依赖关系更加明确。
八、Jet:传统模板引擎路线
Jet 属于典型的 Go 模板引擎。
使用方式更接近:
HTML
+
模板语法
+
模板引擎
相比 html/template,Jet 提供了更多模板层面的能力,例如:
- 模板继承
- 更丰富的模板语法
- 更灵活的控制结构
- 模板复用
因此如果团队非常习惯传统模板引擎,Jet 的开发体验可能比标准库模板更加丰富。
但是它仍然属于模板引擎路线,与 templ 的编译型方案存在明显区别。
九、quicktemplate:极致性能路线
quicktemplate 的设计目标非常明确:
尽可能降低模板渲染开销。
它同样采用代码生成思路,把模板转换成 Go 代码。
quicktemplate
↓
生成 Go 代码
↓
Go 编译
↓
运行
在特定 benchmark 中,它可以比传统 html/template 快很多。
但是性能并不是唯一指标。
quicktemplate 的主要取舍包括:
- 生态相对较小
- 开发体验不如现代组件化方案
- 类型安全和组件组织体验不如 templ
- 模板语法和普通 HTML、Go 的融合程度不如 templ
因此,如果单纯追求极限模板渲染性能,可以考虑 quicktemplate;如果追求综合开发体验,templ 更合适。
十、四种方案的性能思路
html/template
│
│ 运行时模板执行
↓
中等性能
Jet
│
│ 运行时模板执行
↓
中等性能
templ
│
│ 编译为 Go
↓
高性能
quicktemplate
│
│ 编译为 Go + 性能优化
↓
极高性能
但是实际 Web 请求通常包含:
HTTP 请求
↓
Router
↓
Middleware
↓
权限验证
↓
Redis
↓
数据库
↓
业务逻辑
↓
模板渲染
↓
网络传输
模板往往只是其中很小的一部分。
因此,不应该仅仅为了 benchmark 中的几倍性能,就重构整个模板系统。
十一、安全性对比
安全性是选择模板方案时比性能更加重要的一项。
11.1 html/template
Go 官方标准库重点解决了 HTML 上下文安全问题。
例如:
<div>{{.Content}}</div>
用户输入:
<script>alert('XSS')</script>
不会直接作为 HTML 执行。
11.2 templ
templ 同样提供 HTML escaping,并且通过生成 Go 代码实现渲染。
正确使用 templ 时,同样可以避免常见的 XSS 风险。
11.3 不要为了性能直接拼接 HTML
fmt.Fprintf(w, "<h1>%s</h1>", user.Name)
如果 user.Name 是用户输入,就可能产生 XSS。
正确原则是:
用户输入
↓
安全处理
↓
模板安全输出
而不是:
用户输入
↓
字符串拼接
↓
HTML
十二、templ 与 html/template 可以混用
不需要因为采用 templ 就一次性重构整个项目。
对于旧项目,可以采用:
现有系统
│
├── html/template
│ ├── 老页面
│ └── 老组件
│
└── templ
├── 新页面
└── 新组件
逐步迁移。
这对于已经存在大量 HTML 模板的项目尤其重要。
十三、SEO 方面谁更好?
如果都是服务端直接输出完整 HTML:
搜索引擎
↓
完整 HTML
↓
SEO
那么 templ、html/template、Jet、quicktemplate 本身没有本质 SEO 差距。
SEO 的关键在于:
- 是否 SSR
- HTML 是否完整
- Title
- Description
- Canonical
- Structured Data
- 页面内容
- 内链
- URL
- 页面速度
- Core Web Vitals
而不是模板引擎本身。
十四、与 HTMX 搭配
templ 非常适合:
Go
+
templ
+
HTMX
例如访问商品页面:
浏览器
↓
GET /products
↓
Go
↓
templ
↓
完整 HTML
用户点击分页或筛选:
浏览器
↓
HTMX
↓
GET /products?page=2
↓
Go
↓
templ
↓
HTML Fragment
↓
局部更新
这样可以避免引入大型 SPA 框架。
对于后台系统、电商网站、内容网站、社区、搜索网站和 SaaS 都非常适合。
十五、推荐技术栈
如果从零开始开发一个现代 Go SSR 网站,可以考虑:
Browser
│
↓
HTMX
│
↓
┌────────────────┐
│ Go Router │
└────────────────┘
│
↓
Controller
│
↓
Service
│
↓
Repository
/ \
↓ ↓
PostgreSQL Redis
│
↓
ViewModel
│
↓
templ
│
↓
HTML
前端:
templ
HTMX
Alpine.js(可选)
Tailwind CSS(可选)
后端:
Go
Gin / Echo / net/http
PostgreSQL
Redis
这套架构可以在不使用 React、Next.js 的情况下实现完整的现代 SSR 网站。
十六、最终选型建议
新项目
推荐:
templ
主要原因:
- 编译期检查
- Go 类型安全
- 性能高
- 组件化好
- IDE 支持好
- 与 Go 生态融合度高
- 适合 HTMX
- 适合 SSR
- 适合大型项目
已经使用 html/template 的项目
推荐继续使用。
除非已经遇到以下问题:
- 模板维护困难
- 类型错误频繁
- 组件复用困难
- 模板渲染成为实际性能瓶颈
否则没有必要为了性能整体迁移。
极端性能场景
可以考虑:
quicktemplate
适合对模板渲染本身进行极限优化的场景,但需要接受生态和开发体验上的取舍。
需要丰富模板语法
可以考虑:
Jet
但新项目通常没有足够强的理由优先选择它。
十七、一句话结论
| 场景 | 推荐 |
|---|---|
| Go 官方标准、简单稳定 | html/template |
| 新建 Go SSR 网站 | templ |
| 大型 Go SSR 项目 | templ |
| Go + HTMX | templ |
| 极致模板性能 | quicktemplate |
| 传统模板引擎需求 | Jet |
| 已有大量 html/template | 继续使用 |
| 为了性能从 html/template 全量迁移 | 不建议 |
综合建议:
2026 年新建 Go SSR 项目
↓
templ
↓
Go + templ + HTMX
↓
类型安全 + 组件化 + 高性能 + SSR
最终结论:2026 年新建 Go SSR 项目,优先选择 templ;传统项目继续使用 html/template;只有明确存在模板渲染性能瓶颈时,才考虑为了性能迁移或使用 quicktemplate。
