HTMX 对于 WordPress 来说可能是一件大事

已发表: 2024-05-10

在浏览器中构建丰富的用户体验可能是一项具有挑战性的任务,通常需要大量 JavaScript 代码。 随着应用程序的需求和目标不断增长,JavaScript 代码的复杂性也随之增加。 因此,开发人员经常修改我们思考和编写 JavaScript 应用程序的方式也就不足为奇了。

距离上一个 JS 框架已经过去了几天。

WordPress 插件开发者的处境更糟。 我们的目标环境既不是我们控制的服务器,也不是能够完全控制整个页面的服务器。 虽然很可能在 WordPress 插件中成功使用 JavaScript 框架,但也很容易最终得到一个规模和复杂性超出您预期或预期的项目。

但如果不需要这样呢? 在本文中,我们将探讨如何使用 JavaScript 构建现代 Web UI、开发人员面临的困难以及 HTMX 提供的替代方案。 特别是,我们将了解为什么 HTMX 和 WordPress 可能是天作之合。

我们是如何到达这里的

在 JavaScript 之前,浏览器基本上只是美化的文档阅读器。 因此,大多数网络体验都是“多页面应用程序”,简称 MPA。 MPA 是由多个 HTML 文档组成的 Web 应用程序,应用程序中的每一页都有一个文档。 当用户使用该应用程序时,他们会看到具有不同可用操作的不同文档。

海洋保护区的建设非常简单。 导航是使用<a>标签链接到其他文档来完成的,并且可以使用<form>元素捕获用户输入。 服务器使用新的 HTML 文档响应链接和表单请求,替换屏幕上显示的页面。

您可能非常熟悉的 MPA 的一个很好的例子是 WP Admin。 每个管理页面都是一个包含 HTML 的文档,该文档由服务器上运行的 WordPress PHP 代码生成。 WP Admin 中的大多数页面(例如 WordPress 设置页面)往往主要由您(用户)可以提交的表单组成。

相反,单页应用程序(SPA)是使用单个 HTML 页面的应用程序。 然后,导航和用户输入由 JavaScript 代码处理,动态地就地更改页面的某些部分,而无需换出整个页面或刷新它。 这会带来更流畅、响应更灵敏的用户体验。

如今,许多 Web 应用程序都使用 SPA 作为其 Web 界面。 在 RebelCode,我们为两个主要 WordPress 插件的管理界面构建了 SPA:Spotlight 和 Aggregator。 WordPress 中相对较新的站点编辑器也是 SPA,基于块的帖子编辑器也是如此。

我们付出的代价

SPA 是它们自己的应用程序,用 JavaScript 编写并在浏览器中运行。 他们自己的定义也是他们最大的警告:他们无法立即访问服务器资源。 这意味着我们需要在SPA和服务器之间建立一个通信通道。

让我们创建一个简单的 WordPress 插件来使用示例。 这个插件提供了
用于管理书籍的简单 CRUD 用户界面。 服务器上插件的内部 API 可能如下所示:

 <?php function get_books(?int $count = null, int $page = 1): Book[]; function get_book(int $id): Book; function insert_book(Book $book): Book; function update_book(Book $book): Book; function delete_book(int $id): void;

为了创建我们的现代 SPA 界面,我们将使用像 React 这样的 JavaScript 框架; 最流行的 JavaScript 框架,WordPress 也使用它。 让我们首先添加一个管理页面:

 <?php add_action('admin_menu', function () { add_menu_page('Books', 'Books', 'manage_options', 'books', 'books_page'); }); function books_page() { echo '<div></div>'; }

我们的页面将渲染一个空的<div>元素,它将作为 React 应用程序的根,UI 的其余部分将在其中渲染。

 const rootEl = document.getElementById("books-app"); const root = ReactDOM.createRoot(rootEl); root.render(<BooksApp />); function BooksApp() { return ( <div> <h1>My Books</h1> ... </div> ); }

