SpringBoo+HTMX编程简介

HTMX 是一个小型 JavaScript 库,可让您使用 HTML 中的自定义属性来定义页面中元素的行为。它有点像现代版的 onclick 属性,但功能更强大、更灵活。

它的效率也更高,因为它使用浏览器内置的 HTTP 协议栈来发出请求,而且可以使用浏览器内置的缓存和历史记录管理功能。

它非常适合 Spring Boot 等服务器端框架,因为它允许使用服务器来生成页面的内容和行为,并允许使用浏览器的内置功能来管理导航和历史记录。

HTMX安装
1、最简单的方法是从 CDN 抓取,然后添加到 layout.html 模板中:
<script src='https://unpkg.com/htmx.org/dist/htmx.min.js'></script>

2、使用 Webjar 将库加载到 classpath 中,这样也可以正常工作。Spring 可以做一些额外的事情来帮助浏览器缓存程序库,还可以帮助进行版本管理。见:GitHub (dsyer/webmvc-thymeleaf).


表单处理
我们可以轻松添加的一项功能是使用 HTMX 提交表单,而无需重新加载整个页面。为此,我们可以在表单元素中添加 hx-post 属性:

<form th:action="@{/greet}" method="post" hx-post="/greet">
    <input type=
"text" name="name" th:value="${name}"/>
    <button type=
"submit" class="btn btn-primary">Greet</button>
</form>

这将导致 HTMX 拦截表单上的提交操作,并使用 AJAX 请求将数据发送到服务器。服务器将处理请求并返回结果,HTMX 将用结果替换表单内容。

如果不希望结果替换表单内容的效果,可以通过在表单元素中添加 hx-target 属性来解决这个问题:

<form th:action="@{/greet}" th:hx-post="@{/greet}" method="post" hx-target="content">

 "conent"元素:包含ID 和 hx-swap-oob 属性,以实现将传入的内容应替换现有内容:

<div id="content" class="col-md-12" hx-swap-oob="true">
    <span th:text=
"${greeting}">Hello, World</span><br/>
    <span th:text=
"${time}">21:00</span>
</div>

这样,HTMX 就提取 "content "元素并为我们切换其内容。图片和其他静态内容不会重新加载,浏览器的历史记录也会更新,以反映页面的新状态。

后端springboot代码:

@PostMapping(path = "/greet")
String name(Map<String, Object> model, @RequestParam String name) {
    greet(model);
    model.put(
"greeting", "Hello " + name);
    model.put(
"name", name);
    return
"greet";
}

HTMX 在向后端服务器/greet发出的请求中添加了 hx-request 标头:这是 HTMX 的一项功能,可让您在服务器端代码中匹配请求,接下来我们将使用该功能。

使用片段模板
现在,服务器仍会为表单提交重新渲染整个页面,可以通过使用片段模板来提高效率。

在 greet.html 模板中添加 th:fragment 属性:

<div id="content" th:fragment="content" class="col-md-12" hx-swap-oob="true">
    <span th:text=
"${greeting}">Hello, World</span><br/>
    <span th:text=
"${time}">21:00</span>
</div>

然后,我们就可以在 SampleController 中的一个新映射方法中使用该片段,该方法仅在请求来自 HTMX 时触发(通过匹配 hx-request 头信息):

@PostMapping(path = "/greet", headers = "hx-request=true")
String nameHtmx(Map<String, Object> model, @RequestParam String name) {
    greet(model);
    return
"greet :: content";
}

(":: "语法是 Thymeleaf 的一项功能,可让您呈现模板的片段。例如,找到 "greet "模板,然后查找名为 "content "的片段)。

如果现在提交表单,并查看浏览器开发工具中的网络活动,就会发现服务器只返回了更新内容所需的页面片段。

懒加载
另一种常见用例是在页面首次加载时从服务器加载内容,甚至可以根据用户的偏好进行定制。

我们可以使用 HTMX 来实现这一功能,方法是在要触发请求的元素上添加 hx-get 属性。

我们可以在 layout.html 模板中尝试使用徽标,而不是静态地包含图片。

下面是静态包含图片:

<div class="row">
    <div class=
"col-12">
    <img src=
