爬虫代码已复制

一文读懂 HTMX OOB 带外交换

什么是 OOB

OOB - Out Of Band 交换,即“带外交换”。

这个元素不是替换发起请求的那个元素,也可以不是 hx-target 指定的元素,而是替换(或插入)页面中其它位置的某个已有 DOM 元素。交换允许 HTMX 从响应中插入内容到非当前 hx-target 所指定的地方。

hx-swap-oob 是 HTMX 中的一个非常实用的 “交换方式属性”,用于在服务端返回的 HTML 中指示,即使这个 HTML 是从异步请求返回的,也可以让它作用于页面中其它的元素(而非触发请求的元素)。

应用场景

这在以下场景中特别有用:

  • 从服务器返回局部片段,同时更新页面多个区域

  • 弹窗控制、消息提示、购物车计数器、导航栏更新等

  • 让服务端控制页面其它部分的更新,而不是只局限于 hx-target

OOB 示例

客户端 HTML

<!-- index.html -->
<body>
  <div id="oob_target1">oob_target1</div>
  <div id="oob_target2">oob_target2</div>
  <div id="normal_target">normal_target</div>
  <button hx-get="/api" hx-target="#normal_target" hx-swap="outerHTML">加载</button>
</body>

以上 HTML 代码中,有3个 div,并分别添加了 ID 以便区分。

服务端 Python

# app.py
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template("index.html")

@app.route('/api')
def api():
    return render_template("content.html")

if __name__ == '__main__':
    app.run(debug=True)

以上 Flask 代码中有两个路由,一个根路由,用来返回 index.html 模板,另外一个是 /api 路由,用来响应浏览器 GET 请求,响应内容为 content.html 模板。

<!-- content.html -->
<div id="oob_target1"  hx-swap-oob="true">From server,will fill in oob target 1</div>
<div id="oob_target2"  hx-swap-oob="true">From server,will fill in oob target 2</div>
<div>From server,normal content</div>

content.html 模板同样有三个 div ,ID 与 index.html 中的一致,并且加入了 HTMX 属性 hx-swap-oob="true",告诉 HTMX 这里按 OOB 规则进行交换。

运行 app.py,在浏览器打开 localhost:5000,就能够看到 index.html 中的内容。

点击加载按钮,浏览器向服务器 /api 路径发起 GET 请求,并获取到返回的 content.html 模板的内容,如果不使用 OOB,则所有内容都会交换到 <div id="normal_target"> 中,但我们现在启用了 OOB ,就会发现 content.html 中的三个 div 的内容分别交换到了 index.html 中相同 ID 的三个 div 中,这个就是 OOB 的用途。

点击加载按钮后的 html 源码:

<div id="oob_target1">From server,will fill in oob target 1</div>
<div id="oob_target2">From server,will fill in oob target 2</div>
<div class="">From server,normal content</div>
<button hx-get="/api" hx-target="#normal_target" hx-swap="outerHTML" class="">加载</button>

可以看到,使用 hx-swap-oob 之后,一次响应就能够刷新 html 页面中的多个元素,这对于页面的内容与状态栏、面包屑等的更新非常有用。也在一定程度上支持了页面布局的灵活性,同时也能减少对服务器的请求数量,在一定程度上提高系统性能。

hx-swap-oob 的赋值范围除了 true 外,还可以使用 hx-swap 的值来指定交换的方式,如:

<!-- content.html -->
<div id="oob_target1"  hx-swap-oob="beforeend">From server,will fill in oob target 1</div>
<div id="oob_target2"  hx-swap-oob="aftebegin">From server,will fill in oob target 2</div>
<div>From server,normal content</div>

以上的代码就给 hx-swap-oob 指定了交换方式(默认的是 innerHTML),执行后最终的页面 HTML 代码就变成了:

<div id="oob_target1">oob_target1From server,will fill in oob target 1</div>
<div id="oob_target2">From server,will fill in oob target 2</div>
<div class="">From server,normal content</div>
<button hx-get="/api" hx-target="#normal_target" hx-swap="outerHTML" class="">加载</button>

但在测试中发现,hx-swap-oob 无法使用类名进行指定,如以下的代码正确交换:

<!-- content.html -->
<div class="oob_target1"  hx-swap-oob="true">From server,will fill in oob target 1</div>
<div class="oob_target2"  hx-swap-oob="true">From server,will fill in oob target 2</div>
<div>From server,normal content</div>

本文上的示例代码已经上传到 GitHub