那么我们如何列出数据库中存储的书籍呢? 执行此操作的代码位于服务器上,因此我们需要一种方法来调用它并获取其结果。

为此,我们可以从服务器公开 JSON API。 然后,React 应用程序可以向我们的 API 的 URL 发出请求,接收 JSON 格式的书籍,然后呈现列表。 对于此示例,假设我们已向 WordPress REST API 添加了一个端点:

 GET https://my-wp-site.com/wp-json/books { "books": [ { "id": 15, "title": "Mistborn", "author": "Brandon Sanderson", }, { "id": 44, "title": "The Hobbit", "author": "JRR Tolkien", }, ] }

然后我们可以编写一个 React 组件来获取书籍并将它们呈现为列表:

 function BookList() { const [books, setBooks] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect( function () { setIsLoading(true); fetch("https://my-wp-site.com/wp-json/books") .then((res) => res.json()) .then((data) => setBooks(data.books)) .else((error) => setError(error)) .finally(() => setIsLoading(false)); }, [setBooks, setIsLoading], ); if (isLoading) { return <div>Loading...</div>; } if (error) { return <div>Error: {error}</div>; } return ( <ul> <li> {books.map((book) => ( <a key={book.id} href={book.url}> {book.title} </a> ))} </li> </ul> ); }

但这个解决方案过于幼稚,并且产生了粗糙的用户体验。 它不适应组件卸载后的状态更改、缓存响应、重试失败的查询或防止过时状态覆盖较新的状态。 事实上,我们通常不鼓励在 React 效果中使用fetch()方式。

在很多方面,这可能比传统的 MPA 更糟糕。 因此,为了正确地做到这一点,我们需要在客户端中实现更多的东西。 或者,更实际的是,使用第 3 方软件包。

但所有这些都开始让人觉得仅仅为了呈现一个图书清单就需要付出不成比例的努力。 我们真的需要创建 JavaScript 应用程序和 JSON API 来创建流畅的用户体验吗?

让我们将其与 MPA 进行对比,在 MPA 中,只需几行 PHP 代码即可完成书籍列表的呈现,而无需任何依赖项:

 <?php function render_books() { ?> <ul> <?php foreach (get_books() as $book): ?> <li> <a href="<?= $book->url ?>"> <?= $book->title ?> </a> </li> <?php endforeach; ?> </ul> <?php }

但当然,这不是一个公平的比较。 这个图书列表只是静态 HTML; 它不会对状态变化或用户输入做出反应。

如果我们想要获得类似 SPA 的体验,同时还要在服务器上渲染 HTML,其中我们的代码可以立即访问数据库,那么我们需要找到一种方法,让服务器渲染的 HTML 找到浏览器的路径并替换之前的书籍列表。 但目前不使用任何 JavaScript 代码来实现这一目标是不可能的,因此我们无论如何都必须硬着头皮使用 JavaScript。

但我们不需要自己写。

HTMX 简介

HTMX 是一个小型 JavaScript 库,主要做一件事:允许 HTML 从服务器请求新的 HTML。 它使用新的属性来完成此操作,这些属性允许我们告诉 HTMX 从哪里获取新的 HTML、用什么交换它以及什么触发交换。 它充当 HTML 服务器和浏览器页面之间的桥梁。

这是一种截然不同的 SPA 思考方式,因为我们不是构建客户端 JavaScript 应用程序来更新当前页面。 相反,我们只需添加一些 HTML 属性来告诉 HTMX 当某些事件发生时我们希望页面如何更改。

即使没有 HTMX,您也可以仅使用 HTML 更改屏幕上显示的内容,尽管方式非常有限。 您已经熟悉这个 HTML 功能:不起眼的<a>链接元素。

 <a href="https://my-wp-site.com/books">View books</a>

链接元素为浏览器提供了执行导航所需的所有信息。 单击该按钮后,浏览器会从该元素获取href ,在该 URL 发出请求,下载响应,并假设它包含 HTML,然后用新的 HTML 替换页面内容。

