Read this post in other languages:
开始一个新项目总是既令人兴奋又充满挑战。 选择哪些技术来实现您的解决方案,需要对这些选择带来的成本加以权衡。 每增加一项技术,可能引入的问题和依赖项都可能导致进度逐渐放缓,甚至停滞不前。 前端是开发者经常感到决策疲劳的地方。 React、Angular 和 Vue 等前端框架的流行确实带来了许多价值,但也在工具、安全考量因素、网络流量和巨大的初始负载方面产生了代价高昂的权衡。 如果您在为下一个项目做出前端决策时感到不知所措,那么这篇文章非常适合您。
在这篇文章中,我们将探索一个名为
Htmx
的新兴库,它允许您利用现有的 Spring Boot 知识来提供交互式用户体验,同时避免您在使用其他前端框架时可能遇到的部分挫折。 阅读完这篇文章后,您应该对将 Htmx 添加到新项目或现有 Spring Boot 项目中充满信心。
什么是 Htmx?
要理解什么是 Htmx,我们必须首先从哲学上理解这个库试图实现的目标。
Htmx 毫不掩饰地以加入超媒体阵营为荣。
超媒体是 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 Web
和
Thymeleaf
依赖项。
首先,我们创建一个新的
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">
<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>
<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
进行不可变调用,使用 POST
、PUT
和 DELETE
进行可变调用。
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">
<title>fragments</title>
</head>
<div th:fragment="result" id="parent-div">
<p th:text="${now}"></p>
</body>
</html>
运行我们的应用程序,我们现在可以点击页面上的按钮并实时查看界面更新。
恭喜。 您已成功处理了即将到来的许多 Htmx 请求中的第一个!
现在,让我们为更复杂的场景实现该搜索文本框。
Spring Boot 中由 Htmx 提供支持的搜索
我们将向示例中添加一个新的搜索功能,实现之前展示的代码段。 首先,更新 HTML 代码段以包括搜索用户界面。 在 index.html
中,更新内容以匹配以下代码:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<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>
<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">
</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">
<title>fragments</title>
</head>
<div id="search-results" th:fragment="results">
<ul th:each="result: ${results}">
<li th:text="${result}"></li>
</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
类中执行的操作。
我们的 home
方法为初始体验设置搜索结果。
我们的 index.html
使用 search :: results
片段
当用户输入时,/search
端点处理查询并返回修改后的 search :: results
片段与我们的新结果。
所有这些逻辑均基于我们对 Spring Boot 和 Thymeleaf 的了解实现。 这真是太神奇了。 重新运行应用程序并开始在搜索框中输入以查看筛选后的结果。
可能看起来不太像,但您已经实现了一些复杂的 Htmx 场景。 除此之外,还有更多内容需要学习,而本文介绍的基础知识是一个绝佳起点。
Htmx 拥有一个不断壮大的社区,社区的开发者充满热情,我们希望与您分享他们的一些作品。 了解社区对 Htmx 的热情可能有助于减轻您对采用这项技术的焦虑。
官方 Htmx 网站和文档 – https://htmx.org/
Josh Long – HTMX 和 Spring Boot
SivaLabs – Spring Boot Thymeleaf HTMX 教程
Dan Vega – 在 Spring Boot 中使用 Thymeleaf 轻松上手 HTMX
Will Iverson –Spring Boot + HTMX = 轻松实现全栈
Wim Deblauwe – Spring 提示: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 的经验。 如果您有任何问题或反馈,请随时评论。
提交此表单,即表示我同意 JetBrains s.r.o. ("JetBrains") 使用我的姓名、电子邮件地址和位置数据向我发送简报和商业通讯,并为此目的而处理我的个人数据。我同意 JetBrains 根据 JetBrains 隐私政策为此目的使用第三方服务处理上述数据。我了解我可以在我的个人资料中随时撤回此同意。此外,每封电子邮件中也都包含退订链接。
Java 23 和 IntelliJ IDEA
Java 23 包含全新和更新的 Java 语言功能、核心 API 以及 JVM,同时适合新的 Java 开发者和高级开发者。 IntelliJ IDEA 2024.2 现已支持 Java 23 功能。
跟上 Java 新版本的发布节奏可能很难 – 更改是什么、为什么要更改以及如何使用全新和更新的功能。 在这篇博文中,我将介绍 Java 23 的一些全新和更新功能 – 它们为您解决的痛点、语法和语义,以及 IntelliJ IDEA 如何帮助您使用它们。
我将重点介绍 Java 23 功能,例如在模式匹配中包含基元数据类型、在代码库中导入模块的能力、在文档注释中使用 Markdown 的可能性…
Java 如今还重要吗?
当今技术面临的重大挑战是如何跟上最新发展、满足现代商业需求并持续为用户提供附加价值。 世界正在以前所未有的速度发生变化,并且这种变化的速度没有减缓的迹象。
作为一种主要编程语言,Java 也需要应对这些挑战。 随着更新、更简化的语言不断涌现, Java 有能力保住领先地位吗? 对于刚刚开始 Java 开发生涯的新人来说,Java 还有潜力吗?
在这篇博文中,我们汇集了 2023 开发者生态系统现状和 2022 开发者生态系统现状报告中的数据*,探索这些问题的答案以及其他信息。 我们将对比 Java 和其他语言,找出使用它的行业和产品,提供数据帮助您判断这种语言是否值得学习,并额外分享一个代表性…
在典型的调试场景中, 你会设置断点来告诉调试器何时暂停你的程序。 断点通常标志着进一步调查的开始。
决定在哪里设置断点并不简单。 有时会发生不清楚确切行数的情况, 也有开发者更倾向于根据时间而不是特定代码来暂停程序。
在本文中,我们将探讨 IntelliJ IDEA 的暂停程序 (Pause Program) 功能—— 这是一个较少为人知,但是在某些场景下又极其强大的调试技术, 包括上述提到的情况。 我们将讨论它的使用场景和限制,并分享一个隐秘的单步调试技巧。
什么是暂停功能?
暂停程序 (Pause Program) 是 IntelliJ IDEA 调试器中的一项功能,让开发者能够在任意时刻…
Java 运行时:Spring Boot 角度的见解
为您的 Spring Boot 项目选择合适的 Java 运行时乍看可能十分简单。 毕竟,所有运行时都基于 OpenJDK 代码并提供相同的 API。
但并非所有运行时都生来平等。 本文将探讨可能影响您为 Spring Boot 应用程序选择特定 Java 发行版的决定的各种指标。
本文由 JetBrains IntelliJ IDEA 的团队主管 Aleksey Stukalov 和客座作者 Catherine Edelveis(BellSoft 的技术布道师)撰写。
开发者的视角
功能兼容性
对于大多数开发者来说,使用哪个 Java 发行版并无太大区别。 无论您是在开发 Spring Bo…