Back

使用 htmx 的智能加载模式

使用 htmx 的智能加载模式

你构建了一个仪表板。每个组件在加载时都触发 hx-get。用户看到六个旋转的加载器,等待本可以随初始页面一起到达的内容。听起来很熟悉?

问题不在于 htmx——而在于对每一块内容都采用相同的处理方式。智能的 UI 加载模式需要理解何时加载内容,而不仅仅是如何加载。本文涵盖了真正重要的 htmx 加载模式:视口触发加载、分阶段延迟,以及通过 hx-boost 实现的渐进增强。

核心要点

  • 询问内容是否必须在用户操作之前存在——如果是,则在服务器端渲染;如果否,则使用 htmx 延迟加载
  • 使用带有交错延迟的 hx-trigger="load" 来防止请求风暴并创建视觉级联效果
  • 对于标准页面滚动选择 revealed,对于滚动容器或阈值控制选择 intersect
  • 应用 hx-boost 实现即时导航,无需更改服务器代码或破坏渐进增强

为什么加载策略很重要

htmx 将状态保持在服务器端。这是它的优势。但网络请求仍然需要时间,当关键内容到达较晚时,用户会注意到。

核心问题:这个内容是否需要在用户操作之前就存在?

如果是,则在服务器端渲染。如果否,htmx 可以稍后获取它。这个简单的过滤器消除了大部分加载模式的困惑。

使用 htmx 的懒加载:load 触发器

hx-trigger="load" 模式在元素进入 DOM 时触发请求。它对于延迟昂贵的查询同时保持初始页面快速加载很有用。

<section hx-get="/api/analytics" 
         hx-trigger="load delay:300ms"
         hx-swap="innerHTML">
    <div class="skeleton">加载分析数据中...</div>
</section>

delay 修饰符可以防止请求风暴。交错你的延迟——中等优先级内容使用 300ms,低优先级部分使用 600ms 或更长。这样可以分散服务器负载并创建自然的视觉级联效果。

何时使用 load:

  • 首屏以下的内容
  • 难以缓存的个性化数据
  • 超过 200ms 的查询

何时避免使用:

  • 用户期望立即看到的主要页面内容
  • SEO 关键信息
  • 100ms 以下的快速查询(直接在服务器端渲染即可)

视口触发加载:revealed vs intersect

htmx 提供两种基于视口的加载触发器。选择正确的触发器取决于你的布局。

revealed 触发器

revealed 在元素滚动到视图中时触发一次。它使用针对文档视口的简单可见性检查。

<div hx-get="/api/recommendations"
     hx-trigger="revealed"
     hx-swap="outerHTML">
    加载推荐内容中...
</div>

这对于标准页面滚动效果很好——无限滚动实现、懒加载图片,或用户可能永远不会到达的内容部分。

intersect 触发器

intersect 使用 Intersection Observer API 并接受 threshold 修饰符。当你需要更精细的控制或内容位于可滚动容器内时使用它。

<div hx-get="/api/items"
     hx-trigger="intersect once threshold:0.5"
     hx-swap="innerHTML">
    <div class="placeholder"></div>
</div>

once 修饰符防止重复请求。threshold:0.5 表示元素必须 50% 可见才会触发。

选择 intersect 的情况:

  • 在滚动容器内加载(而非主视口)
  • 需要阈值控制
  • 想要显式的 Intersection Observer 行为

选择 revealed 的情况:

  • 标准文档滚动
  • 更简单的语义就足够了

使用 hx-boost 的渐进增强

hx-boost 将标准链接和表单转换为 AJAX 驱动的请求,无需更改服务器代码。浏览器交换 <body> 内容,同时最小化 <head> 的重新处理。

<body hx-boost="true">
    <a href="/contacts">联系人</a>
    <a href="/settings">设置</a>
    <a href="/report.pdf" hx-boost="false">下载报告</a>
</body>

这是最简单的渐进式加载。导航感觉更快,因为脚本和样式不会重新加载。历史记录和后退按钮行为正常工作。如果 JavaScript 失败,链接仍然作为标准导航功能。

对于不应被拦截的文件下载或外部链接,使用 hx-boost="false" 覆盖。

使用 hx-sync 控制请求竞争

多个触发器可能会产生竞争条件。hx-sync 协调相关元素上的请求。

<input hx-get="/search"
       hx-trigger="keyup changed delay:200ms"
       hx-sync="closest form:replace">

replace 策略在新请求开始时取消正在进行的请求。其他策略包括 queuedrop。当快速的用户输入可能触发重叠请求时使用此功能。

保留已加载的内容

当 htmx 交换内容时,默认情况下会替换目标。对不应重新加载的元素使用 hx-preserve——视频播放器、带有用户数据的表单输入,或具有内部状态的组件。

<video id="player" hx-preserve="true">...</video>

只要元素的 id 匹配,该元素就会在交换过程中保持不变。

决策框架

在向任何内容添加 hx-trigger="load" 之前,问自己:

  1. 这对于理解页面是否至关重要? → 服务器端渲染
  2. 查询是否超过 200ms? → 懒加载
  3. 这是否在首屏以下? → 使用 revealedintersect
  4. 这是否是个性化内容? → 懒加载(缓存无济于事)

从服务器渲染的内容开始。仅在解决实际问题的地方添加 htmx 性能模式——慢查询、个性化数据或用户可能不需要的内容。

结论

最好的加载模式往往就是没有加载模式。渲染重要的内容,延迟不重要的内容,让服务器保持控制。通过应用决策框架——服务器渲染关键内容,懒加载慢查询,对首屏以下的部分使用视口触发器——你可以避免旋转加载器陷阱,并提供感觉即时的界面。

常见问题

revealed 触发器使用针对文档视口的简单可见性检查,当元素滚动到视图中时触发一次。intersect 触发器使用 Intersection Observer API,为你提供阈值控制和可滚动容器内的正确行为。对于标准页面滚动使用 revealed,当需要更精细的控制时使用 intersect。

不应该。懒加载会增加网络往返次数,可能会使关键内容感觉变慢。只延迟用户不需要立即看到的内容、查询时间超过 200ms 的内容、位于首屏以下的内容,或包含无法缓存的个性化数据的内容。100ms 以下的快速查询应该在服务器端渲染。

使用 hx-sync 来协调相关元素上的请求。replace 策略在新请求开始时取消正在进行的请求。你也可以使用 queue 按顺序处理请求,或使用 drop 在有请求挂起时忽略新请求。这对于带有 keyup 触发器的搜索输入特别有用。

不会。当 hx-boost 拦截导航时,htmx 会正确更新浏览器历史记录。后退和前进按钮按预期工作。如果 JavaScript 完全失败,增强的链接会回退到标准导航,因为它们是具有有效 href 属性的常规锚标签。

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay