面向 Spring Boot 开发者的 Htmx 简介 |技术解析

开始一个新项目总是既令人兴奋又充满挑战。选择哪些技术来实现您的解决方案,需要对这些选择带来的成本加以权衡。每增加一项技术,可能引入的问题和依赖项都可能导致进度逐渐放缓,甚至停滞不前。前端是开发者经常感到决策疲劳的地方。React、Angular 和 Vue 等前端框架的流行确实带来了许多价值,但也在工具、安全考量因素、网络流量和巨大的初始负载方面产生了代价高昂的权衡。如果您在为下一个项目做出前端决策时感到不知所措,那么这篇文章非常适合您。

在这篇文章中,我们将探索一个名为 Htmx 的新兴库,它允许您利用现有的 Spring Boot 知识来提供交互式用户体验,同时避免您在使用其他前端框架时可能遇到的部分挫折。阅读完这篇文章后,您应该对将 Htmx 添加到新项目或现有 Spring Boot 项目中充满信心。

下载 IntelliJ IDEA



什么是 Htmx?

要理解什么是 Htmx,我们必须首先从哲学上理解这个库试图实现的目标。

Htmx 毫不掩饰地以加入超媒体阵营为荣。超媒体(Hypermedia)是 Ted Nelson 于 1965 年创造的术语,它专注于单个文档可能包含多个交互式元素(如文本、图像、视频,以及其他文档的链接)这样一种观点。如果这听起来像 HTML,那您猜对了。HTML 就是这一概念的典型示例,并已成功实现了我们今天熟知的互联网。然后,在 2015 年,Web 开发的格局发生了变化。

在 Web 2.0 时代,Web 开发开始将 Web 体验的 UI 元素分为前端和后端。后端 API 提供 JSON 或 XML,因为这些负载比完整的 HTML 负载更小。前端负责使用 JavaScript 将数据转换为演示性 HTML。这种模式绕过了客户端和渲染速度的限制,并在当时为访客提供了更出色的用户体验。自那时以来,一些情况发生了变化:

  • 互联网的整体速度显著提高

  • 客户端在渲染 HTML 方面效率更高

  • JavaScript 和 JSON 的负载已经膨胀

  • JavaScript 工具变得越来越复杂

对于 Htmx,在现代应用程序的上下文中,前端框架可能成为比它们声称要解决的问题更大的负担。那么,Htmx 的运作方式有何不同?

Htmx 专注于声明式编程风格,允许您使用 Htmx 特定的特性装饰现有的 HTML 输出。这些特性为那些 HTML 元素提供了通常可能没有的更多功能。所有 Htmx 的基本流程都包括以下几点:

  • 引发事件的客户端触发器

  • 事件通常触发对服务器后端的 Web 请求

  • 服务器以 HTML 片段响应

  • Htmx 将现有的 DOM 元素替换为响应

我们看一个稍后我们将使用 Spring Boot 实现的简单 Htmx 示例。

<button hx-post="/clicked"
    hx-trigger="click"
    hx-target="#parent-div"
    hx-swap="outerHTML"
>

    Click Me!
</button>

hx- 特性允许此按钮在每次点击时触发 HTTP POST。一旦服务器响应,我们将找到 #parent-div 并将其与生成的 HTML 交换。

这些特性不专属于任何 HTML 元素,可以组合使用来创造丰富的体验。例如,下面是一个当用户更改值时会触发请求的搜索框:

<input type="text" name="q"
    hx-get="/trigger_delay"
    hx-trigger="keyup changed delay:500ms"
    hx-target="#search-results"
    placeholder="Search..."
>

<div id="search-results"></div>

这一特定示例还定义了在向服务器发出请求前的 500ms 延迟,以避免在用户仍在输入时发送请求,让服务器只收到最相关的搜索查询,而不是全部输入 – 这种技术称为“去抖动”。

现在,您对 Htmx 有了大致的了解,我们将它添加到一个 Spring Boot 示例项目中并实现两个代码段的后端。


Spring Boot 中的

首个 Htmx 体验

开始之前,我建议为 JetBrains IDE 安装 Htmx 支持插件。这将极大提升您的 Htmx 开发体验。感谢 Hugo Mesquita!

在 IntelliJ IDEA 中使用 New Project(新建项目)对话框创建一个新的 Spring Boot 项目。如果您已经有一个 Spring Boot 项目,请跳过此步骤。

