htmx:后端主導(dǎo)的前端框架是啥樣的?
大家好,我卡頌。
前端領(lǐng)域這幾年涌現(xiàn)了很多新興的前端框架,比如Qwik、Svelte、Astro等。
這些框架多以「前端工程師」作為受眾。
那么,以「后端工程師」作為受眾的前端框架是啥樣的,他與前者有什么區(qū)別呢?
介紹htmx
htmx是一款在Django技術(shù)棧最近比較熱門(mén)的前端框架。
他的理念是 —— 「讓網(wǎng)頁(yè)回歸HTML的本質(zhì),不再受JS束縛」。是不是很有web1.0的風(fēng)格?
他是怎么做到的呢?答案是:通過(guò)大量預(yù)制的自定義HTML屬性。
當(dāng)你在頁(yè)面中引入htmx.org.js后,可以在HTML中書(shū)寫(xiě)以hx-開(kāi)頭的自定義屬性。比如下面的代碼:
<button
hx-post="/data"
hx-trigger="click"
>
點(diǎn)我請(qǐng)求data
</button>
點(diǎn)擊按鈕(hx-trigger指定的click事件)后,會(huì)向data接口(hx-post指定)發(fā)起post請(qǐng)求。
那請(qǐng)求返回的數(shù)據(jù)如何顯示呢?我們?cè)僭黾?個(gè)自定義屬性:
<button
hx-post="/data"
hx-trigger="click"
hx-target="#parent-div"
hx-swap="outerHTML"
>
點(diǎn)我請(qǐng)求data
</button>
hx-target指代「返回的HTML結(jié)構(gòu)」會(huì)被注入到哪里。這里會(huì)被注入到「id為parent-div的DOM」中。
hx-swap指代「返回的HTML結(jié)構(gòu)會(huì)以什么形式注入」。這里會(huì)直接替換「id為parent-div的DOM」。
如果hx-swap="innerHTML",則代表會(huì)以「id為parent-div的DOM」的innerHTML形式注入。
如果要表達(dá)復(fù)雜的邏輯,需要結(jié)合很多自定義屬性與屬性值,比如下面的代碼:
<input
type="text"
hx-get="/trigger_delay"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
placeholder="Search..."
>
當(dāng)input觸發(fā)keyup事件且值改變后,延遲500ms,向trigger_delay接口發(fā)起請(qǐng)求,返回的HTML結(jié)構(gòu)被注入到「id為search-results的DOM」中。
與其說(shuō)htmx是一款前端框架,更貼切的說(shuō),他應(yīng)該是一款「HTML自定義屬性工具庫(kù)」。
他將很多常見(jiàn)JS交互邏輯收斂到自定義HTML屬性中,借此減少JS代碼量。
現(xiàn)代前端框架通常是「狀態(tài)驅(qū)動(dòng)UI」,而htmx的理念是「過(guò)程驅(qū)動(dòng)UI」(類(lèi)似jQuery時(shí)代編寫(xiě)頁(yè)面的方式)。
如果希望引入狀態(tài),需要以插件的形式引入alpine-morph。
相比于:
React:基于JSX
Vue:基于模版語(yǔ)法
alpine是一款基于HTML的前端框架。
這意味著使用alpine需要直接在HTML中以自定義屬性的形式書(shū)寫(xiě)狀態(tài)(與Vue v1類(lèi)似)。所以,他能很好的融入htmx的體系中。
比如下面這段代碼是段結(jié)合htmx與alpine的HTML,其中以hx-開(kāi)頭的是htmx屬性,以x-開(kāi)頭的是alphine屬性:
<div hx-target="this" hx-ext="alpine-morph" hx-swap="morph">
<div x-data="{ count: 0, replaced: false,
message: 'Change me, then press the button!' }">
<input type="text" x-model="message">
<div x-text="count"></div>
<button x-bind:style="replaced && {'backgroundColor': '#fecaca'}"
x-on:click="replaced = true; count++"
hx-get="/swap">
Morph
</button>
</div>
</div>
這段代碼包含了交互邏輯與前端狀態(tài),最重要的是:他是合法的HTML(而不是JSX或模版語(yǔ)法這樣的DSL),這意味著他能輕松的在前后端之間傳遞,并在前端展示。
交互邏輯守恒
本質(zhì)來(lái)說(shuō),網(wǎng)頁(yè)的最終消費(fèi)品是HTML與CSS。開(kāi)發(fā)者編寫(xiě)交互邏輯改變HTML與CSS。
前端工程師習(xí)慣在網(wǎng)頁(yè)中通過(guò)JS編寫(xiě)交互邏輯。
后端工程師習(xí)慣在后端編寫(xiě)交互邏輯。比如在htmx中,請(qǐng)求返回的是HTML結(jié)構(gòu),這部分「生成HTML的邏輯」是在后端controller中實(shí)現(xiàn)的(而不是在前端通過(guò)JS生成)。
除此之外,還有一部分交互邏輯是在后端「HTML模版」中產(chǎn)生的。下圖是Django中結(jié)合htmx的后端模版代碼示例:
不管交互邏輯在前端還是后端實(shí)現(xiàn),也不管用哪種語(yǔ)言實(shí)現(xiàn),他是一定需要實(shí)現(xiàn)的,也就是說(shuō)「交互邏輯守恒」。
但是,交互邏輯在前端還是后端實(shí)現(xiàn),對(duì)頁(yè)面帶來(lái)的影響是不同的。
對(duì)頁(yè)面性能的影響
交互邏輯在前端實(shí)現(xiàn)的越多,意味著「越多的JS代碼」,如果這部分代碼是首屏渲染所需的,那意味著更差的FCP[1]指標(biāo)。
如果這部分代碼是后續(xù)交互所需的,那意味著更差的TTI[2]指標(biāo)。
為了減少前端JS資源對(duì)性能的影響,前端框架都在逐步向后迭代,比如Next.js之于React,Nuxt.js之于Vue。
新興框架中的Astro、Qwik等也是類(lèi)似思路。
而本文聊的htmx作為后端主導(dǎo)的前端框架,本身的立足點(diǎn)就是后端的view層,所以天生就是頁(yè)面性能友好的。
總結(jié)
根據(jù)「交互邏輯守恒」,交互邏輯一定需要實(shí)現(xiàn),不是在前端就是在后端。
傳統(tǒng)來(lái)說(shuō),前端框架將交互放在前端,這會(huì)造成JS資源變大,影響性能。
單純從功能來(lái)講,htmx僅僅是個(gè)「HTML自定義屬性工具庫(kù)」,他將一部分交互收斂到自定義屬性中,減少前端交互邏輯。
剩下的交互邏輯放在后端的view(作為頁(yè)面模版),或controller(將HTML作為接口返回值),以此減少前端JS資源的體積。
對(duì)于頁(yè)面交互復(fù)雜度不高,且是后端主導(dǎo)的項(xiàng)目(不想寫(xiě)JS邏輯),相信htmx會(huì)是不錯(cuò)的選擇。
參考資料
[1]
FCP:
https://web.dev/fcp/
[2]
TTI:
https://web.dev/tti/
作者:卡頌
歡迎關(guān)注微信公眾號(hào) :魔術(shù)師卡頌