htmx 4.0.0 正式发布
htmx 团队非常高兴地宣布 htmx 4.0.0 正式发布!这是 8 个月工作(外加一个游戏)的结晶,我们对结果非常满意。
htmx 4 的想法开始萌芽,是在我决定创建 fixi 的时候,借此我对 fetch() API 和 JavaScript 异步编程变得更熟悉了。(出于向后兼容的考虑,htmx 一直使用 XMLHttpRequest。)
一个偶然的晚上,Christian 联系了我,他对流式 HTML有一些有趣的想法,这让我开始思考:把内部实现迁移到 fetch() 会让他的场景乃至整个库都更简单。
经过一番工作,我说服了 Michael 和 Alex 加入,我们便一发不可收拾。
开发过程非常顺利。我们从移植 fixi 和 htmx 测试套件开始。随着时间推移,我们不断重新理解 htmx 当初为什么那样设计,并让新实现一步步向旧实现靠拢。现在 2.x 与 4.x 的行为差异已经相对较小,而在确实存在分歧的地方,我们都做出了明确取舍,让基于 htmx 的应用有良好基础去成为百年 Web 服务
请注意,我们没有在 NPM 上把 4.0 标记为 latest,因为不想强迫升级那些依赖无版本号 CDN URL 的用户。2.x 将继续作为 latest,4.0 线将保持 next,直到 2027 年初的某个时点。不过官网会指向 4.0。
主要变化
如上所述,从用户视角看,htmx 4 与 htmx 2 几乎完全相同。主要有三个变化:
- 属性继承默认从「隐式」改为「显式」(这是升级中最重要的一项)
- htmx 事件名称已标准化并做了清理。部分高级用户可能需要修改自己监听的事件。
- 历史支持默认不再使用
localStorage(这曾是许多支持难题的根源)。大多数人完全不会注意到这一点。
在内部,我们从 XMLHttpRequest 迁移到了 fetch(),但这对大多数 htmx 用户应当是透明的。
属性继承
在 htmx 2 中,很多属性默认是「可继承」的:把属性放在父元素上,其行为会应用到子元素。这一行为源自 intercooler.js 时代,灵感来自 CSS,而效果也和 CSS 差不多:强大,但有时难以理解。
在 htmx 4 中,属性默认不再继承,除非你在属性名后面加上 :inherited 显式声明:
<!-- htmx 2 -->
<div hx-confirm="Are you sure?">
<button hx-delete="/item/1">Delete</button>
</div>
<!-- htmx 4 -->
<div hx-confirm:inherited="Are you sure?">
<button hx-delete="/item/1">Delete</button>
</div>
这将是从 htmx 2 迁移到 htmx 4 时最大的升级负担。为了让事情容易些,我们提供了一个命令行工具,帮你找出需要标记为 inherited 的位置。
注意,hx-disinherit 之类的属性不再需要,应当移除。
事件
htmx 2 触发的事件是随库的生命周期自然生长起来的,组织得并不好,很难搞清楚哪个事件在何时触发。
在 htmx 4 中,所有事件都遵循 htmx:phase:action[:sub-action]:
| htmx 2 | htmx 4 |
|---|---|
htmx:beforeRequest | htmx:before:request |
htmx:afterRequest | htmx:after:request |
htmx:beforeSwap | htmx:before:swap |
htmx:afterSwap | htmx:after:swap |
htmx:configRequest | htmx:config:request |
此外还有以下变化:
- 大多数错误事件合并为
htmx:error。HTTP 错误响应会触发htmx:response:error。 htmx:xhr:*系列事件已移除。htmx 4 使用fetch()。htmx:validation:*系列事件已移除,改用浏览器原生表单校验。
完整对照表见 What's New in htmx 4。
命令行升级检查器能在 hx-on 属性以及它能找到的 JavaScript 中标记出旧事件名。
历史
htmx 一直内置历史支持,让你用简单属性实现感知浏览器后退按钮的交互。在 htmx 2 中,它用 localStorage 里的缓存对页面做快照以便恢复。可惜一个主要的问题来源是:快照可能包含第三方 JavaScript 库造成的 DOM 修改。页面恢复时,那些修改还在,但其背后的 JavaScript 逻辑却不在了。
htmx 4 不再把页面缓存到 localStorage。后退导航时,htmx 会重新请求页面并把它换入 <body>,若存在 [hx-history-elt] 元素则换入该元素。这让第三方 JavaScript 库在大多数情况下「直接可用」,而且配合良好的请求缓存,速度非常快。
如果你想要本地缓存,我们如今附带了一个非常完善的 hx-history-cache 扩展,从 sessionStorage 恢复历史,并且设计为能与 Alpine.js 等脚本方案良好集成。
新特性
htmx 4 有两个令人兴奋的大型新特性:
Morph 交换
htmx 现在开箱即用地支持 morph 交换。我创建了 idiomorph,当年差点把它并入 htmx 2.x,后来还是放弃了。在 htmx 4 中,Michael 在该算法上做了出色改进,并将其无缝集成进 htmx。
<hx-partial>
另一个重要的新特性是 <hx-partial> 标签。它与带外交换(out-of-band swaps)类似,但当你想做的不仅仅是「把单个元素替换成自己的新版本」时,它的表达清晰得多:
<hx-partial hx-target="#messages" hx-swap="beforeend">
<div>New message</div>
</hx-partial>
<hx-partial hx-target="#count">
<span>5</span>
</hx-partial>
扩展
htmx 4 令人兴奋的地方很大程度在扩展上。内部改用 fetch() 让我们得以重新思考扩展可以并且应该怎样工作,也催生(或重造)了许多新扩展,例如:
hx-preload—— 预加载内容(例如在mouseover时)以加快请求hx-download—— 基于原生 fetch 的文件下载hx-alpine-compat—— 化解 htmx 与 Alpine.js 之间的兼容性问题hx-history-cache—— 将历史缓存到sessionStorage,并提供 Alpine.js 兼容性
此外,还有三个新的或更新的流式 HTML 扩展:
hx-sse通过text/event-stream流式传输。hx-ws通过 WebSocket 流式收发。hx-multipart通过multipart/mixed流式传输。
最后,我们觉得是时候亲手做一个与 htmx 紧密集成的轻量前端脚本方案了。hx-live 的灵感来自 Alpine.js、jQuery 和 hyperscript,让前端脚本编写变得愉快而有趣。它甚至支持我们所说的基于 DOM、对 HATEOAS 友好的响应式。
发行包中还有一个新的 htmax.js 捆绑包,把 htmx 与其中最受欢迎的扩展打包进单个文件——如果你不想纠结选哪些的话。
升级
完整的升级指南见 What's New in htmx 4。
如前所述,我们提供了一个升级工具来帮助你:
$ npx htmx.org@4.0.0 upgrade-check -- ./templates
File extensions: .html, .php, .js, .ts, .jinja, .jinja2, .j2, .erb, .hbs
Use --ext to add more (e.g. --ext .vue --ext .svelte)
Scanning 1 file(s)...
Found 8 issue(s) in 1 of 1 file(s).
templates/index.html:1: [inheritance] hx-headers needs :inherited suffix (descendant on line 3 has hx-delete) (this looks like a CSRF token; without :inherited the header does not reach child elements and the server rejects the request)
templates/index.html:2: [inheritance] hx-target needs :inherited suffix (descendant on line 3 has hx-delete)
templates/index.html:2: [inheritance] hx-confirm needs :inherited suffix (descendant on line 3 has hx-delete)
templates/index.html:3: [renamed-attr] hx-disable -> rename to hx-ignore (hx-disable now means 'disable during request')
templates/index.html:4: [removed-attr] hx-vars is removed -> use hx-vals with js: prefix
templates/index.html:4: [removed-attr] hx-prompt is removed -> load the hx-prompt extension to keep the same syntax
templates/index.html:9: [old-event] old event name "htmx:afterRequest" -> "htmx:after:request"
templates/index.html:9: [old-api] htmx.addClass() is removed -> use element.classList.add()
我们还随版本发布了一个 agent skill 来辅助升级
安装
htmx 4.0 可以通过包管理器指定版本 4.0.0 安装,也可以通过 CDN 引入:
<script src="https://unpkg.com/htmx.org@4.0.0/dist/htmx.min.js"></script>
或者直接下载
LLM
不管你喜不喜欢,很多人都在用 LLM,为此我们提供以下 skills 文件:
htmx-guidance—— 使用 htmx 4 进行开发的核心技能htmx-debugging—— 开发过程中诊断 htmx 问题htmx-extension-authoring—— 编写与调试 htmx 4 扩展htmx-upgrade-from-htmx2—— 将代码库从 htmx 2.x 迁移到 4.x
(在 LLM 时代发布库的新版本究竟是好事还是坏事,这个话题就先放到一边吧!)
结语
希望你喜欢 htmx 4。htmx 2 将无限期继续获得支持,所以不必有升级压力。
我要感谢以下人士在本次发布中提供的所有帮助:
- Michael West —— 了不起的队友、grug 头脑的开发者
- Christian Tanul —— 启发了 htmx 4,并主导流式与 live 扩展
- Alex Petros —— 让这艘船始终保持平稳
- Stephen Mitchell —— 那个游戏背后的天才
- Stu Kennedy —— 我们的 WebSocket 专家
- André Ahlert Jr. —— 提供 IDE 与编辑器支持
- Dien Hoa Truong —— 早期试用 htmx 4 并帮忙修复了许多 bug
升级音乐
没有升级音乐的 htmx 更新是不完整的: