移除JavaScript:如何使用HTML与Htmx并减少代码量
在这篇文章中,了解htmx以及它如何帮助你重用服务器上的元素,减少JavaScript代码量,并避免构建。
HTML是宇宙的中心
我们今天所知的互联网在很大程度上归功于HTML和CSS。Javascript(JS)可以作为它们之间的粘合剂,使页面更加动态和互动,但网络编程的历史发展却有所不同。在客户端渲染和其他类似技术出现后,使用JS来创建网络应用变得更加困难。
什么是Htmx?
Htmx是一个库,允许你用简单的标记创建现代和强大的用户界面。有了它,AJAX请求、触发CSS转换、调用WebSocket和服务器发送的事件都可以直接从HTML元素中执行。
SSR应用
htmx的使用促使人们逐渐放弃客户端渲染,而选择服务器端渲染。SSR被认为是最后的手段,只有在需要快速提高性能的时候才会使用。然而,服务器端渲染可以构建一个应用程序的整个用户界面。
Htmx的运行不需要任何其他JS包,而且与框架和语言无关。因此,它可以用于任何服务器平台,如Node Express、RAILS、Django、Phoenix、Laravel等。
重用服务器上的组件
Htmx允许你用更熟悉的库重用服务器端的UI元素;例如,Node的Pug或RAILS和Django的模板库。它有助于使HTML变得复杂和动态。
这里是Rentals Listing,一个用Express.js和HTML构建的演示应用程序。它在统计和动态场景中都使用了相同的部分。
HTML
ul.results
each rental in rentals
li
article.rental
button.image(type="button", _="on click toggle .large then if #view-caption.textContent === 'View Larger' then set #view-caption.textContent to 'View Smaller' else set #view-caption.textContent to 'View Larger'")
img(src=rental.attributes.image, alt='An image of ' + rental.attributes.title)
small#view-caption View Larger
.details
h3
a(href='/rentals/' + rental.id) #{rental.attributes.title}
.detail.owner
span Owner:
| #{rental.attributes.owner}
.detail.type
span Type:
| #{rental.attributes.category}
.detail.location
span Location:
| #{rental.attributes.city}
.detail.bedrooms
span Bedrooms:
| #{rental.attributes.bedrooms}
.map
img(alt='A map of ' + rental.attributes.title, src=rental.mapbox, width="150",height="150")
在主页的列表中,我使用了Pug库中的include来显示局部。
HTML
extends layout
block content
.jumbo
.right
h2 Welcome to Super Rentals!
p We hope you find exactly what you're looking for in a place to stay.
a.button(href="/about") About Us
.rentals
label
span Where would you like to stay?
input.light(type="text", name="search",
hx-post="/search" ,
hx-trigger="keyup changed delay:500ms" ,
hx-target=".results" ,
hx-indicator=".htmx-indicator")
include includes/rental-list.pug
每次用户在网站上搜索租房时,我都使用同一个局部来填充搜索结果。结果看起来是这样的。
JavaScript
app.post('/search', (req, res) => {
const { search } = req.body;
const results = _rentals.data.filter(r => {
const _search = search.toLowerCase();
const _title = r.attributes.title.toLowerCase();
return _title.includes(_search);
});
const template = pug.compileFile('views/includes/rental-list.pug');
const markup = template({ rentals: results });
res.send(markup);
});
服务器端路由
客户端路由带来了一大堆问题。例如,在基于哈希的路由和基于URL的路由之间总是存在着两难的选择。由于旧的浏览器不支持历史API(如Internet Explorer 11),基于哈希的路由,在URL中使用片段ID,几乎总是首选。
大多数JS框架实现了他们自己的客户端路由逻辑。同时,所有的框架都使用自己的浏览器API,如window.history。这导致应用程序中出现大量的模板代码。
更少的JS代码
在我看来,htmx的主要优势在于我们编写和发送至浏览器的JS代码数量。与hyperscript一起,该库允许你创建丰富的交互式应用程序,而无需使用JS的客户端代码。
HTML
<!-- have a button POST a click via AJAX -->
<button hx-post="/clicked" hx-swap="outerHTML">
Click Me
</button>
当单页应用程序首次开始流行时,社区采用JSON作为数据交换的标准。现在,为了从JSON数据中逆向开发HTML,你往往需要处理大量的客户端数据,这些数据是通过API来自服务器的。API响应往往包含不完整或多余的数据。
为了解决这个问题,已经开发了像GraphQL这样复杂的替代品,由于它,你可以从服务器上只获得你需要的数据。Htmx提供了一个更好的解决方案:你只需要用从服务器收到的HTML响应来替换HTML:不再有客户端数据。
无需构建/编译
htmx的另一个优点是缺乏网络应用程序构建工具。你可以使用CDN工具来代替。
HTML
<!-- Load from unpkg -->
<script src="<https://unpkg.com/htmx.org@1.3.3>"></script>
缺乏构建是网络应用程序开发的一个全球趋势。一方面,ES模块的规范已经被所有的浏览器开发者所接受。另一方面,现在我们有了Skypack、Snowpack和Vite工具,可以与CDN和ESM的方法相结合。所有这些将最终导致这样一个事实:客户端JavaScript的建设将减少。再加上不需要安装成千上万的npm包,也不需要维护复杂的构建配置。
统一代码库
一个应用程序的两个代码库意味着额外的开发挑战。特别是,你需要同步更新部署,两次配置构建管道,更新两个基地的框架,维护你的代码,并从两个来源运行测试包。
Htmx允许你把所有的代码合并到一个地方:因为渲染是在服务器端进行的,所以不需要为界面建立一个单独的基地。从长远来看,这可以为你节省大量的时间和金钱。此外,开发人员可以更一致地行动:他们不必检查两个或更多的存储库。
本地行为原则(LoB)
LoB原则是由编程理论家Richard Gabriel制定的。它指出,所有的开发者都应该努力确保每段代码的行为在被验证时应该是显而易见的。
根据Gabriel的说法,局部性对于保持你的代码易于维护至关重要。定位性是一种特性,它允许程序员在只看到代码的一小部分后就能理解代码属于架构的哪一部分。
它看起来像这样。
HTML
The behaviour of a code unit should be as obvious as possible by looking only at that unit of code
<div hx-get="/clicked">Click Me</div>
LoB经常与软件开发的其他原则相冲突,如关注点分离。当React将HTML和CSS引入到JavaScript时,这种冲突得到了部分解决。同时,React的创建者Pete Hunt认为,这是关于技术的分离。
缺乏与状态同步的问题
客户端状态管理产生的问题比它解决的问题多。这一原则的实施导致了在客户端和服务器端都需要进行状态管理的事实。另一个解决方案是将状态存储在服务器上。在这种情况下,客户端作为一个假的执行者来呈现状态的变化。
这类似于瘦客户机模型,即带有轻量级操作系统的本科计算机,连接到终端服务器上。这样的设备被用来创建第一批节省资源的网络应用。
Htmx将帮助你避免在状态管理网络中混淆UI代码;例如,双向数据绑定、单向数据流和反应式数据。