翻译全文后,感觉这是给后端提供的前端逻辑处理能力,且是在交互能力一般的页面中运用,重交互的页面使用这个方式估计会炸。也不推荐前端开发学习这个,因为核心思想主打不使用js,回归html来实现很多交互能力,因此很多能力写起来很反常规前端开发思路,且复用性和灵活性不高,而且不想记住这么多api😄
简介
在这篇文章中,我们将探索HTMX
是什么?它拥有什么样的能力?
HTMX是一个体积小(压缩最小至14k)且无依赖的js库,它能通过简洁强大的超文本(模版)能力创建顶尖的用户界面。通过属性就可以直接访问AJAX,CSS动画,WebSockets和服务发送事件。因为提供了在模版层面就能实现交互能力,HTMX已经在改变开发者的编码习惯。
安装HTMX
3种方式安装HTMX:
1、CDN: 顾名思义,在你的HTML文件头部引入CDN地址即可。
html
<script
src="https://unpkg.com/htmx.org@1.9.6"
integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni"
crossorigin="anonymous"
></script>
不建议在生产环境中使用CDNs。
2、本地引用:将HTMX源码拷贝一份在本地,然后本地引用
从unpkg.com中下载htmx.min.js
,并把它放在工程合适的位置上,通过script标签引用即可。
html
<script src="/path/to/htmx.min.js"></script>
3、npm包:通过如下的命令来执行安装过程
shell
npm install htmx.org
完成安装后,您需要使用适当的工具来使用(node_modules/htmx.org/dist/htmx.js
或node_modules/htmx.org/dist/htmx.min.js
)。例如,您可以使用某些扩展和特定于项目的代码来打包htmx。?????????
Webpack配置:如果你在用Webpack管理你的Js代码,首先,通过包管理工具安装htmx,然后在index.js
中引入这个包
js
import "htmx.org"
如果你要使用全局的htmx变量,你可以按照下面的方法将其注入到window上:
- 创建一个唯一的JS文件
- 引入这个文件到
index.js
中
js
import "path/to/my_custom.js"
- 在文件中添加如下的代码
js
window.htmx = require("htmx.org")
- 最后重新构建应用(让配置生效)
Ajax请求
HTMX提供了一些自定义属性允许你在HTML中直接发送Ajax请求:
Attribute | Description |
---|---|
hx-post | 对于提供的Url发送post请求 |
hx-get | 对于提供的Url发送get请求 |
hx-put | 对于提供的Url发送put请求 |
hx-patch | 对于提供的Url发送patch请求 |
hx-delete | 对于提供的Url发送delete请求 |
使用案例:
html
<div hx-post="/messages">Put To Messages</div>
上述代码通过hx-post
属性触发了一个post请求,请求url为:"/messages"
在HTMX中,元素的自然事件启动AJAX请求。例如,onchange事件触发input
, select
和textarea
;onsubmit事件触发表单
;onclick事件触发了其他一切。
在HTMX提供了一个独一无二的的属性hx-trigger
来解决改变事件并触发请求的问题:
html
<div hx-post="http://localhost:3000/submit" hx-trigger="mouseenter">
Submit
</div>
上述代码中,当且仅当用户鼠标hover在元素之上的时候触发请求指定的URL。
Trigger修饰符
触发的行为同样可能会被其他的修饰符更改。假设你想要限制请求只发生一次,对trigger使用Once
修饰符。
html
<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
Submit
</div>
还有其他的修饰符:
- changed - 当且仅当元素的值发生变化的时候进行请求
- delay:
<time interval>
- 在正式请求之前声明一个延迟的时间,当再次请求的时候会重新延迟 - throttle:
<time interval>
- 请求之前的一段延迟,跟delay不同的是,在当前延迟未完的时间段里的请求都会被取消,直到当前延迟完成以后才会触发新请求,也就是防抖 - from:
<CSS Selector>
- 对不同的元素进行事件监听
想要知道更多的HTMX的修饰符,可以看这里
Trigger过滤器
过滤器的运用是通过在事件名称后的方括号里添加一个js表达式来实现,如果表达式的结果是true
,事件就会被触发,否则则不会触发。
html
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">
Click the force
</div>
htmx有一些特定的事件可以和hx-trigger
结合使用:
- load - 当元素初次被加载后就被触发
- revealed - 当元素滚动到视图窗口的时候触发
- intersect - 当元素与视图初次相交的时候触发,这里还有两个额外的选项:
- root:
<selector>
- 基于选择器相交的根元素 - threshold:
<float>
- 一个在0.0到1.0之间浮动的数字来定义相交的程度,进而决定是否需要发送请求
- root:
如果你还有更高级的使用case,你可以使用自定义事件来触发事件。
轮询
你可以在hx-trigger
属性中使用every
语法 + 时间范围来让一个元素轮询一个指定的URL:
html
<div hx-get="/get-star-wars-data" hx-trigger="every 2s"></div>
加载轮询
"加载轮询"是HTMX中额外的一种轮询的方式,它包括了一个声明了load trigger
和delay
的元素,同时在触发后的结果会取代元素本身。
html
<div hx-get="/get-star-wars-data" hx-trigger="load delay:1s" hx-swap="starWarsDataHTML"></div>
请求指示符
因为浏览器在发出AJAX请求时不会提供反馈,所以通知用户发生了任何事情通常都是有意义的。现在你可以通过HTMX的html-indicator
类来处理这类问题。
任何使用了html-indicator
类的元素的opacity属性默认都是0,也就是说纵使该元素在DOM中展现了,但是是不可见的。
当HTMX触发了一个请求,htmx-request
类会被添加到当前元素上,当一个含有html-indicator
类的子元素暴露给了html-request
类,它会转化opacity为1,进而展现指示符。
html
<button hx-get="/click">
Click Me!
<img class="htmx-indicator" src="/spinner.gif">
</button>
使用hx-indicator
属性和CSS选择器,你可以添加htmx-request
类到一个你想要指定的元素上:
html
<div>
<button hx-get="/click" hx-indicator="#indicator">
Click the force!
</button>
<img id="indicator" class="htmx-indicator" src="/spinner.gif" />
</div>
结果id
你可以通过hx-target
属性(接受CSS选择器)将响应的内容放在一个元素里面而不是触发了请求的元素中。
html
<input type="text" name="star-wars-input"
hx-get="/star-wars-characters"
hx-trigger="keyup delay:500ms changed"
hx-target="#search-results"
placeholder="Search..."
>
<div id="search-results"></div>
上述例子中,hx-target
属性指定了id为#search-result
,并且当请求成功以后将请求的结果放在含有这个id的div里面。
置换
你可以在HTMX中交换HTML的内容并返回到DOM中,目标元素的innerHTML会自动地被内容替换。而这些能力只需要你在使用hx-swap
属性并配合下面的值:
名称 | 描述 |
---|---|
innerHTML | 默认将内容插入到目标元素内 |
outerHTML | 内容替换当前的目标元素 |
afterbegin | 当前元素的第一个子元素之前添加内容 |
beforebegin | 当前元素之前添加内容 |
beforeend | 在当前元素的最后一个子元素的后添加内容 |
afterend | 在当前元素之后添加内容 |
delete | 删除目标元素而不考虑响应 |
none | 不处理返回的内容 |
一个关于hx-swap
的例子:
html
<div hx-get="/example" hx-swap="afterend">
Get Some HTML & Append It
</div>
在上述的代码中,div
发起了一个请求并且在div
之后添加了返回的内容。
同步
当你想在两个元素中发请求,并且希望请求存在先后顺序,HTMX提供的hx-sync
特性或许可以帮到你。
例如,让我们来提供这样一个场景,有两个请求,一个为表单提交,另一个则是input的验证请求,如下所示:
html
<form hx-post="/submit-character">
<input id="title" name="title" type="text"
hx-post="/validate-character"
hx-trigger="change"
>
<button type="submit">Submit</button>
</form>
当input输入完成且表单立即进行提交,在没有使用hx-sync
的情况下,两个请求会并发请求。
当对input运用hx-sync="closest form:abort"
,form的请求会被监控,当发现了form请求以后input请求会被终止,或者,当input的请求处理完了以后才会开始form的请求。这就自动地解决了同步限制的问题。
html
<form hx-post="/submit-character">
<input id="title" name="title" type="text"
hx-post="/validate-character"
hx-trigger="change"
hx-sync="closest form:abort"
>
<button type="submit">Submit</button>
</form>
另外,htmx提供了编码式的方法来取消请求:为了停止进行中的请求,你可以在htmx.trigger
函数里传递给一个元素一个htmx:abort
事件:
html
<button id="request-button" hx-post="/submit-character">
Submit Character
</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">
Cancel Submission
</button>
CSS动画
HTMX中你可以在不使用js的情况下运行CSS动画,接下来通过一个案例说明:
html
<div id="div1">The Empire</div>
发送了一个请求并且使用下面的内容来替换现在的元素:
bash
<div id="div1" class="red">
The Republic
</div>
上述代码中,red
类已经被添加到元素中。我们可以事先定义好CSS动画,像这样:
css
.red {
color: red;
transition: all ease-in 1s;
}
HTMX是如何处理这个动画的呢?
在新内容添加到页面之前,所有匹配新内容的
id
元素都会被找到,这发生在服务端返回;在交换之前,旧节点的的所有属性都会被复制到新节点上;最后新节点替换成功,同时保留了旧节点的所有属性。新属性在一个设定好的时间(默认为20ms)后切换。
???替换
响应html的hx-swap-oob
属性能够通过id
属性直接将返回内容替换到DOM中:
html
<div id="message" hx-swap-oob="true">Dark Sidious</div>
Anakin Skywalker
上述代码中,额外的内容通过一种特定的方式替换到目标节点中,但是在这个返回中,div#message
将会直接被替换成相应地DOM元素。
变量
你可以通过hx-params
属性过滤通过Ajax调用的提交参数。该属性有这些可能的值:
- "*" - 默认全部的变量
- none - 不包含任何变量
<param-list>
且不包含任何变量,即所有不需要的参数列表 (这里有待验证,不清楚这个跟下面是具体case是什么样的)<param-list>
,须臾通过逗号分隔所有需要的参数列表,逗号分隔
html
<div hx-get="/get-starwars-characters" hx-params="*">
Luke Skywalker
</div>
上述例子中,在这个div中,Post
请求包含的所有变量都会被保留,然而这里定义成了一个GET
请求,所有变量都会被encode且挂在URL上面。
确认请求
HTMX提供的hx-confirm
属性通过直接使用js的对话框来帮你来确认是否开始执行一个事件:
html
<button hx-delete="/delete-character" hx-confirm="Are you sure you wish to delete Obiwan Kenobi">
Delete Obiwan Kenobi
</button>
Web Sockets & SSE
HTMX现在实验性地支持了WebSockets和Server Send Events。然而,你可以通过hx-ws
属性来建立一个Websocket连接:
html
<div hx-ws="connect:wss:/darth-chatroom">
<div id="chat_room">
...
</div>
<form hx-ws="send:submit">
<input name="chat_message">
</form>
</div>
SSE(Server Send Events)可以实现向浏览器发送事件,在父节点声明一个hx-see
的属性,并且添加一个connect <url>
的值即可。
接着,在该节点的子节点中声明一个hx-trigger="see:<event_name>"
,该属性最终会被server-sent事件语法激活,一个简单的例子如下:
html
<body hx-sse="connect:/darth-events">
<div hx-trigger="sse:new_news" hx-get="/news"></div>
</body>
History模式的支持
hx-push-url
属性实现了浏览器的history功能,案例如下:
html
<a hx-get="/star-wars-characters" hx-push-url="true">
Blog
</a>
hx-push-url的策略是:开始请求/star-wars-characters
之前,HTMX会对当前的文档进行快照,在用户点击该链接的时候进行存储。在那之后,一个新的location会被添加到history栈里面,替换也就完成了。
HTMX会在用户点击返回按钮的时候从存储栈里检索来模拟"返回"的能力,当检索不到的时候会发送一个特定的请求,该请求会带上HX-History-Restore-call:true
的headers,并且会返回一个全屏的HTML内容作为兜底。与此同时,如果htmx.config.refreshOnHistoryMiss:true
,那么浏览器会强制刷新。
HTMX的验证功能
Htmx使用的是HTML5的验证API,因此一个不合格的验证输入框会阻止form请求的生成,这在WebSocket发送和AJAX请求中均有效。
Htmx中具体的验证方式是这样的:
htmx:validation:validate
- 在元素的checkValidity()的方法调用前执行,通常在自定义验证逻辑中使用htmx:validation:failed
- 在checkValidity()执行返回false的时候执行,表明有非法的inputhtmx:validation:halted
- 由于验证错误导致请求没有发出的时候执行,特定的错误会在event.detail.errors对象中找到
html
<form hx-post="/create-characters">
<input _="on htmx:validation:validate
if my.value != 'foo'
call me.setCustomValidity('Please enter the value foo')
else
call me.setCustomValidity('')"
name="example"
>
</form>
上述代码即为input使用htmx:validation的案例,代码中,超脚本用来验证事件进而来保证input有foo这个值。
HTMX的动画
HTMX允许你在只使用CSS和HTML的情况下添加丝滑的动画和变形能力到你的web页面中。你可以使用新的View Transitions API来创建动画。最基础的例子就是Fade Out On Swap ,如果你想在请求结束以后移除的元素拥有淡出的效果,使用htmx-swapping
类加一些CSS来延长swap阶段直到animation完成。下面就是例子:
html
<style>
.fade-me-out.htmx-swapping {
opacity: 0;
transition: opacity 1s ease-out;
}
</style>
<button class="fade-me-out"
hx-delete="/fade_out_demo"
hx-swap="outerHTML swap:1s">
Fade Me Out
</button>
更多地动画看这里
HTMX中的扩展(插件)
HTMX中包含了一个扩展框架------基于htmx-based应用即可进行扩展的创建和发布。hx-ext
属性就是用来使用这些扩展插件的。
插件的使用包含两步:
- 引入插件的定义,这会将在HTMX插件中注册
- hx-ext属性用来声明插件
来个例子:
html
/ Including the extension library
<script src="/path/to/ext/debug.js" defer></script>
// add the extension(debug) to the hx-ext attribute
<button hx-post="/example" hx-ext="debug">
This Button Uses The Force
</button>
HTMX附带了一个插件的集合,这些插件基本涵盖了基本的开发需求。在每个版本中,这些扩展都针对htmx进行测试。你可以通过这里查看完整的插件列表。
日志与事件
事件
HTMX提供了健壮的事件机制,该机制同样也会用作日志系统。你可以通过The event listener approach轻松地注册HTMX事件:
js
document.body.addEventListener("htmx:load", function (evt) {
myJavascriptLib.init(evt.detail.elt);
});
或者通过HTMX helpers进行注册:
js
htmx.on("htmx:load", function (evt) {
myJavascriptLib.init(evt.detail.elt);
});
上述的方法都是注册htmx:load
的事件。该事件在HTMX加载元素到DOM的时候进行执行,它在功能上与传统的加载事件相同。
你还可以通过htmx:load
来初始化三方库:
js
htmx.onLoad(function (target) {
myJavascriptLib.init(target);
});
在一个事件里面配置请求的案例如下:
js
document.body.addEventListener("htmx:configRequest", function (evt) {
evt.detail.parameters["auth_token"] = getAuthToken(); // add a new parameter into the request
evt.detail.headers["Authentication-Token"] = getAuthToken(); // add a new header into the request
});
上述代码则是通过htmx:configRequest
事件来实现请求发送之前修改AJAX的配置。
htmx:beforeSwap
事件则允许你调整HTMX的交换行为。
js
document.body.addEventListener("htmx:beforeSwap", function (evt) {
if (evt.detail.xhr.status === 404) {
// alert the user when a 404 occurs (maybe use a nicer mechanism than alert())
alert("Error: Could Not Find Resource");
} else if (evt.detail.xhr.status === 422) {
// allow 422 responses to swap as we are using this as a signal that
// a form was submitted with bad data and want to rerender with the
// errors
//
// set isError to false to avoid error logging in console
evt.detail.shouldSwap = true;
evt.detail.isError = false;
} else if (evt.detail.xhr.status === 418) {
// if the response code 418 (I'm a teapot) is returned, retarget the
// content of the response to the element with the id `teapot`
evt.detail.shouldSwap = true;
evt.detail.target = htmx.find("#teapot");
}
});
在上面的代码中,我们处理了一些400级错误响应代码,这些代码通常不会导致htmx中的交换。
HTMX中的事件列表,请查看这里。
日志
htmx.logger
变量可以进行日志处理,下面是设置日志的案例:
js
htmx.logger = function (elt, event, data) {
if (console) {
console.log(event, elt, data);
}
};
结论
本文中我们通览了HTMX库,你可以在不使用js的情况下通过HTMX实现AJAX的功能,允许你轻松地动态构建应用。HTMX不仅仅是一个新库,它可能是一种变革的方式------在web开发中实现交互能力。