HTMX 的核心理念是:让 HTML 拥有直接发起 AJAX 请求的能力,而无需编写 JavaScript。你只需要在 HTML 标签上添加特定的属性(以hx-开头),即可实现现代 Web 应用的交互体验。
第一部分:核心概念 (The Big 4)
HTMX 的工作原理可以概括为四个核心问题:
- 触发方式:谁触发了请求?(
hx-trigger) - 请求方式:发送什么类型的请求?(
hx-get,hx-post...) - 目标位置:返回的内容放到哪里?(
hx-target) - 替换方式:如何替换目标内容?(
hx-swap)
1. 发送请求 (AJAX)
在标准的 HTML 中,只有<a>和<form>能发送请求。HTMX 允许任何元素发送请求。
hx-get="/url": 发送 GET 请求hx-post="/url": 发送 POST 请求hx-put="/url": 发送 PUT 请求hx-delete="/url": 发送 DELETE 请求
示例:点击按钮发送 POST 请求。
<button hx-post="/click-me">点击我</button>
2. 触发器 (hx-trigger)
默认情况下:
<button>在click时触发。<input>在change时触发。<form>在submit时触发。
你可以通过hx-trigger自定义触发时机。
常见修饰符:
keyup: 键盘抬起时触发。changed: 只有值改变了才触发。delay:500ms: 延迟 500ms 触发(常用于搜索框防抖)。every:2s: 每 2 秒触发一次(轮询)。load: 页面/元素加载时立即触发(懒加载)。
示例:搜索框防抖
<input type="text" name="search" hx-get="/search" hx-trigger="keyup changed delay:500ms">
3. 目标 (hx-target)
默认情况下,HTMX 会将服务器返回的内容放到发起请求的元素内部。你可以通过 CSS 选择器指定其他位置。
hx-target="#some-id": 替换 ID 为some-id的元素。hx-target="this": 替换当前元素(默认)。hx-target="closest tr": 替换最近的父级<tr>元素(表格行操作常用)。hx-target="next .error": 替换下一个 class 为 error 的元素。
示例:点击按钮,更新下方的 div
<button hx-get="/news" hx-target="#news-content">加载新闻</button><div id="news-content"></div>
4. 替换策略 (hx-swap)
决定了服务器返回的 HTML 如何与目标元素交互。
innerHTML:(默认)替换目标元素内部的内容。outerHTML: 替换目标元素本身(整个标签被替换)。beforeend: 追加到目标元素内容的末尾。afterbegin: 插入到目标元素内容的开头。delete: 忽略响应内容,直接删除目标元素。none: 发送请求但不进行任何内容替换。
图解区别:
假设目标是<div id="t">原内容</div>,服务器返回<b>新</b>:
innerHTML-><div id="t"><b>新</b></div>outerHTML-><b>新</b>(原 div 没了)
第二部分:常用实战模式
1. 实时搜索 (Active Search)
这是 HTMX 最经典的案例。用户输入时自动搜索,无需点击按钮。
<input type="text" name="q" placeholder="搜索..." hx-get="/search" hx-trigger="keyup changed delay:500ms" hx-target="#search-results" hx-indicator=".htmx-indicator">
<span class="htmx-indicator">搜索中...</span>
<div id="search-results"></div>
后端逻辑:接收参数q,返回搜索结果的 HTML 列表片段。
2. 行内编辑 (Click to Edit)
点击文本,变成表单;保存后,变回文本。
查看状态 (index.jte):
<div hx-target="this" hx-swap="outerHTML"> <label>姓名: 张三</label> <button hx-get="/contact/1/edit">编辑</button></div>
编辑状态 (返回的片段):
<form hx-put="/contact/1" hx-target="this" hx-swap="outerHTML"> <label>姓名: <input type="text" name="name" value="张三"></label> <button type="submit">保存</button> <button hx-get="/contact/1">取消</button></form>
3. 无限滚动 (Infinite Scroll)
当页面滚动到底部时加载更多内容。
<tr hx-get="/contacts?page=2" hx-trigger="revealed" hx-swap="afterend"> <td>最后一行数据...</td></tr>
hx-trigger="revealed": 当这个元素滚动进入视口(被用户看到)时触发。hx-swap="afterend": 把新加载的数据加在这个元素后面。
第三部分:进阶功能
1. 传递参数 (hx-vals)
默认情况下,HTMX 会自动包含引起请求的元素及其子元素的name/value。
如果你想传递额外参数(通常是 JSON 格式):
<button hx-post="/register" hx-vals='{"userId": "123", "role": "admin"}'> 注册</button>
或者使用js:前缀动态获取 JS 变量:
<button hx-post="/add" hx-vals='js:{"time": new Date().toISOString()}'>...
2. 加载指示器 (hx-indicator)
在网络慢时给用户反馈。HTMX 会在请求期间给指定元素添加htmx-request类。
- 定义 CSS:
.htmx-indicator { display: none; } .htmx-request .htmx-indicator { display: inline; } .htmx-request.htmx-indicator { display: inline; }
- 使用:
<button hx-post="/save">保存</button><img src="/spinner.gif" class="htmx-indicator"/>
3. 带外更新 (Out of Band Swaps) - 高级技巧
有时候你提交一个表单,想更新页面上两个完全不同的位置(例如:更新列表 + 更新右上角的“购物车数量”)。
后端只需将额外的片段标记为hx-swap-oob="true"并在同一个响应中返回。
后端返回的 HTML:
<div>添加成功!</div>
<div id="cart-count" hx-swap-oob="true"> 5</div>
4. 确认对话框 (hx-confirm)
最简单的确认操作。
<button hx-delete="/account" hx-confirm="确定要注销账号吗?"> 注销</button>
第四部分:调试与开发建议
1. 常见错误
- 点击没反应:检查控制台 (F12)。有没有引入
htmx.js?网络请求发出了吗? - 全页刷新了:如果你的
<button>在<form>里,且没写type="button",它默认是 submit。或者后端返回了错误状态码。 - 内容套娃:如果你用
hx-swap="innerHTML"(默认),但后端返回了包含外层容器的代码,可能会导致<div>
。考虑用outerHTML或调整后端返回的片段。2. 怎么看日志?
在开发时,可以使用htmx.logAll()来监控所有事件。
<script>
htmx.logAll();
</script>
3. 思想转变
- 忘掉 JSON:除了极少数情况,后端 Controller 应该总是返回 HTML 字符串/模板。
- 后端主导:状态管理(State Management)回归后端。前端只是后端的投影。
结合 Spring Boot + JTE 总结
- 前端写
<div hx-get="...">。 - 后端 Controller 写
@GetMapping,处理业务逻辑。 - 后端返回
return "path/to/template";(HTML 片段)。 - HTMX 自动把新 HTML 塞进页面。
这就是 HTMX 的全部精髓:简单、声明式、回归 Web 本源。
该文章在 2025/12/27 17:49:57 编辑过