阿川私房教材:
學 JavaScript 前端,帶作品集去面試!

63 個專案實戰,寫出作品集,讓面試官眼前一亮!

立即開始免費試讀!

在本文中,我將向您介紹 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-datax-showx-text )直接在 HTML 中描述行為。

<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-ifx-show指令根據載入狀態和資料可用性處理動態 UI 渲染。

  • 當狀態改變時,模板會自動重新渲染,使 UI 與底層資料保持同步,而無需手動操作 DOM。

比較

HMPL 提供了一種具有內建請求處理和模板的自動渲染方法,非常適合複雜資料驅動的元件但需要編譯。 Alpine.js 透過明確取得呼叫和反應狀態管理提供更透明的控制,更適合輕量級互動。選擇取決於專案需求 - 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 中這一點是有道理的,因為事實上,這是 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 ,但是相同,但具有有限的語法,可以防止大多數危險,但仍然存在危險。這不僅是它的問題,也是所有模組(包括 HMPL)的問題,但順便說一下,在 HMPL 中,有一個選項可以sanitize來自DOMPurify傳入 HTML。


📁 磁碟空間

這可能是比較時最簡單、最容易理解的事。只需編寫相同的程式碼並進行比較(但我們必須明白,如果應用程式很大,那麼程式碼也會呈指數增加)。

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
  );

這裡大概是清楚了,寫出來會比較短。

但是,如果您仍然需要成熟的測試,那麼有一個包含測試的儲存庫。該模組還有第二個版本,甚至更短,但第三個版本的本質並沒有改變。

比較

這些數字最接近大型和小型專案的實際結果。


⚙️ 模組內部是啥

這是指發送請求本身的技術。我不會寫關於使用RegExp來處理模板,或如何在陣列中保存元素的工作原理——這對客戶來說根本不有趣。

我們主要考慮的是XMLHTTPRequestfetch支援。關於這個主題,我們有以下內容:

Alpine.js

Alpine.js 與fetchXMLHTTPRequest方法完全相容。在某些情況下,這允許提出更精確的請求,例如使用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>

這也有一個好處,因為thencatch和其他東西都是在模組內部實現的,所以你不必像在 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


共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。

阿川私房教材:
學 JavaScript 前端,帶作品集去面試!

63 個專案實戰,寫出作品集,讓面試官眼前一亮!

立即開始免費試讀!