<form>元素是 HTML 如何请求新 HTML 的另一个示例。

 <form action="/contact.php"> <label> Your message: <input type="text" name="message" /> </label> <button type="submit">Send message</button> </form>

这次,浏览器从表单中的所有输入中收集值,将它们发送到服务器,下载响应并将其呈现在屏幕上。

为什么只有<a><form>能够发出 HTTP 请求? 为什么只能更换整个屏幕?

来自 HTMX 的 GitHub 自述文件

嗯,HTMX 改变了这一点。

 <a href="https://my-wp-site.com/books" hx-target="#books"> View books </a> <div></div>

使用 HTMX hx-target属性,单击链接现在会将来自https://my-wp-site.com/books的响应放置在具有"books" ID 的元素内。 当然,将一个页面嵌入另一个页面并不是这里的目标。 我们的服务器不需要响应整个页面,而可以只响应 HTML 片段。

通过从我们的服务器公开 HTML 片段并告诉 HTMX 如何、从哪里以及何时获取这些片段,我们可以创建一个类似 SPA 的 Web 应用程序,而无需任何 JavaScript,其中服务器完全控制。 从某种意义上说,HTML 已经成为我们新的 JSON。

我们需要做的就是将 HTMX 脚本加载到我们的页面中:

 <script src="https://unpkg.com/[email protected]"></script>

(请务必查看 HTMX 文档以获取说明,因为上述代码可能已过时)。

让我们看另一个例子:

 <button hx-get="/button/off" hx-target="this" hx-swap="outerHTML"> Turn off </button>

这里还发生了很多事情,所以让我们来分解一下:

  • hx-get指定单击按钮时发送GET请求的 URL。
  • hx-target="this"告诉 HTMX 将单击的按钮与响应交换。
  • hx-swap="outerHTML"告诉 HTMX 交换整个按钮,而不仅仅是其中的内容。

总而言之,这告诉 HTMX:

单击该按钮时,向/button/off发送 GET 请求,并用响应替换该按钮。

假设服务器使用以下 HTML 响应/button/off

 <button hx-get="/button/on" hx-target="this" hx-swap="outerHTML"> Turn on </button>

你能看到区别么? hx-get属性现在指向/button/on并且按钮内的文本现在是“Turn on”。 单击此按钮时,它也将被替换为/button/on的响应。 正如您可以想象的那样,我们可以让服务器使用原始按钮进行响应来完成我们的切换!

允许任何元素从服务器请求新的 HTML 并决定 HTML 的去向,这个简单的想法被证明是非常强大的。 我们可以创建选项卡、使用实时结果进行搜索、进度条等等。

优点和缺点

与大多数 JavaScript 框架不同,HTMX 不需要编译和捆绑我们的客户端应用程序代码。 仅此一点就是巨大的好处; JavaScript 构建系统的设置和维护非常困难,尤其是当您开始引入更多奇特的功能和库(例如 TypeScript、JSX、CSS 预处理器等)时。对于中型到大型团队来说,拥有一名或多名成员致力于这项任务。

另一个明显的好处是不需要单独的客户端应用程序。 由于我们需要的是一个以 HTML 响应的 HTTP 服务器,因此我们可以使用任何我们喜欢的编程语言。 如果您的团队不熟悉现代 JavaScript,或者规模不够大,无法构建两个应用程序,那么这对您来说可能是一个很大的卖点。 如果您是 WordPress 插件开发人员,这可能会特别有吸引力,因为您可以将 PHP 用于插件的所有方面。

但也许最重要的好处是您不再需要应用程序的后端和前端之间的 API。 这可以节省大量的开发时间,并减少可能产生错误的代码量,从长远来看,这也可以节省时间。

