最难的部分,不是删掉那 10,000 行 JavaScript。
最难的是承认一个事实:
那几千行代码,其实是在解决一个
HTMX 直接让它“不存在”的问题。
HTML over the wire —— 一个并不新、只是被重新命名的老思想 ——
干掉了我们维护多年的层层抽象:
而且不是靠魔法。
为什么当初会有那 10,000 行 JavaScript?
我们的 UI 架构,其实非常“标准”:
复杂度并不是来自功能本身,
而是来自浏览器“需要知道的一切”。
客户端状态意味着什么?
随着产品增长,前端开始出现典型症状:
而 HTMX 的模型,正好反着来:
真相只存在于服务器
浏览器只负责交换 HTML 片段
没有 controller
没有 store
没有 diff 引擎
HTML Over the Wire 到底是什么意思?
一句话版本:
服务器返回“可直接渲染的 HTML”,
而不是 JSON。
浏览器通过这些属性完成交互:
为什么这很重要?
接下来几个例子,正是复杂度“蒸发”的地方。
重写一个被严重过度设计的搜索框
之前,这个搜索框需要:
使用 HTMX 之后:
👉 一个服务端 endpoint
👉 一个 HTML 属性
Before:JSON + JavaScript
searchInput.addEventListener("input", debounce(async e => {
const q = e.target.value;
const res = await fetch(`/api/search?q=${q}`);
const data = await res.json();
results.innerHTML = renderResults(data.items);
}, 200));
After:HTMX
<input hx-get="/search"
hx-trigger="keyup changed delay:200ms"
hx-target="#results">
<div id="results"></div>
发生了什么变化?
实际效果
渲染时间:72ms → 11ms
不是“微优化”,
是架构层面的减少。
流程简化示意图(非常关键)
Before:JSON 驱动
[Browser JS] --fetch--> [API]
| |
parse JSON build JSON
| |
render client server template
| |
DOM <------diff--------|
After:HTMX
[Browser] --hx-get--> [Server templates]
| |
| render HTML
| |
<-----swap fragment----|
少了解析、没有 diff、没有重复逻辑。
干掉整个客户端 Router
之前的 SPA Router 负责:
每加一个路由,要动 4–6 个文件。
HTMX 的替代方案
<a hx-get="/settings"
hx-push-url="true"
hx-target="#view">
Settings
</a>
<div id="view"></div>
为什么这很重要?
实际结果
Router 文件:
1387 行 → 42 行
(只剩一个滚动恢复工具函数)
API 层直接砍掉一半
我们并没有完全删除 API(移动端还要用),
但我们删掉了所有这种 API:
“只是为了让前端重新渲染列表 / 卡片 / 表格”
Before:JSON API
func ListUsers(w http.ResponseWriter, r *http.Request) {
users := db.AllUsers()
json.NewEncoder(w).Encode(users)
}
After:服务端渲染
func ListUsersHTML(w http.ResponseWriter, r *http.Request) {
users := db.AllUsers()
render("users/list.html", users, w)
}
实际收益
⚠️ 重要说明:
真正需要结构化数据的地方,JSON 依然保留
HTMX 是用来干掉不必要的前端专用 API
让整个团队信服的一张表
我们 benchmark 了最忙的页面:
带筛选和分页的表格
+---------------------------+---------+---------+
| Metric | JSON/JS | HTMX |
+---------------------------+---------+---------+
| Avg. render time | 131ms | 46ms |
| Lines of frontend code | 420 | 52 |
| API calls per interaction | 3 | 1 |
| Bug reports per quarter | 14 | 3 |
+---------------------------+---------+---------+
最重要的不是速度。
而是:
稳定性。
新功能不再“顺手把隔壁页面搞坏”。
为什么 HTMX 感觉比 React / Vue / 原生 JS 更快?
不是因为 HTMX 更强。
而是因为它几乎什么都不做。
浏览器不再需要:
服务器渲染的 HTML:
浏览器只负责放进去。
现实边界
如果你的 UI 是:
👉 HTMX 不够
HTMX 的主场是:
那个大家“假装在用”的渐进增强
我们以前也说支持 progressive enhancement。
说实话?
没有 JS 基本啥都用不了。
HTMX 完全不需要额外努力,就做到了。
示例
<form hx-post="/update" hx-target="#row-{{id}}">
<input name="email" value="{{email}}">
<button>Save</button>
</form>
如果 HTMX 不存在:
这是一个诚实的 fallback。
结果:
错误状态(尤其是弱网 / 离线)
下降了 27%
最意外的变化:开发速度暴涨
迁移后第一个月,PR 直接变小。
+------------------------+---------+---------+
| PR Metric | Before | After |
+------------------------+---------+---------+
| Avg. PR lines changed | 510 | 91 |
| Avg. review time | 3.1h | 41min |
| Rollback frequency | 11/mo | 3/mo |
+------------------------+---------+---------+
为什么会这样?
功能 = 一个模板
渲染 = 后端控制
“半残 UI 状态”几乎消失。
更大的架构教训
HTMX 并不是因为“新”才帮了我们。
而是因为我们终于停止了:
让前端复制一遍后端逻辑
架构变化对比
之前
[Backend rules]
|
v
[JSON API] ---> [Client state]
|
v
[UI renderer]
之后
[Backend rules]
|
v
[Server templates] ---> [Browser swaps]
删掉一个状态层,
就是删掉一整类 bug。
什么时候不该用 HTMX?
用 JSON + JS 的场景
HTMX 不适合
HTMX 最适合
唯一靠谱的迁移策略
我们没有重写应用。
我们是一点一点替换的。
方法
HTMX 支持渐进迁移。
真正的收益,是复杂度慢慢消失。
最后的结论
删掉 10,000 行 JavaScript
不是成就。
真正的成就是:
允许服务器重新负责渲染。
HTMX 没给我们魔法,
它给的是:
最讽刺的是:
代码越简单,反而越快。
如果你的前端
已经比问题本身还复杂,
试试只替换一个 JSON endpoint,
用 一个 HTML 片段。
你可能会删掉
比你想象中更多的代码。