在下一个屏幕上,选择 Spring WebThymeleaf 依赖项。

首先,我们创建一个新的 HomeController 类。这将是我们为第一个示例添加应用程序逻辑的地方。

package org.example.htmxdemo;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {
    @GetMapping("/")
    public String home() {
        return "index";
    }
}

接下来,我们在 resources/templates/index.html 下创建 index HTML 模板文件。确保粘贴以下内容。提供的 HTML 中的 head 标记中已包含依赖项,客户端将在页面呈现给用户时检索这些依赖项。

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
    <title>Getting Started: Serving Web Content</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="color-scheme" content="light dark" />
    <title>Htmx Demo</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" >
    <script src="https://unpkg.com/[email protected]"></script>
</head>
<body>
    <main class="container">
        <section>
        <h1>Htmx Demo</h1>
        <div id="parent-div"></div>
        <button hx-post="/clicked"
                hx-trigger="click"
                hx-target="#parent-div"
                hx-swap="outerHTML">

            Click Me!
        </button>
        </section>
    </main>
</body>
</html>

Htmx 是一个 no-build 库,这意味着您不需要任何额外的依赖项即可使用它。如您所注意到的,在我们模板的 head 元素中,我们只需要对 HTML 中库的 script 引用。此外,我还包含了一个 CSS 库 PicoCSS,以便使页面更加美观。根据开发环境的浅色/深色模式设置,您的输出可能略有不同。

最终,您需要下载并存储所有第三方文件与代码以用于生产设置。

接下来,返回到 HomeController 并实现 /clicked 端点。记住,这需要使用 POST HTTP 方法进行处理。使用适当的 HTTP 方法来处理交互对 Htmx 开发至关重要。通常,使用 GET 进行不可变调用,使用 POSTPUTDELETE 进行可变调用。

package org.example.htmxdemo;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import java.time.LocalDateTime;

@Controller
public class HomeController {
    @GetMapping("/")
    public String home() {
        return "index";
    }

    @PostMapping("/clicked")
    public String clicked(Model model) {
        model.addAttribute("now", LocalDateTime.now().toString());
        return "clicked :: result";
    }
}

最后,让我们在 clicked.html 中实现 HTML 片段,随后将其放置在 resources/templates/ 中的其他模板文件旁边。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org" lang="en">

<head>
  <title>fragments</title>
</head>
<body>
  <div th:fragment="result" id="parent-div">
    <p th:text="${now}"></p>
  </div>
</body>
</html>

运行我们的应用程序,我们现在可以点击页面上的按钮并实时查看界面更新。

恭喜。您已成功处理了即将到来的许多 Htmx 请求中的第一个!

现在,让我们为更复杂的场景实现该搜索文本框。


Spring Boot 中由 Htmx 

提供支持的搜索

我们将向示例中添加一个新的搜索功能,实现之前展示的代码段。首先,更新 HTML 代码段以包括搜索用户界面。在 index.html 中,更新内容以匹配以下代码:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
    <title>Getting Started: Serving Web Content</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="color-scheme" content="light dark" />
    <title>Htmx Demo</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" >
    <script src="https://unpkg.com/[email protected]"></script>
</head>
<body>
    <main class="container">
        <section>
        <h1>Htmx Demo</h1>
        <div id="parent-div"></div>
        <button hx-post="/clicked"
                hx-trigger="click"
                hx-target="#parent-div"
                hx-swap="outerHTML">

            Click Me!
        </button>
        </section>

        <section>
            <input type="text"
                   name="q"
                   hx-get="/search"
                   hx-trigger="keyup changed delay:500ms"
                   hx-target="#search-results"
                   placeholder="Search..."
            >

            <div th:replace="search::results">
            </div>
        </section>

    </main>
</body>
</html>

resources/templates/ 中创建一个新的 search.html 文件,然后将以下内容复制到新创建的文件中。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org" lang="en">

<head>
    <title>fragments</title>
</head>
<body>
<div id="search-results" th:fragment="results">
    <ul th:each="result: ${results}">
        <li th:text="${result}"></li>
    </ul>
</div>
</body>
</html>

此文件包含我们的响应片段,它将显示用户发起的搜索的结果。我们来最后一次更新HomeController 

package org.example.htmxdemo;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import java.time.LocalDateTime;
import java.util.List;