然而,我们不应该天真地认为使用 HTMX 就意味着不需要编写任何JavaScript。 拖放、图表、颜色和日期选择器等操作可能仍需要一定量的 JavaScript。 尽管我们始终可以使用与框架无关的解决方案,例如 SortableJS 和 Floating UI。 此外,随着 Web 标准随着新的 HTML 元素(例如最近的<dialog>元素)不断发展,我们可能还会发现未来对 JavaScript 的需求会减少。

其次,具有讽刺意味的是,PHP 并不擅长 HTML 模板,尽管它就是为了做到这一点而构建的。 它的标签语法过于冗长,并且它的 HEREDOC 字符串语法对字符串插值的支持有限。

最后,在 WordPress 中创建端点并不是很简单。 考虑前面示例中的书籍插件。 我们需要在服务器上有一个路径,以 HTML 形式响应图书列表。 我们如何注册这个端点?

  • initadmin_init操作期间检测到 GET 参数?
  • 使用管理 Ajax API?
  • 注册 REST API 端点?
  • 添加自定义重写规则?

有很多选择,但没有一个能让事情变得像应有的那么简单。

哈特奥阿斯

在我们之前的例子中,有一个很容易被忽略的微妙细节,一旦被指出,它可能听起来很明显。

当我们从服务器获取 HTML 时,页面上的按钮要么是 ON 变体,要么是 OFF 变体。 根据屏幕上显示的内容,点击操作会有所不同。

正因为如此,浏览器不需要理解我们的应用程序。 我们通常会通过提供 JavaScript 代码来显式编程所有行为来浏览器理解。 现在,我们只有 HTML,浏览器不需要先了解我们的应用程序的行为或其状态。 它只需要在屏幕上呈现 HTML,它本身对我们的应用程序的状态进行编码。

这种类型的架构称为 HATEOAS,代表“超媒体作为应用程序状态引擎”。 它是一种特殊类型的 REST 架构,使用超媒体作为状态传输的媒介,并且相同的超媒体成为用户将应用程序驱动到新状态的接口。

如果您有兴趣了解更多信息,HTMX 网站上有大量有关该主题的文章、论文和讲座。 出于本文的目的,我们将继续讨论为什么 HTMX 对于 WordPress 开发人员来说很重要。

HTMX <3 WordPress

WordPress 是一个巨大的、整体的 PHP 服务器。 WordPress 插件也主要是用 PHP 编写的。 他们可以使用 WordPress 提供的 PHP API(例如 Hooks API 和数据库 API)向网站添加新功能。 JavaScript 无法提供这些 API,因此插件开发人员应该希望将尽可能多的插件代码保留在服务器上。 如果有使用 HTMX 的动机,那就是它了!

从很多方面来说,HTMX 都是为 WordPress 构建的。 或者更确切地说,对于WordPress 这样的应用程序; 那些不愿承受外语负担的应用程序,迫使他们放弃服务器 API 集合。 尤其是当使用超媒体简单地传输状态就足够了时。

更轻松地为 WordPress 插件创建良好的 UI 可以对插件生态系统产生巨大影响。 维护免费插件的开发人员可能会发现为用户构建更好的用户体验更加可行,而较小的团队可能能够在节省时间的情况下更快地迭代功能。 这有助于使较小的插件在由预算更大的大团队主导的市场中更具竞争力。

更大的插件也可能特别感兴趣。 JavaScript 应用程序可以呈指数级快速增长。 HTMX 可以允许这些插件删除其大量的 JSON API 和 JavaScript 应用程序,并在其位置保留一个精简的 HTML 服务器,该服务器可以完全访问 WordPress API。

最后的想法

我已经使用 HTMX 一段时间了,将其与 PHP 和 Go 一起使用。 它为在网络上构建用户界面提供了令人信服的替代方案,并为使用超媒体驱动应用程序状态提供了令人信服的论据。

如果您是插件开发人员,请务必查看 HTMX。 我们在本文中仅仅触及了皮毛,文档写得非常好,有大量的示例。 考虑到 HTMX 开箱即用的功能,它的长度也出奇的短。 您应该能够在几分钟内开始使用 HTMX 和 PHP。