最佳 Alpine.js 替代方案
在本文中,我将向您介绍 Alpine.js 的绝佳替代品,它将帮助您使用服务器端 HTML 完成相同的操作(甚至更多)。
几个月前,我写了一篇有关 HTMX 的类似文章,现在我终于可以写一下使用HMPL而不是Alpine.js可以获得的好处。
我认为这个想法比迄今为止所做的有更大的潜力。
好吧,让我们开始吧!
👀 我们如何比较?
首先,所有比较都将在服务器连接的上下文中进行。我们不会在这里考虑常规客户端功能的选项。尽管一切都在客户端完成,但仍然存在明显的差异。
比较时我们将考虑以下参数:
- 渲染
- 自定义服务器请求
- 磁盘空间
- 模块内部是什么样的
我们还将讨论支持、安装简易性和其他一些要点。
🔧 渲染
在现代 Web 开发中,界面渲染方法的选择至关重要。我们将探讨两种截然不同的方法:HMPL 中的模板编译和 Alpine.js 的声明式方法。这两种技术为用户界面提供了不同的处理范例,每种范例都有各自的优势和实现特性。
HMPL
HMPL 使用客户端模板编译,这意味着标记被转换成动态生成 HTML 的 JavaScript 函数。
const templateFn = hmpl.compile(`
<div>
{{#request
src="/api/my-component"
indicators=[
{
trigger: "pending"
content: "<p>Loading...</p>"
}
]}}
{{/request}}
</div>
`);
// Usage
const component = templateFn();
该模板被编译一次为可执行函数,该函数:
-
创建优化的渲染函数
-
缓存结果以供重复使用
-
为将来的更新准备 DOM 结构
Alpine.js
Alpine.js 提供了一种声明式风格——您可以通过属性( x-data
、、 )直接在 HTML 中描述行为。x-show
x-text
<div x-data="{ user: null, loading: true }"
x-init="fetch('/api/user')
.then(r => r.json())
.then(data => { user = data; loading = false; })">
<template x-if="loading">
<div>Loading...</div>
</template>
<div x-show="user" class="user-card">
<h2 x-text="user.name"></h2>
<span x-show="user.isPremium" class="badge">Premium</span>
</div>
</div>
以下是 Alpine.js 代码工作原理的简明 4 点细分:
-
该组件使用 Alpine.js 指令直接在 HTML 标记中定义其反应状态和数据获取逻辑。
-
该
x-init
钩子在组件安装时自动触发数据加载,管理请求和状态更新。 -
Alpine 的
x-if
指令x-show
根据加载状态和数据可用性处理动态 UI 渲染。 -
当状态改变时,模板会自动重新渲染,使 UI 与底层数据保持同步,而无需手动操作 DOM。
比较
HMPL 提供了一种自动化渲染方法,内置了请求处理和模板功能,非常适合需要编译的复杂数据驱动组件。Alpine.js 通过显式 fetch 调用和响应式状态管理提供了更透明的控制,更适合轻量级交互。具体选择取决于项目需求——HMPL 擅长结构化模板,而 Alpine.js 则擅长快速原型设计和简单的动态元素。
🪄 自定义服务器请求
在构建动态 Web 应用程序时,高效地管理服务器请求至关重要。与自定义范围有限的 HTMX 不同,Alpine.js 提供了更广泛的自定义范围,自定义过程本身几乎与 jsx 类似,只需将 js 代码直接插入属性即可。当然,唯一的限制是模板。
<div x-data="{ user: null, error: null }"
x-init="fetch('/api/user')
.then(r => r.json())
.then(data => user = data)
.catch(e => error = e)">
</div>
用这种方法我们看到它非常方便,但问题是它很可能看起来像eval
,或者根据现成的模式处理模板。
这种方法存在许多严重的缺陷,因为随着 JS 的每个新版本的发布,越来越多的新功能需要通过这种方法来支持。在 jsx 中,这一点是合理的,因为事实上,jsx 是 React 的基础,而在这里它只是一个可以通过script
标签连接的模块。我们严重依赖于版本更新,这使得这种方法虽然方便,但并不完全实用。
在 HMPL 中,使用了一种不同的方法,我们可以直接在模板中编写我们需要的一些最小部分,例如方法、接收 HTML 的路由和其他内容,但是我们将整个 js 部分写入 js 中。
const templateFn = hmpl.compile(...);
const elementObj = templateFn(({
request: { event, clearInterval }
})=>{
clearInterval?.();
return {
mode: "cors",
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "text/html",
},
redirect: "follow",
get: (prop, value) => {},
referrerPolicy: "no-referrer",
body: new FormatData(event.target, event.submitter),
}
});
这里,js 部分和 html 部分有明确的区分。因此,你不会依赖于模块的新版本,因为描述 js 所需的一切,你都可以在这里编写,即使是 1.0.0 版本,即使是 3.0.1 版本。
此外,在 Alpine.js 中,使用此类语法存在 XSS 注入的风险。没错,它当然不是真正的eval
,而是相同的,只是语法有限,可以避免大多数风险,但仍然存在风险。这不仅是 Alpine.js 的问题,也是所有模块(包括 HMPL)的问题。顺便说一下,HMPL 中有一个选项,可以从sanitize
传入 HTML DOMPurify
。
📁 磁盘空间
这可能是比较时最简单、最易理解的事情。只需编写相同的代码并进行比较(但我们必须明白,如果应用程序很大,那么代码也会呈指数级增长)。
Alpine.js
document.querySelector(
"#app"
).innerHTML = `<div x-data="{ count: 0, l() { fetch('/a').then(r => r.text()).then(d => this.c = d)}}"><button @click="l()">Click!</button><div>Clicks: <span x-text="c"></span></div></div>`;
HMPL
document
.querySelector("#app")
.append(
hmpl.compile(
`<div><button>Click!</button><div>Clicks: {{#r src= "/api/clicks" after="click:button" }}{{/r}}</div></div>`
)().response
);
这里大概是清楚了,写出来会比较短。
但是,如果您仍然需要完整的测试,那么有一个包含 tests 的存储库。该模块还有第二个版本,它更短,但第三个版本的本质没有改变。
这些数字最接近大型和小型项目的实际结果。
⚙️ 模块内部是啥
这是指发送请求本身的技术。我不会写关于RegExp
处理模板的方法,或者如何将元素保存到数组中——这些对客户端来说根本不应该有趣。
我们主要考虑的是XMLHTTPRequest
支持fetch
。关于这个话题,我们有以下内容:
Alpine.js
fetch
Alpine.js 与和方法完全兼容XMLHTTPRequest
。在某些情况下,这允许发出更精确的请求,例如overrideMimeType
:
<div x-data="{ data: null }"
x-init="
const xhr = new XMLHttpRequest();
xhr.overrideMimeType("text/html");
xhr.open('GET', '/api/data');
xhr.onload = () => { data = JSON.parse(xhr.responseText) };
xhr.send();
">
<div x-text="data?.message || 'Loading...'"></div>
</div>
尽管这种方法存在缺点eval
,但它仍然是一种非常方便的切换方式,Alpine.js 在这方面可以发挥一定的优势。在相同的 HTMX 中,您只能使用它,XMLHTTPRequest
而无法更改它。
HMPL
不幸的是(或者说幸运的是),HMPL 不支持XMLHTTPRequest
请求,并且所有内容都建立在 之上fetch
,这是不可替代的。请求发生在模块内部,您只需描述它们:
<div>
<button data-action="increment" id="btn">Click!</button>
<div>Clicks: {{#request
src="/api/clicks"
after="click:#btn"
}}{{/request}}</div>
</div>
这也有一个优点,因为then
,catch
和其他东西都是在模块内实现的,所以你不必像在 jsx 中那样编写它。
结论
当然,你可以在项目中使用最适合你的框架。比如说,在文章中我们讨论了与服务器请求相关的部分,但 Alpine.js 也包含其他部分,这使得它成为一种轻量级框架。但是,如果我们要处理服务器端的工作,我建议(谁会怀疑呢👽)使用 HMPL,因为它更适合这项任务。所以,这两个选项都很不错!
🔗 模块链接:
HMPL - https://github.com/hmpl-language/hmpl
Alpine.js - https://github.com/alpinejs/alpine
非常感谢大家阅读这篇文章!
文章来源:https://dev.to/hmpljs/best-alpinejs-alternative-2hme