likes
comments
collection
share

HTMX入门即学完

作者站长头像
站长
· 阅读数 10

翻译全文后,感觉这是给后端提供的前端逻辑处理能力,且是在交互能力一般的页面中运用,重交互的页面使用这个方式估计会炸。也不推荐前端开发学习这个,因为核心思想主打不使用js,回归html来实现很多交互能力,因此很多能力写起来很反常规前端开发思路,且复用性和灵活性不高,而且不想记住这么多api😄

简介

在这篇文章中,我们将探索HTMX是什么?它拥有什么样的能力?

HTMX是一个体积小(压缩最小至14k)且无依赖的js库,它能通过简洁强大的超文本(模版)能力创建顶尖的用户界面。通过属性就可以直接访问AJAX,CSS动画,WebSockets和服务发送事件。因为提供了在模版层面就能实现交互能力,HTMX已经在改变开发者的编码习惯。

安装HTMX

3种方式安装HTMX:

1、CDN: 顾名思义,在你的HTML文件头部引入CDN地址即可。

<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标签引用即可。

<script src="/path/to/htmx.min.js"></script>

3、npm包:通过如下的命令来执行安装过程

npm install htmx.org

完成安装后,您需要结合适当的工具来使用(node_modules/htmx.org/dist/htmx.jsnode_modules/htmx.org/dist/htmx.min.js)。例如,您可以使用某些扩展和特定于项目的代码来打包htmx。

Webpack配置:如果你在用Webpack管理你的Js代码,首先,通过包管理工具安装htmx,然后在index.js中引入这个包

import "htmx.org"

如果你要使用全局的htmx变量,你可以按照下面的方法将其注入到window上:

  • 创建一个唯一的JS文件
  • 引入这个文件到index.js
import "path/to/my_custom.js"
  • 在文件中添加如下的代码
window.htmx = require("htmx.org")
  • 最后重新构建应用(让配置生效)

Ajax请求

HTMX提供了一些自定义属性允许你在HTML中直接发送Ajax请求:

AttributeDescription
hx-post对于提供的Url发送post请求
hx-get对于提供的Url发送get请求
hx-put对于提供的Url发送put请求
hx-patch对于提供的Url发送patch请求
hx-delete对于提供的Url发送delete请求

使用案例:

<div hx-post="/messages">Put To Messages</div>

上述代码通过hx-post属性触发了一个post请求,请求url为:"/messages"

在HTMX中,元素的自然事件启动AJAX请求。例如,onchange事件触发input, selecttextarea;onsubmit事件触发表单;onclick事件触发了其他一切。

在HTMX提供了一个独一无二的的属性hx-trigger来解决改变事件并触发请求的问题:

<div hx-post="http://localhost:3000/submit" hx-trigger="mouseenter">
  Submit
</div>

上述代码中,当且仅当用户鼠标hover在元素之上的时候触发请求指定的URL。

Trigger修饰符

触发的行为同样可能会被其他的修饰符更改。假设你想要限制请求只发生一次,对trigger使用Once修饰符。

<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,事件就会被触发,否则则不会触发。

<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之间浮动的数字来定义相交的程度,进而决定是否需要发送请求

如果你还有更高级的使用case,你可以使用自定义事件来触发事件。

轮询

你可以在hx-trigger属性中使用every语法 + 时间范围来让一个元素轮询一个指定的URL:

<div hx-get="/get-star-wars-data" hx-trigger="every 2s"></div>

加载轮询

"加载轮询"是HTMX中额外的一种轮询的方式,它包括了一个声明了load triggerdelay的元素,同时在触发后的结果会取代元素本身。

<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,进而展现指示符。

<button hx-get="/click">
    Click Me!
    <img class="htmx-indicator" src="/spinner.gif">
</button>

使用hx-indicator属性和CSS选择器,你可以添加htmx-request类到一个你想要指定的元素上:

<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选择器)将响应的内容放在一个元素里面而不是触发了请求的元素中。

<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的例子:

<div hx-get="/example" hx-swap="afterend">
  Get Some HTML & Append It
</div>

在上述的代码中,div发起了一个请求并且在div之后添加了返回的内容。

同步

当你想在两个元素中发请求,并且希望请求存在先后顺序,HTMX提供的hx-sync特性或许可以帮到你。

例如,让我们来提供这样一个场景,有两个请求,一个为表单提交,另一个则是input的验证请求,如下所示:

<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的请求。这就自动地解决了同步限制的问题。

<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事件:

<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动画,接下来通过一个案例说明:

<div id="div1">The Empire</div>

发送了一个请求并且使用下面的内容来替换现在的元素:

<div id="div1" class="red">
  The Republic
</div>

上述代码中,red类已经被添加到元素中。我们可以事先定义好CSS动画,像这样:

.red {
  color: red;
  transition: all ease-in 1s;
}

HTMX是如何处理这个动画的呢?

在新内容添加到页面之前,所有匹配新内容的id元素都会被找到,这发生在服务端返回;在交换之前,旧节点的的所有属性都会被复制到新节点上;最后新节点替换成功,同时保留了旧节点的所有属性。新属性在一个设定好的时间(默认为20ms)后切换。

替换

响应html的hx-swap-oob属性能够通过id属性直接将返回内容替换到DOM中:

<div id="message" hx-swap-oob="true">Dark Sidious</div>
Anakin Skywalker

上述代码中,额外的内容通过一种特定的方式替换到目标节点中,但是在这个返回中,div#message将会直接被替换成相应地DOM元素。

变量

你可以通过hx-params属性过滤通过Ajax调用的提交参数。该属性有这些可能的值:

  • “*” - 默认全部的变量
  • none - 不包含任何变量
  • <param-list>且不包含任何变量,即所有不需要的参数列表 (这里有待验证,不清楚这个跟下面是具体case是什么样的)
  • <param-list>,须臾通过逗号分隔所有需要的参数列表,逗号分隔
<div hx-get="/get-starwars-characters" hx-params="*">
  Luke Skywalker
</div>

上述例子中,在这个div中,Post请求包含的所有变量都会被保留,然而这里定义成了一个GET请求,所有变量都会被encode且挂在URL上面。

确认请求

HTMX提供的hx-confirm属性通过直接使用js的对话框来帮你来确认是否开始执行一个事件:

<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连接:

<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事件语法激活,一个简单的例子如下:

<body hx-sse="connect:/darth-events">
  <div hx-trigger="sse:new_news" hx-get="/news"></div>
</body>

History模式的支持

hx-push-url属性实现了浏览器的history功能,案例如下:

<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的时候执行,表明有非法的input
  • htmx:validation:halted - 由于验证错误导致请求没有发出的时候执行,特定的错误会在event.detail.errors对象中找到
<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完成。下面就是例子:

<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属性用来声明插件

来个例子:

/ 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事件:

document.body.addEventListener("htmx:load", function (evt) {
  myJavascriptLib.init(evt.detail.elt);
});

或者通过HTMX helpers进行注册:

htmx.on("htmx:load", function (evt) {
  myJavascriptLib.init(evt.detail.elt);
});

上述的方法都是注册htmx:load的事件。该事件在HTMX加载元素到DOM的时候进行执行,它在功能上与传统的加载事件相同。

你还可以通过htmx:load来初始化三方库:

htmx.onLoad(function (target) {
  myJavascriptLib.init(target);
});

在一个事件里面配置请求的案例如下:

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的交换行为。

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变量可以进行日志处理,下面是设置日志的案例:

htmx.logger = function (elt, event, data) {
  if (console) {
    console.log(event, elt, data);
  }
};

结论

本文中我们通览了HTMX库,你可以在不使用js的情况下通过HTMX实现AJAX的功能,允许你轻松地动态构建应用。HTMX不仅仅是一个新库,它可能是一种变革的方式——在web开发中实现交互能力。

原文地址:refine.dev/blog/what-i…