@Controller
public class HomeController {

    static List searchResults =
            List.of("one""two""three""four""five");

    @GetMapping("/")
    public String home(Model model) {
        model.addAttribute("results", searchResults);
        return "index";
    }

    @GetMapping("/search")
    public String search(String q, Model model) {
        var filtered = searchResults
                .stream()
                .filter(s -> s.startsWith(q.toLowerCase()))
                .toList();

        model.addAttribute("results", filtered);
        return "search :: results";
    }

    @PostMapping("/clicked")
    public String clicked(Model model) {
        model.addAttribute("now", LocalDateTime.now().toString());
        return "clicked :: result";
    }
}

我们停下来思考一下我们在 HomeController 类中执行的操作。

  1. 我们的 home 方法为初始体验设置搜索结果。

  2. 我们的 index.html 使用 search :: results 片段

  3. 当用户输入时,/search 端点处理查询并返回修改后的 search :: results 片段与我们的新结果。

所有这些逻辑均基于我们对 Spring Boot 和 Thymeleaf 的了解实现。这真是太神奇了。重新运行应用程序并开始在搜索框中输入以查看筛选后的结果。

可能看起来不太像,但您已经实现了一些复杂的 Htmx 场景。除此之外,还有更多内容需要学习,而本文介绍的基础知识是一个绝佳起点。


社区参考

Htmx 拥有一个不断壮大的社区,社区的开发者充满热情,我们希望与您分享他们的一些作品。了解社区对 Htmx 的热情可能有助于减轻您对采用这项技术的焦虑。

如果想回顾后端技术,另请阅读我的指南系列文章“面向 ASP.NET Core 开发者的 Htmx”,其中包含许多可以根据 Spring Boot 进行调整的示例和技术。

对于认真投入 Htmx 开发的人,请查看适用于 Htmx 的 Spring Boot 和 Thymeleaf 库,它为您的 Spring Boot 应用程序添加了有用的元素。

另外,要获取本文中展示的示例代码,技术布道师 Marit van Djik 将一个完整示例推送到她的 GitHub 仓库


结论

如果您深受前端的困扰,并且更喜欢使用像 Spring Boot 和 Thymeleaf 这样的后端工具,您可以考虑在下一个解决方案中使用 Htmx。在我看来,Htmx 的一个最大卖点是您可以逐步将其分层。您的 Web 应用程序中的一些页面可能会大量使用 Htmx,而其他页面则完全不提及它。这可以显著加快您的交付速度,因为您可以减少与 JavaScript 构建工具打交道的时间,而将更多时间用于构建用户喜爱的解决方案。

我们希望您喜欢这篇文章。我们很乐意听到您集成 Htmx 和 Spring Boot 的经验。如果您有任何问题或反馈,请随时评论。


本博文英文原作者:Khalid Abuhakmeh

先行体验 JetBrains AI Assistant


IntelliJ IDEA 相关阅读

关于 IntelliJ IDEA

JetBrains 的旗舰 IDE IntelliJ IDEA 专为高效的 JVM 开发而设计。 凭借对语言和技术的深入了解以及符合人体工程学的用户界面,IntelliJ IDEA 使开发成为愉悦的体验!


免费的开源 IntelliJ IDEA 社区版和 IntelliJIDEA Edu 也可以用于学习和教学编程。

进一步了解 IntelliJ IDEA

⏬ 戳「阅读原文」了解更多

本文分享自微信公众号 - JetBrains(JetBrainsChina)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

微软开源基于 Rust 的 OpenHCL 字节跳动商业化团队模型训练被“投毒”,内部人士称未影响豆包大模型 华为正式发布原生鸿蒙系统 OpenJDK 新提案:将 JDK 大小减少约 25% Node.js 23 正式发布,不再支持 32 位 Windows 系统 Linux 大规模移除疑似俄开发者,开源药丸? QUIC 在高速网络下不够快 RustDesk 远程桌面 Web 客户端 V2 预览 前端开发框架 Svelte 5 发布,历史上最重要的版本 开源日报 | 北大实习生攻击字节AI训练集群;Bitwarden进一步脱离开源;新一代MoE架构;给手机装Linux;英伟达真正的护城河是什么?
{{o.name}}
{{m.name}}

猜你喜欢

转载自my.oschina.net/u/5494143/blog/16367457