发布于 2026-01-06 6 阅读
0

我用这个替代方案替换了应用中的 Alpine.js 🔥

我用这个替代方案替换了应用中的 Alpine.js 🔥

通常,像 Alpine.js 这样的框架会被拿来与像 Next.js 这样的框架进行比较,因为它们比 Next.js 具有一些优势。

现在我们要深入探讨一些更相关的内容。这两个模块(Alpine.js 和 HMPL)都直接操作 HTML,并且都旨在让逻辑更贴近标记语言。这使得我们的比较更加实际。

今天,我们将用HMPL替换项目中的Alpine.js

让我们开始吧!🏎️


⚙️ 从简单的例子

我们先从最基本的点击器示例开始。使用 Alpine.js,你可能会这样写:

<div
  x-data
  x-fetch:clicker="{
    url: '/click',
    method: 'POST'
  }"
  x-init="$fetch.clicker"
  class="clicker-container"
>
  <h1>Alpine Clicker</h1>  
  <div class="click-count" x-text="$fetch.clicker.data?.count ?? 0"></div>  
  <button class="click-button" @click="$fetch.clicker">Click</button>
</div>
Enter fullscreen mode Exit fullscreen mode

它看起来很棒,最重要的是,它能正常运行。现在让我们来看看HMPL.js版本:

<body></body>
<script>
  const templateFn = hmpl.compile(`
    <div class="clicker-container">
      <h1>HMPL Clicker</h1>
      <div class="click-count" id="counter">
        {{#request src="/click" after="click:#btn"}}
        {{/request}}
      </div>
      <button id="btn">Click!</button>
    </div>
  `);
  const clicker = templateFn().response;
  document.querySelector("body").append(clicker);
</script>
Enter fullscreen mode Exit fullscreen mode

如您所见,我们采用了两种不同的理念:Alpine.js 在 HTML 内部以声明式方式工作,而 HMPL 则编译模板并通过 JS 动态渲染。但它们的接口在概念上是相似的——操作与事件相关联,数据在 DOM 中更新。区别在于控制和灵活性。

另外,如果您能用星星支持一下这个项目就太好了!谢谢❤️!

💎 星级 HMPL ★


🔍 内置限制

Alpine.js 虽然功能强大且简单易用,但它并非基于面向服务器的架构。它是一个客户端增强工具,旨在实现轻量级交互,而非完整的模板引擎或服务器端渲染。这使得它非常适合渐进增强,但在构建严重依赖服务器端内容的应用程序时则存在一定的局限性。

由于 Alpine 本身并没有将服务器视为 UI 生命周期的核心部分,因此集成复杂的请求流程或处理服务器状态往往感觉像是后期添加上去的。即使使用了类似这样的插件@alpinejs/fetch,你仍然需要编写大量的客户端逻辑,手动协调 fetch 调用,并在浏览器中管理响应式状态。

这种客户端密集型方法在应用规模扩大时会变得非常繁琐。表单提交、条件加载、动态内容更新等都需要越来越冗长的 JavaScript 绑定或涉及x-init外部方法和嵌套指令的变通方案。Alpine 提供了控制权,但通常缺乏抽象层。

相比之下,像 HTML 这样的模板语言是以服务器端为核心设计的。它原生支持服务器端渲染的 HTML 和基于获取数据的响应式开发。你无需手动协调状态或请求——它是声明式的、上下文感知的,专为服务器端和 UI 协同工作的场景而构建。


🔗 复杂示例

我们来看一个更高级的例子,一个带有加载指示器的注册表单。以下是它在 Alpine.js 中的实现示例:

<div x-data="formComponent()" id="wrapper">
  <form @submit.prevent="submit">
    <div class="form-example">
      <label for="login">Login: </label>
      <input type="text" name="login" id="login" x-model="form.login" required /><br/>
      <label for="password">Password: </label>
      <input type="password" name="password" id="password" x-model="form.password" required />
    </div>
    <div class="form-example">
      <input type="submit" value="Register!" />
    </div>
  </form>

  <template x-if="loading">
    <div class="indicator">
      <p>Loading...</p>
    </div>
  </template>

  <div id="response" x-text="responseText"></div>
</div>

<script>
  function formComponent() {
    return {
      form: { login: '', password: '' },
      loading: false,
      responseText: '',
      async submit() {
        this.loading = true;
        const res = await fetch('/api/register', {
          method: 'POST',
          body: JSON.stringify(this.form),
          headers: { 'Content-Type': 'application/json' },
        });
        const text = await res.text();
        this.responseText = text;
        this.loading = false;
      }
    };
  }
</script>
Enter fullscreen mode Exit fullscreen mode

现在让我们看看如何在HMPL中实现同样的功能:

<body></body>
<script>
  const templateFn = hmpl.compile(`
    <div id="wrapper">
      <form onsubmit="event.preventDefault();" id="form">
        <div class="form-example">
          <label for="login">Login: </label>
          <input type="text" name="login" id="login" required /><br/>
          <label for="password">Password: </label>
          <input type="password" name="password" id="password" required />
        </div>
        <div class="form-example">
          <input type="submit" value="Register!" />
        </div>
      </form>
      <p>
        {{#request
          src="/api/register"
          after="submit:#form"
          repeat=true
        }}
          {{#indicator trigger="pending"}}
            <div class="indicator">
              <p>Loading...</p>
            </div>
          {{/indicator}}
        {{/request}}
      </p>
      <div id="response">{{response.body}}</div>
    </div>
  `);

  const initFn = (ctx) => {
    const event = ctx.request.event;
    return {
      body: new FormData(event.target, event.submitter),
      credentials: 'same-origin',
    };
  };

  const result = templateFn(initFn);
  document.body.append(result.response);
</script>
Enter fullscreen mode Exit fullscreen mode

借助HMPL,我们可以获得精细的控制。您可以拦截事件、访问元素FormData、自定义标头、控制指示器,所有这些都可以在声明式模板结构中完成。


📊 尺寸对比

我们不能忽视尺寸差异。

Alpine.js 体积非常小,而且由于功能极简,所以体积也保持得很小。以下是一个简单的示意图:

📦 Alpine.js:

高山

📦 HMPL:

HMPL

所以,没错,Alpine 在体积上确实略逊一筹。但差距并不大——如果你要构建更具交互性或服务器驱动的系统,那么这点体积上的增加或许能带来更大的灵活性

此外,在更广泛的比较中,HMPL 的表现仍然出人意料地好:

比较

当然,功能集越小,最终的 JS 包就越小,但这同时也意味着你需要在 HTML 之外编写更多的命令式逻辑。


✅ 结论

Alpine.js 对于那些需要简单、内联响应式且无需大型框架复杂性的项目来说是一个不错的选择。但在本文中,我想介绍一种替代方案。

HMPL 为服务器驱动的 UI 提供了一种更现代化、更可定制的方法。如果您的应用需要动态数据处理、高级请求逻辑或完全的获取控制,HMPL 可能更适合您。

归根结底,关键在于选择合适的工具来完成合适的工作


感谢阅读这篇文章❤️!

你对这个对比有什么看法?请在评论区告诉我!

实用链接:

文章来源:https://dev.to/hmpljs/i-replaced-alpinejs-in-my-app-with-this-alternative-kgk