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。