前端工作方式要換了?HTMX簡介:無需JavaScript的動態(tài)HTML
HTMX允許你使用擴展的HTML語法代替 JavaScript 來實現(xiàn)交互性。HTMX 在標記中直接為你提供HTTP 交互,并支持許多其他交互需求,無需求助于 JavaScript。這是一個有趣的想法,可能最終會影響到web前端的工作方式。讓我們看看如何使用HTMX以及它的吸引力。
什么是HTMX?
HTMX已經(jīng)存在了一段時間,但它一直是一個不太為人知的項目。它最近被接受到 GitHub Accelerato r中可能會改變這一切。基本的想法是取代那些需要模板化的 JavaScript 和 HTML 交互的常見用例,僅使用HTML語法,而不是 JavaScript。許多交互與HTMX一起變得聲明式。
這聽起來很有前景,不是嗎?每個web開發(fā)者都知道有很多常見的模板化用例。HTMX的創(chuàng)建者Carson Gross表示,他希望“完善HTML作為超文本,增加其表現(xiàn)力,使其成為更先進、現(xiàn)代web應用程序的有力競爭者。”
為了快速了解,看看這個HTMX演示?;旧?,我們點擊一個按鈕來啟用對用戶對象的字段進行編輯。數(shù)據(jù)實際上是PUT到一個后端端點。你可以在圖1中看到演示 —— 在你點擊“顯示”后注意底部框架中的網(wǎng)絡交互。
通常,無論你使用什么框架,這都需要某種形式的JavaScript。
HTMX 將交互轉(zhuǎn)變?yōu)閮蓧K標記:一個用于顯示UI,一個用于編輯UI,如Listing 1所示。
Listing 1. HTMX中的用戶更新
<div hx-target="this" hx-swap="outerHTML">
<div><label>First Name</label>: Joe</div>
<div><label>Last Name</label>: Blow</div>
<div><label>Email</label>: joe@blow.com</div>
<button hx-get="/contact/1/edit" class="btn btn-primary">
點擊編輯
</button>
</div>
<!-- 編輯: -->
<form hx-put="/contact/1" hx-target="this" hx-swap="outerHTML">
<div>
<label>First Name</label>
<input type="text" name="firstName" value="Joe">
</div>
<div class="form-group">
<label>Last Name</label>
<input type="text" name="lastName" value="Blow">
</div>
<div class="form-group">
<label>Email Address</label>
<input type="email" name="email" value="joe@blow.com">
</div>
<button class="btn">提交</button>
<button class="btn" hx-get="/contact/1">取消</button>
</form>
如果你看Listing 1中的標記,很容易看出發(fā)生了什么:hx-swap屬性為編輯前的 div 提供HTML,outerHTML告訴框架它如何與內(nèi)部的動態(tài)內(nèi)容相關??删庉嫲姹咀鳛橐粋€表單元素到達,其中包含x-put屬性,該屬性標識PUT HTML方法和要使用的端點。
問題變成,HTMX如何實現(xiàn)這種“交換”和后續(xù)的PUT,而不做任何JavaScript呢?答案很簡單:它使用服務器端渲染的HTML作為編輯標記,并將表單封裝抽象到框架中。JavaScript 仍然在幕后工作。實際上,我們得到了一個更細粒度的 HTML 語法,它只能加載片段而不是整個頁面,并且可以提交Ajax請求。
這是DRY原則在行動中的一個有趣的例子。即使使用像React這樣的東西,從一個表單到另一個表單也有一定數(shù)量的模板代碼。當然,HTMX并沒有完全消除這一點,但它已經(jīng)將工作轉(zhuǎn)移到了服務器上。
HTMX的服務器端
現(xiàn)在,讓我們考慮等式的服務器端。有許多使用HTMX的服務器端技術(shù)的例子,因為,正如Gross所說,HTMX是“后端不可知的。它不關心你使用什么后端,只要它產(chǎn)生HTML?!?為了了解它是如何工作的,讓我們看一個使用Express和Pug HTML模板引擎的TODO示例。這個例子是經(jīng)典TODO應用程序的實現(xiàn)。
首先,現(xiàn)有的待辦事項從Express輸出,命令如下:
res.render('index', { todos: filteredTodos, filter, itemsLeft: getItemsLeft() });
此命令使用內(nèi)存中的待辦事項集合,并使用一個Pug模板渲染它們,該模板是典型的格式,但它包括驅(qū)動HTMX交互的特殊hx-屬性。例如,用于POST新待辦事項的表單顯示在Listing 2中。
Listing 2. 具有HTMX屬性的表單POST
form(hx-post="/todos", hx-target="#todo-list", hx-swap="afterbegin", _="on htmx:afterOnLoad set #txtTodo.value to ''")
input#txtTodo.new-todo(name="todo",placeholder='需要做什么?', autofocus='')
你可以在這里看到 afterbegin 屬性如何工作,將新內(nèi)容放在列表中的正確位置。on htmx腳本是 Hyperscript 的一個例子,這是一種簡化的腳本語言。它經(jīng)常與HTMX一起使用,但并不嚴格屬于HTMX或需要使用它。實際上,on htmx在這里用于處理在創(chuàng)建新的待辦事項后設置輸入表單的值。
作為另一個例子,Listing 3顯示了待辦事項編輯的Pug模板。
Listing 3. 在PUG中編輯服務器端模板
form(hx-post="/todos/update/" + todo.id)
input.edit(type="text", name="name",value=todo.name)
在Listing 3中,標記使用hx-post屬性來指示發(fā)送已編輯待辦事項的JSON的位置。從這些例子中得到的結(jié)論是我之前提到的:服務器負責提供HTML(帶有HTMX標簽)的適當大小的塊,以填充前端為其各種交互所需的屏幕的不同部分。HTMX客戶端將根據(jù)屬性將它們放在它們應該在的位置,并處理發(fā)送由服務消費的適當數(shù)據(jù)。
負責接收數(shù)據(jù)的端點可以像典型的端點一樣操作,區(qū)別在于響應應該是必要的HTMX。例如,在Listing 4中,你可以看到Express服務器如何處理POST以創(chuàng)建新的待辦事項。
Listing 4. 處理待辦事項創(chuàng)建
app.post('/todos', (req, res) => {
const { todo } = req.body;
const newTodo = {
id: uuid(),
name: todo,
done: false
};
todos.push(newTodo);
let template = pug.compileFile('views/includes/todo-item.pug');
let markup = template({ todo: newTodo});
template = pug.compileFile('views/includes/item-count.pug');
markup += template({ itemsLeft: getItemsLeft()});
res.send(markup);
});
Listing 4 是一個典型的POST body處理器,它從表單數(shù)據(jù)中取出值并使用它創(chuàng)建一個新的業(yè)務對象(newTodo)。然后,它使用這些值填充Pug模板并將其發(fā)送回客戶端,用作前端的Todo列表中的插入。
其他服務器端技術(shù)的例子包括使用HTMX與Java世界中的Spring Boot和Thymeleaf以及Python世界中的Spring Boot和Django。
使用HTMX的客戶端模板
HTMX支持的這種模式的一個變種是使用客戶端模板。這是一個在客戶端運行的層,接受來自服務器的JSON,并在那里進行標記轉(zhuǎn)換。當我問Gross關于使用帶有 JSON的 RESTful 服務時,他指出這是可能的,但前提是REST通常被誤解。
一個相反的問題是,我們?nèi)绾蜗蚍掌魈峤籎SON,而不是默認的表單編碼?再次,有一個擴展可以做到這一點;即,JSON-ENC。
結(jié)論
考慮HTMX會導致一堆想法同時到來。結(jié)論是這個概念和這個項目本身一樣有益。作為一個成熟的項目的HTMX可能最終不會像今天這樣工作,但它已經(jīng)證明是一個有益的影響。最吸引人的是處理所有這些非常常見的Ajax風格的請求的想法,這通常意味著使用fetch()或類似的東西,只用一個HTML屬性。這只是更簡單、更干凈,并且將一切都保持在一個地方。很明顯標記做了什么。
我對服務器端標記生成持更加矛盾的態(tài)度。開發(fā)者習慣于為此目的處理JSON;引入標記只是在客戶端創(chuàng)建中增加了一個步驟。我們已經(jīng)看到了許多服務器端方法,它們總是似乎模糊了HTML、JavaScript和CSS的強大組合,這三者最終總是勝出。也許這次會不同。這是一個大的擺動。
當然,還有客戶端模板選項,它使服務器成為一個熟悉的JSON發(fā)射器。我試圖想象它在一個大型軟件項目中是如何工作的。它會減少大規(guī)模項目中的總體復雜性嗎?
Gross對復雜性有自己的想法。你可以看到他的想法在HTMX的設計中得到體現(xiàn)。這項技術(shù)希望通過將我們帶回到Hypertext作為web應用程序的狀態(tài)機制來簡化事情。這個例子顯示了這個想法的運作。使用JSON作為協(xié)議意味著使客戶端更加智能、更加復雜,并使架構(gòu)變得不那么自描述。
也許它都可以工作。如果我們避免了固有的復雜性,擴展了底層語言HTML,實際上處理現(xiàn)代需求,比如Ajax,我們可以回到一個更簡單的時代。標記將再次成為中心數(shù)據(jù)描述符,并足以描述UI以及線上的數(shù)據(jù)。