原文出處:https://dev.to/turculaurentiu91/why-you-should-choose-htmx-for-your-next-project-o7j

在本文中,我們將旨在了解為什麼您下次為 Web 應用程式選擇技術堆疊時應該考慮 HTMX 作為 React 的替代品。我們將研究傳統 HTTP JSON API + React 帶來的複雜性和挑戰,以及如何透過使用 HTMX 輕鬆避免它們。

注意:在本文中,我將討論 React,但它可以替換為任何其他前端框架,如 Angular、Vue、Svelte 或 Solid,但我談論 React 是因為它是大多數 Web 的預設技術開發人員預設為。

HTMX 到底是什麼

如果您還不知道,HTMX 是一個小型瀏覽器 (JS) 庫,它使用一些屬性擴展了 HTML,允許您使用來自伺服器。它還使 HTML 能夠對所有動詞發出 HTTP 請求,而不僅僅是 GET 和 POST。

React 解決了什麼問題

React 是一個 JavaScript 程式庫,可協助您透過保持使用者介面與狀態同步來編寫高度互動的應用程式。您告訴它如何渲染給定的狀態,每次更新狀態時,它都會重新渲染(盡可能有效率)UI 以反映狀態變更。

每次狀態發生變化時,您都會通知庫它發生了變化,並提供新的狀態,它將處理 UI 更新。

需要本地記憶體狀態的高互動應用程式的範例可以是您可以在網路上找到的各種文字編輯器(VSCode)之一、拖放看板(如 Trello 或 JIRA)、視訊播放器或聊天室。

什麼不是此類應用程式的範例?您正在建立的待辦事項清單、您正在閱讀的新聞網站、您發布的部落格以及周圍的大多數網站。如果我們看一下 80/20 規則

80% 的結果來自 20% 的原因,80% 的結果來自 20% 的努力。

你可以說 80% 使用 React 的 Web 應用程式不需要本地狀態。對於那 20% 需要它的人來說,你可以說它只是應用程式的一小部分(大約 20%),其餘部分只能用 HTML 來表達。

這些數字是編造的,我沒有任何研究來支持這一點

React 也解決了哪些問題,使其被現代網站廣泛採用

HTML 已經過時了。使用 HTML 製作應用程式的舊方法涉及頁面、連結和表單的集合,這些頁面、連結和表單向使用者描述給定資源的當前狀態以及他們可以執行哪些操作來更改它。

每次使用者與資源互動時,應用程式只能重新載入整個頁面以顯示資源的新狀態。

幾年後,FaceBook 推出了 React,這是一個 JS 程式庫,可讓開發人員建立單頁應用程式 (SPA)。導航時不再需要重新加載整個頁面,狀態更新的酷過渡、對用戶的有趣反饋以及其他使 Web 開發人員在其網站中採用 SPA 框架的細節。

複雜性問題

AI 產生圖像,透過 next.js 展示現代應用程式的瘋狂複雜性

如果你不理解上面的架構,不用擔心,沒有什麼好理解的。我要求 ChantGPT 為我生成它,由於它過於複雜且沒有任何意義,它完美地反映了現代 Web 應用程式目前的預設基礎架構。

一個很酷的程式設計原則是KISS,它代表“保持簡單,愚蠢”,或者有些人可能喜歡開玩笑,“保持簡單,愚蠢!”

現代開發人員預設建立 Web 應用程式的當前基礎設施和技術堆疊極其複雜,做了很多不必要的事情,只是因為它很酷!

當您自己建立第一個 POC 時,效果很好,但下一刻您加入了更多團隊成員,並且使用多次迭代和「擁抱」更改的敏捷方式,它有點崩潰,原因是我們將往下看一下。

傳統 HTTP JSON API + React 的狀態管理問題

