htmx 怪癖

这是一个“怪癖”页面,基于 SQLite 的“SQLite 中的怪癖、注意事项和陷阱”页面

属性继承

htmx 中的许多属性是继承的:子元素可以从父元素上的属性接收行为。

例如,以下是两个通过 htmx 驱动的按钮,它们从父 div 继承其 target

<div hx-target="#output">
    <button hx-post="/items/100/like">Like</button>
    <button hx-delete="/items/100">Delete</button>
</div>
<output id="output"></output>

这有助于避免重复属性,从而保持代码 DRY

另一方面,当属性距离元素越来越远时,你会失去 Locality of Behavior,并且理解元素在做什么会变得更加困难。

通过向父元素添加属性,也有可能无意中改变元素的行為。

有些人更喜欢完全禁用 htmx 中的继承,使用 htmx.config.disableInheritance 配置变量

以下是一个实现此配置的 meta 标签:

  <meta name="htmx-config" content='{"disableInheritance":true}'>

默认交换策略是 innerHTML

hx-swap 属性允许你控制交换如何执行。默认策略是 innerHTML,即将响应 HTML 内容放置在目标元素内部。

许多人更喜欢将 outerHTML 策略作为默认策略。

你可以使用 htmx.config.defaultSwapStyle 配置变量 来更改此行为。

以下是一个实现此配置的 meta 标签:

  <meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>

针对 body 的目标总是执行 innerHTML 交换

出于历史原因,如果你针对 body 元素,htmx 将 总是执行 innerHTML 交换

这意味着你无法通过 htmx 请求更改 body 标签上的属性。

默认情况下 4xx5xx 响应不执行交换

htmx 从未默认交换“错误”状态响应代码(400500)。

这种行为让一些人感到恼火,特别是某些服务器框架会返回 422 - Unprocessable Entity 响应代码来表示表单未正确填写。

初次遇到时,这可能会非常令人困惑。

你可以通过 htmx:beforeSwap 事件或 通过 htmx.config.responseHandling 配置数组 来配置 htmx 的响应行为。

以下是默认配置:

{
  "responseHandling": [
    {"code":"204", "swap": false},
    {"code":"[23]..", "swap": true},
    {"code":"[45]..", "swap": false, "error":true},
    {"code":"...", "swap": false}]
}

注意,204 No Content 也不会被交换。

如果你想无论响应代码如何都交换一切,可以使用此配置:

{
  "responseHandling": [
    {"code":"...", "swap": true}]
}

如果你想特别允许 422 响应进行交换,可以使用此配置:

{
  "responseHandling": [
    {"code":"422", "swap": true},
    {"code":"204", "swap": false},
    {"code":"[23]..", "swap": true},
    {"code":"[45]..", "swap": false, "error":true},
    {"code":"...", "swap": false}]
}

以下是一个允许所有响应进行交换的 meta 标签:

  <meta name="htmx-config" content='{"responseHandling": [{"code":"...", "swap": true}]}'>

非表单元素上的 GET 请求默认不包含表单值

如果非表单元素通过 htmx 发出非 GET 请求(例如 PUT 请求),则该元素的封闭表单(如果有)的值将被包含在请求中

但是,如果元素发出 GET,则封闭表单的值 不会被包含

如果你希望在发出 GET 时包含封闭表单的值,可以像这样使用 hx-include 属性:

<button hx-get="/search"
        hx-include="closest form">
  Search
</button>

历史记录可能很棘手

htmx 提供了与浏览器 历史记录 交互的支持。这可能非常强大,但也可能很棘手,特别是如果你使用修改 DOM 的第三方 JavaScript 库。

使用 htmx 的历史记录支持时,也可能存在 安全问题

这些问题中的大多数可以通过禁用任何本地历史缓存并在用户向后导航历史记录时简单地发出服务器请求来解决,权衡是历史导航会变慢。

以下是一个禁用历史缓存的 meta 标签:

  <meta name="htmx-config" content='{"historyCacheSize": 0}'>

有些人不喜欢 hx-boost

hx-boost 与 htmx 的其他大多数方面相比是一个奇怪的功能:它“神奇地”将所有锚标签和表单转换为 AJAX 请求。

这可以加速这些交互的感觉,并且允许表单和锚标签在 JavaScript 被禁用 时继续工作,但是它也带来了一些权衡:

htmx 核心团队的一些成员认为,由于这些问题,以及浏览器在页面导航方面已经有了很大改进,最好避免使用 hx-boost,并 只需使用未增强的链接和表单

毫无疑问,hx-boost 与其他 htmx 属性相比是一个异类,并且遭受“如果某事神奇地工作,它也可能神奇地崩溃”的格言。

尽管如此,我(Carson)仍然认为它在许多情况下很有用,并且它被用于 https://htmx.cndocs.org 网站。

异步加载 htmx 不可靠

htmx 设计为使用标准的、阻塞的 <script> 标签加载,而不是 模块延迟 的那种。虽然我们做出了 最佳努力尝试,无论脚本在文档生命周期中的何时加载,都初始化 htmx,但有些用例会溜走,通常涉及捆绑或 AJAX 插入 htmx 本身的情况。

我们 过去的尝试 来弥合这个差距都导致了不可接受的回归。因此,虽然 htmx 可以异步加载,但请自行承担风险。

还要记住,如果你的 DOM 内容在 htmx 加载之前加载,所有 htmx 提供的功能在 htmx 加载之前将不可用。预取(甚至“常规”获取)htmx 在你需要它之前是一种可能解决此问题的方法。

JavaScript API 不是重点

htmx 是一个面向超媒体的前端库。这意味着 htmx 通过 HTML 中的 属性 来增强 HTML,而不是提供复杂的 JavaScript API。

确实存在 JavaScript API,但它不是库的重点,在大多数情况下,不应被 htmx 最终用户大量使用。

如果你发现自己大量使用它,特别是 htmx.ajax() 方法,或许值得问问自己是否有更符合 htmx 风格的方法来实现你正在做的事情。