"../static/images/spring-logo.svg" th:src="@{/images/spring-logo.svg}" alt="Logo" style="width:200px;" loading="lazy">
    </div>
</div>

上述代码替换为:

<div class="row">
    <div class=
"col-12">
    <span class=
"fa fa-spin fa-spinner" style="width:200px; text-align:center;">
    </div>
</div>

上述代码使用了占位符替换img位置

让 HTMX 动态加载:

<div class="row">
    <div class=
"col-12" hx-get="/logo" hx-trigger="load">
    <span class=
"fa fa-spin fa-spinner" style="width:200px; text-align:center;">
    </div>
</div>

注意:这里添加了 hx-get 和 hx-trigger。

  • hx-get 属性告诉 HTMX 向服务器发出 GET 请求,以获取元素的内容。
  • hx-trigger 属性告诉 HTMX 在页面加载时触发请求。默认情况下是在点击时触发。

因此,我们需要在 SampleController 中创建一个新的映射:

@GetMapping(path = "/logo")
String logo() {
    return
"layout :: logo";
}

为了只渲染 layout.html 模板中包含图片的片段,必须再次修改 layout.html 模板,使用 th:fragment 属性,替换hx-get和hx-trigger 属性:

<div class="row" th:remove="all">
    <div class=
"col-12" th:fragment="logo">
    <img src=
"../static/images/spring-logo.svg" th:src="@{/images/spring-logo.svg}" alt="Logo"
        style=
"width:200px;" loading="lazy">
    </div>
</div>

请注意,我们必须从模板中th:remove该片段,因为占位符将在初始呈现时替换它。

如果现在运行应用程序,就会看到页面加载时,旋转图标就被真实后端的logo图片取代。

Spring Boot HTMX其他库包
HTMX 还有更多的功能,我们没有时间在此详细介绍。值得一提的是,有一个 Java 库可以帮助实现这些功能,而且还有一些 Thymeleaf 工具:Wim Deblauwe 编写的 Spring Boot HTMX ,可作为 Maven Central 的依赖项。它可以通过自定义注解进行 hx-request 头匹配,还能帮助实现 HTMX 的其他功能。

Unpoly库包
Unpoly的CDN链接是:

<script src='https://unpkg.com/unpoly/unpoly.min.js'></script>

示例代码中的“unpoly”分支和以前一样使用 Webjar。基本(整个页面呈现)表单提交示例如下所示:

<div class="col-md-12">
    <form th:action=
"@{/greet}" method="post" up-target="content">
    <input type=
"text" name="name" th:value="${name}"/>
    <button type=
"submit" class="btn btn-primary">Greet</button>
    </form>
</div>
<div id=
"content" class="col-md-12">
    <span th:text=
"${greeting}">Hello, World</span><br/>
    <span th:text=
"${time}">21:00</span>
</div>

hx-target变成了up-target,其余的 HTMX 装饰只是 Unpoly 中的默认设置。

要转换为片段模板,我们需要遵循 HTMX 中的模式:添加一个th:fragment与 Unpoly 中的唯一标头匹配的控制器方法,例如X-Up-Context。

Hotwired Turbo库包
Hotwired Turbo 的 CDN 链接是:

<script src='https://unpkg.com/@hotwired/turbo/dist/turbo.es2017-umd.js'></script>
示例代码中的“turbo”分支和以前一样使用 Webjar。基本表单提交示例如下所示:

<turbo-frame id="content">
    <div class=
"col-md-12">
    <form th:action=
"@{/greet}" method="post">
        <input type=
"text" name="name" th:value="${name}" />
        <button type=
"submit" class="btn btn-primary">Greet</button>
    </form>
    </div>
    <div class=
"col-md-12">
        <span th:text=
"${greeting}">Hello, World</span><br />
        <span th:text=
"${time}">21:00</span>
    </div>
</turbo-frame>

Turbo 使用自定义元素 ( turbo-frame) 来标识要替换的内容,而不是标识表单处理交互的自定义​​属性。表格的其余部分不变。

要转换为片段模板,我们需要th:fragment向<turbo-frame>和控制器方法添加一个声明,以匹配来自 Turbo 的唯一标头,例如Turbo-Frame。