在 Web 應用程式中,您經常要做的就是從資料庫中獲取資源的狀態並將其呈現給使用者。讓我們以任務管理應用程式為例。使用者有一個任務列表,每個任務都有一個狀態:

  • 任務的標題

  • 說明

  • 任務完成時的標誌

  • 截止日期(可選)

我們通常將此狀態儲存在資料庫中,並將此資訊呈現給用戶,您必須:

  • 從使用者有權存取的資料庫中取得所有任務。

  • 可以選擇轉換資料(也許您儲存完成的日期並從中計算「is_completed」標誌)。

  • 將資料序列化為 JSON。

  • 透過 HTTP 請求取得資料。

  • (可選但通常)根據模式驗證資料,可能使用 YUP 或 ZOD。

  • 使用 Redux、Zustand、react-query 或其他狀態管理庫將 JSON 轉換為狀態並將其儲存在快取中。

  • 轉換 HTML 中的狀態,通常會決定使用者可以使用資料做什麼。

簡而言之,我們正在描述如何在 JavaScript 中渲染所有資源的所有可能狀態,在瀏覽器中下載所述 JavaScript,然後 JavaScript 下載一堆 JSON 格式的資料並將其渲染(如果它知道如何)瀏覽器顯示為HTML!

向使用者顯示任務清單需要做大量工作,特別是當任務僅在使用者變更時才更改時,應用程式必須將應用程式置於載入狀態,發出另一個 HTTP 請求(以PUT 或PATCH 或DELETE)使快取值(狀態)無效並重新獲取它以顯示更改的任務。

或者更糟的是,當用戶更改某些任務時,樂觀地更新本地狀態並立即顯示更改,並在幕後執行更新請求,僅在他們成功更新後通知用戶更新失敗。

這是非常容易出錯的。對於這個待辦事項應用程式來說,它可能會很有效,因為您是唯一的開發人員,並且該應用程式足夠大,您可以在腦海中保留正在發生的所有事情的地圖。但是當你的團隊規模更大時,尤其是當你將團隊分成前端和後端時,溝通不良可能會導致很多問題。

後端可能使用“is_completed”標誌,而前端可能需要“is_active”標誌。後端可能會將經過 Markdown 處理的「描述」傳送到 HTML,而前端可能希望它不被處理。後端可能會將“描述”設為可選,以允許使用者在前端不同步時保存草稿,並且您會看到許多“未捕獲的類型錯誤:無法讀取未定義的屬性(讀取“toLowerCase” )”

另一方面,在 HTMX 上,您可以直接在範本上呈現 HTML,只要後端語言允許,就可以確保類型安全。您僅向瀏覽器發送相關訊息,向使用者提供對資源的適當控制,並指示瀏覽器或 HTMX 如何解釋使用者操作以及後端對這些操作的回應。所有應用程式狀態都是 HTML,這個概念稱為 HATEOAS

傳統 HTTP JSON API + React 對文件的需求

為了讓兩個團隊(後端和前端)獨立工作並透過 HTTP JSON API 進行通信,您需要擁有適當的 API 文件。您還需要記錄如何計算使用者可以對給定資源執行哪些操作以顯示控制項。

大多數此類文件寫起來都很痛苦,特別是因為通常需要在實作之前編寫,而開發人員尚未完全理解問題的範圍,因此前端可以並行開發。這通常會在開發過程中進行許多更新,以適應開發過程中出現的問題,並可能導致團隊之間的版本不一致。

您還需要對 API 進行版本控制,並注意不要在非主要版本變更中引入重大變更。您無法再在不變更主要版本的情況下變更欄位名稱。您還需要保持 API 的多個版本運作或強制前端團隊進行調整。

大多數時候,文件已經過時了。有些必須緊急修復,有些新要求是在發布前一天提出的,現在您的文件已經過時,即使在很短的時間內。而且您必須記住更新它,或者更糟的是,您建立了一張票來記住它,而其他人拿起它,但沒有完整的圖片並記錄了錯誤!

重複的邏輯問題

