htmx 4.0 正式发布,版本号从 2.x 直接跳到了 4.0。这个数字不是发布脚本写错了,而是作者 Carson Gross 对过去承诺的一次“文字游戏式兑现”:2.0 发布时,他曾表示不会有 3.0,因为 htmx 不希望通过大版本制造破坏性变更。如今项目确实引入了破坏性变化,于是 3.0 被跳过,版本直接来到 4.0。
社区还给这种做法起了个玩笑式的名字:“指数级版本号”。按这个梗,下一个大版本会是 8。不过真正值得工程团队关注的,不是版本号,而是 htmx 4.0 对 HTML 属性行为作出的重新定义。
为什么从 2.x 直接跳到 4.0
htmx 4.0 经历了约 8 个月开发,发布说明中提到的核心变化有三条。其中最容易影响现有页面的是:属性继承从隐式改为显式。
过去,父元素上的某些 htmx 属性可以被子元素自动继承。这个机制让页面代码很简洁,例如一个容器可以统一声明请求目标或交换策略,内部按钮只需要写触发请求的属性即可。但简洁也意味着行为不一定显眼:阅读一个子元素时,开发者可能看不到它真正继承了哪些配置。
在页面规模变大、组件开始复用之后,这种隐式关系会带来几个问题:
- 子元素的行为依赖 DOM 上方的祖先节点。
- 把一个按钮移动到另一个容器后,请求行为可能随之变化。
- 组件源码和页面布局之间出现隐藏耦合。
- 调试时需要沿着父子节点回溯属性来源。
4.0 的方向是把这种依赖写出来。属性不再因为“处于某个容器内部”就自动生效,而是需要显式声明哪些配置允许被继承。这样做会增加一些 HTML 标记,却让组件的行为边界更容易审查和测试。
隐式继承变成显式继承
下面的示例用于说明迁移思路。假设项目采用 hx-inherit 作为显式继承声明,实际升级时应以 htmx 4.0 的正式属性名和语法为准。
升级前,代码可能类似这样:
<div hx-target="#result" hx-swap="innerHTML">
<button hx-get="/users">加载用户</button>
</div>
<div id="result"></div>
按钮没有声明 hx-target 和 hx-swap,但请求结果依赖父容器的配置。迁移到显式模型后,可以把继承关系直接写在按钮上:
<div hx-target="#result" hx-swap="innerHTML">
<button
hx-get="/users"
hx-inherit="hx-target hx-swap">
加载用户
</button>
</div>
<div id="result"></div>
如果团队更重视组件的独立性,也可以不依赖继承,直接把关键行为写完整:
<button
hx-get="/users"
hx-target="#result"
hx-swap="innerHTML">
加载用户
</button>
<div id="result"></div>
这个版本的优点是组件可以脱离原来的容器单独阅读和测试。代价是重复属性更多,模板也可能变长。实际项目可以按组件边界做取舍:布局级默认值适合显式继承,业务关键行为则适合直接声明。
升级时不要只搜索版本号
从 htmx 2.x 升级到 4.0,搜索并替换脚本版本只是最小的一步。更有效的做法是盘点页面中依赖祖先节点的 htmx 行为。
可以先用命令找出常见 htmx 属性的位置:
rg -n --glob '*.{html,htm,jinja,j2,tsx,jsx}' \
'hx-(get|post|put|delete|target|swap|trigger|confirm|select|vals|include|boost)' \
templates src views
接着重点检查以下场景:
- 子元素只声明了
hx-get或hx-post,但没有声明目标节点。 - 外层布局统一设置了
hx-target、hx-swap或触发器配置。 - 组件会被服务端模板、局部模板或前端渲染逻辑重复插入。
- 同一按钮在不同页面容器中复用,但预期行为并不相同。
可以把检查结果整理成一张简单的迁移表:
组件 依赖的祖先属性 升级动作
用户列表加载按钮 hx-target, hx-swap 显式声明或增加继承名单
分页链接 hx-target, hx-push-url 逐项确认行为
表单提交区域 hx-target, hx-select 直接写入组件模板
全局导航容器 hx-boost 先在测试页验证
对于关键交互,建议在升级前后都保留浏览器级测试。测试不应只验证 HTTP 请求是否发出,还要验证响应被插入到了哪个节点,以及交换策略是否符合预期。
这次变化的工程含义
隐式继承的优点是少写代码,显式继承的优点是降低理解成本。两者没有绝对的优劣,区别在于复杂度被放在哪里:
- 隐式模型把复杂度放在 DOM 结构中,页面短,但行为需要追踪。
- 显式模型把复杂度放在属性声明中,标记可能变长,但组件更自洽。
对于小型页面,迁移后的重复属性可能显得繁琐。对于大型后台、设计系统或多人维护的服务端模板,显式契约通常更容易 code review,也更适合组件测试。
需要注意的是,来源摘要只明确介绍了属性继承这一项核心变化,另外两项变化不能仅凭版本号推断。升级时不要根据“4.0”这个数字自行猜测所有 API 都发生了变化,应逐条对照官方迁移说明,并为受影响的交互补充回归测试。
一份稳妥的升级清单
- 固定 htmx 4.0 的具体版本,不直接追踪浮动资源。
- 在测试环境中先升级,保留 2.x 与 4.0 的页面行为对比。
- 搜索所有依赖父级 htmx 属性的组件。
- 为继承关系增加显式声明,或将关键属性移动到组件自身。
- 检查请求目标、交换策略、事件触发和表单提交结果。
- 重点验证被多个页面复用的局部模板。
- 不要把跳过 3.0 理解成“没有破坏性变化”;这次版本号恰恰是在提醒使用者认真阅读迁移说明。
htmx 4.0 最值得关注的信号,是一个以简洁著称的工具开始主动收紧行为边界。对于新项目,可以从一开始就把关键交互写成显式契约;对于存量项目,则应优先改造复用率高、祖先属性多、测试覆盖薄弱的组件。