我们曾多次断言,许多人采用SPA架构开发Web应用程序的主要原因之一是出于美学考虑。
正如我们在《超媒体系统》一书中提到的,在讨论我们开始的Web 1.0风格的联系人管理应用程序时,即使它与SPA版本具有功能对等性,该应用程序也存在严重的美学问题:
从用户体验的角度来看:在应用程序页面之间移动时,或者在创建、更新或删除联系人时,会有明显的刷新。这是因为每个用户交互(链接点击或表单提交)都需要整页刷新,每次操作后都要处理一个全新的HTML文档。
–《超媒体系统》 - 第4章
网页之间这种令人不适的"咔嗒"切换,通常伴随着无样式内容闪烁(FOUC),一直伴随着我们。虽然现代浏览器在一定程度上改善了这种情况(但不幸的是,也让请求进行中的提示不那么明显),但情况仍然很糟糕,特别是与精心制作的SPA所能实现的体验相比。
在Web早期,这还不是什么大问题。我们有星星在浏览器的工具栏中绕着恐龙飞,火焰文字,基于表格的布局,跳舞的婴儿等等,而且我们主要将Web与ftp客户端等进行比较。
当时的标准很低,时代很美好。
唉,Web后来摒弃了这些幼稚的东西,现在我们被期望向用户呈现精美、吸引人的界面,包括从一个视图状态到另一个视图状态的平滑过渡。
再次强调,我们认为这就是许多团队默认采用SPA方法的原因:旧方式似乎……太笨拙了。
早期的Web工程师意识到Web开发者希望在不同视图状态之间提供平滑过渡,并提供了各种技术来实现这一点。其中一个主要技术是CSS转换,它允许你指定从一个状态到另一个状态的数学转换。
对HTML来说不幸的是,CSS转换仅在JavaScript可用:你必须动态更改元素才能触发转换,而"原生"HTML无法做到这一点。在实践中,这意味着只有使用JavaScript构建SPA的酷小孩才能使用这些工具,进一步巩固了SPA的美学优越性。
如你所知,htmx通过复杂的交换模型使CSS转换在纯HTML中可用,我们获取新旧内容中都存在的元素并"沉淀"它们的属性。这是一个巧妙的技巧,可用于让超媒体驱动的应用程序感觉像精心制作的SPA一样顺滑。
然而,现在有了一个新成员:视图转换API
视图转换API比CSS转换更具野心,因为它试图提供一个简单直观的API,用于以普通开发者可以利用的方式将整个DOM从一种状态转换到另一种状态。
此外,这个API不仅应该在JavaScript中可用,还应该可用于HTML中的普通旧链接和表单,从而使得使用Web 1.0方法构建更好的用户界面成为可能。
当这个功能可用时,重温"超媒体系统"中的联系人应用程序将会很有趣!
但截至撰写本文时,该API与CSS转换一样,仅在JavaScript中可用,而且它刚刚在Chrome 111+中发布。
在JavaScript中,该API不能再简单了:
// 只需这样就能实现从一个状态到另一个状态的平滑转换!
document.startViewTransition(() => updateTheDOMSomehow(data));
这正是我喜欢的API。
幸运的是,将这个API包装在常规的htmx交换模型周围很简单,这使我们能够开始在htmx中探索视图转换,即使它在HTML中尚未普遍可用!
而且从htmx 1.9.0开始,你可以通过向hx-swap
属性添加transition:true
属性来开始尝试该API。
那么让我们看看这个与htmx结合的新玩具的简单示例。
这样做将涉及两个部分:
我们需要做的第一件事是定义我们想要的视图转换动画。
:view-transition-old()
和:view-transition-new()
伪选择器定义一个名为slide-it
的视图转换.sample-transition
类绑定到我们刚刚定义的slide-it
视图转换,这样我们就可以通过CSS类名将其绑定到元素(关于视图转换API的更完整细节可以在记录它们的Chrome开发者页面上找到。)
<style>
@keyframes fade-in {
from { opacity: 0; }
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes slide-from-right {
from { transform: translateX(90px); }
}
@keyframes slide-to-left {
to { transform: translateX(-90px); }
}
/* 为旧内容和新内容定义动画 */
::view-transition-old(slide-it) {
animation: 180ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(slide-it) {
animation: 420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* 将视图转换绑定到给定的CSS类 */
.sample-transition {
view-transition-name: slide-it;
}
</style>
这个CSS设置使得带有.sample-transition
类的内容在被移除时会淡出并向左滑动,而新内容会淡入并从右侧滑入。
通过CSS定义了视图转换后,下一步是将这个视图转换绑定到htmx将变动的实际元素,并指定htmx应该利用视图转换API:
<div class="sample-transition">
<h1>初始内容</h1>
<button hx-get="/new-content"
hx-swap="innerHTML transition:true"
hx-target="closest div">
切换内容!
</button>
</div>
这里我们有一个按钮,它发出GET
请求以获取一些新内容,并用响应替换最近div的内部HTML。
那个div上有sample-transition
类,因此上面定义的视图转换将应用于它。
最后,hx-swap
属性包含选项transition:true
,这就是告诉htmx在交换时使用内部视图转换JavaScript API的方式。
将所有内容联系起来后,我们就可以开始在htmx中使用视图转换API了。这是一个演示,应该在Chrome 111+中工作(其他浏览器也可以正常工作,但不会有漂亮的动画):
假设你正在Chrome 111+中查看此页面,你应该看到上面的内容优雅地向左滑出,并被从右侧滑入的新内容取代。漂亮!
嘿,这很整洁,而且一旦你理解了这个概念,工作量也没那么大!这个新API显示出了很大的前景。
视图转换是一项令人兴奋的新技术,我们认为它可以显著拉平超媒体驱动应用与如今更流行的SPA架构之间的竞争环境。
通过消除Web 1.0应用程序难看的"咔嗒"声,SPA方法的美学优势将减弱,我们可以减少围绕"花哨效果"做决策,更多地关注与各种架构相关的实际技术权衡。
我们期待视图转换在原生HTML中可用,但在此之前,你今天就可以在htmx中开始使用它们!