對於每個資源,您必須實施授權策略。您必須確定目前使用者是否可以將任務 46234 標記為已完成。您必須在後端程式碼的某個位置編寫此檢查。否則,您的應用程式將開放給不安全的直接物件參考,或任何使用 Postman 的人都可以將您的任務標記為已完成。

您還必須在前端實現相同的邏輯,僅當用戶有權標記已完成時才顯示標記按鈕(讓我們假設您可以與其他用戶共享您的任務,但只有您可以更改它們)。

現在每次這個邏輯改變時,你都必須在兩個應用程式中實作它,並同時發布它或擁有多個版本的API。

效能問題

為了使用 React 在瀏覽器中呈現網站,您需要將佔用大量內存和解析/處理影響的 React 程式碼、狀態管理庫程式碼、腳趾 UI 庫程式碼、CSS-IN- 捆綁在一起。JS 庫程式碼、應用程式程式碼以及我們透過NPM 安裝和使用的任何js 程式庫(我們並不羞於安裝新套件,請參閱leftpad 問題)。這通常會導致透過網路傳送大塊的 JavaScript 資源。當然,您可以在瀏覽器中緩存,但在現代敏捷開發中,每個衝刺至少部署一次,因此這無法解決任何問題。這會消耗網路流量和電池,這對行動裝置來說是一個經常被忽視的問題。

上述JavaScript需要由瀏覽器來解釋,消耗處理能力和電池。

JavaScript,尤其是 ReactDOM,需要追蹤 DOM 的鏡像。在其之上加入普通 DOM 和本地狀態緩存,以及所有渲染函數,以及所有“useMemo”、“useCallback”和“useState”。也要加入需要在記憶體中保存所有上下文變數的所有閉包。 JavaScript 引擎並不以其記憶體效率而聞名!您會聽到人們抱怨瀏覽器消耗了多少內存,但他們低估了他們存取的網站所消耗的內存量。

所有這些加起來,最終會耗盡用戶的電池和記憶體。當然,您可以付出努力並優化所有這些,或使用其他程式庫(如 Svelte),但所有這些努力都可以用於為您的用戶提供更有意義的功能。

服務端渲染的需要

近年來,我們播種了伺服器端渲染專用框架(如「Next.js」)的興起。它們的流行凸顯了對以 HTML 格式交付內容的需求,特別是出於可存取性優化、效能和搜尋引擎優化的原因。

你不想等待瀏覽器下載 JavaScript 來渲染頁面,然後等待 JavaScript 發出 HTTP 請求來獲取內容然後渲染它,你希望它立即渲染,特別是對於上面的情況折疊內容。

這又增加了一層複雜性,包括:

  • 基礎設施,現在您還需要另一台伺服器用於前端應用程式

  • 程式碼更複雜,包括什麼程式碼在伺服器上執行以及什麼在瀏覽器上執行的思維導圖

  • 部署管道現在更加複雜

  • 測試基礎設施現在更加複雜

  • 現在解決問題變得更加困難,您需要了解問題是在瀏覽器上、在客戶端應用程式伺服器上還是在 API 伺服器上

解決這些問題

Web 開發社群各自使用自己的語言或開發技術,以不同的方式解決這些問題:

  • Next.js(以及 Nuxt 等)

  • 反應伺服器元件

  • 拉維爾

  • 慣性.JS

  • 活線

  • 點網

  • Blazor 頁面

  • 靈藥

  • 鳳凰即時查看

  • 鐵鏽

  • 樂浦伺服器功能

還有許多我忘記或從未聽說過的其他解決方案!

無論如何,此類解決方案的存在和流行證明了這些問題是有效的並且在 Web 開發人員的日常生活中遇到過。否則他們不會不遺餘力地解決這些問題,尤其是以開源的方式!

還有 Turbo 以及採用它們的框架、Ruby on Rails、PHP Symphony 以及其他可能以與 HTMX 相同的方式解決相同問題的框架。選擇 HTMX 只是個人喜好,但你絕對應該了解這一點,這和 HTMX 一樣酷!

在所有這些中,HTMX 脫穎而出,不僅因為它不會將您鎖定到特定技術,您可以透過對模板進行微小更改來從 PHP 切換到 Rust,而且還因為它完全消除了對有狀態元件的需求,或者需要追蹤與資源無關的應用程式的某種狀態。

例如,讓我們採用確認對話方塊模式。您通常最終要做的是,您有一個本地記憶體狀態(如果它是開啟的),並根據該狀態將其顯示給使用者。在 HTMX 中,狀態 IS THE HTML 意味著當您按一下開啟模式時,您 GET tasks/{taskId}/confirm-delete 並將回應 HTML 嵌入到 DOM 中。當它被刪除時,您就完全刪除了模式和任務!這以一種獨特且極其簡單的方式解決了上述所有問題,您不需要:

  • 追蹤狀態

  • 知道如何渲染對話框

  • 記錄API

  • 檢查使用者是否可以刪除任務(在前端)

  • 您的後端應用程式始終負責

  • 您可以獲得更好的安全性,因為您不會向瀏覽器發送不相關的資料並竊取敏感資訊

  • 你會得到更好的表現

最重要的是,您可以讓您的應用程式保持簡單,只有在解決使用者問題時才允許複雜性!

您只需指示 HTMX 從何處獲取對話框以及將其放置在何處,一切就完成了!


<!-- the delete button -->
@if ($chirp->user->is(auth()->user()))
    <form>  
        @csrf  
        @method('delete')  
        <x-dropdown-link  
            :component="'button'"  
            type="submit"  
            hx-get="{{ route('chirps.confirm-destroy', $chirp) }}"  
            hx-swap="beforeend"  
            hx-target="closest .chirp"  
    >    
            {{ __('Delete') }}  
        </x-dropdown-link>  
    </form>
@endif

<!-- the dialog template -->
<div class="modal fixed z-10 inset-0 overflow-y-auto flex justify-center items-center bg-black bg-opacity-50" style="backdrop-filter: blur(14px);">  
    <div class="bg-white rounded p-6">  
        <h2 class="text-xl border-b pb-2 mb-2">Confirm Action</h2>  
        <p>Are you sure you want to delete this chirp?</p>  
        <div class="flex justify-end mt-4 gap-4">  
            <x-secondary-button _="on click remove closest .modal" >
                Cancel
            </x-secondary-button>  
            <form>                
                @csrf  
                <x-danger-button  
                    hx-delete="{{route('chirps.destroy', $chirp)}}"  
                    hx-target="closest .chirp"  
                    hx-swap="delete">  
                        Delete  
                </x-danger-button>  
            </form>
        </div>    
    </div>
</div>

這個範例來自我的 [HTMX with Laravel] 教學(https://dev.to/turculaurentiu91/laravel-htmx--g0n) ,請看!

就像這樣,當我們單擊刪除按鈕時,我們指示 HTMX 對 chirps/{chirp}/confirm-destroy 執行 GET 請求,並將結果 HTML 放在最接近的父級 < 之前div class="chirp"> 結束(在底部)。在刪除對話方塊中,當使用者確認時,我們指示 HTMX 對 chirps/{chirp} 端點執行 DELETE 請求,成功後,我們刪除具有 chirp 類別的最接近的父級。

結論

在不斷發展的 Web 開發領域,看到 HTMX 等倡導簡單性和回歸基礎的工具令人耳目一新。透過利用 HTML 和 HTTP 的強大功能,HTMX 允許開發人員建立動態 Web 應用程式,而無需傳統 JavaScript 框架的複雜性和開銷。

因此,下次您開始新專案或考慮重構現有專案時,請嘗試 HTMX。您可能會驚訝於用這麼少的錢就能取得如此大的成就。


共有 0 則留言