🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

HTMX 證明了對不同方法的需求。它簡單而巧妙,但功能有限。控制邏輯最終分佈在 HTML 屬性和後端處理程序之間。它沒有響應式狀態來支援複雜的 UI 流程,而且您的應用程式並非真正「即時」——它仍然需要客戶端程式碼來進行無需用戶互動的更新。

我把它提升到了一個新的維度:Go 語言的集中控制、靈活的路由系統、響應式設計以及跨 UI 的協調更新。它擁有 React 的強大功能,卻沒有它的混亂和 npm 的包袱,還擁有一些獨特的功能。

為了展示實際應用,讓我們建立一個動態、響應式的儀錶板應用程式——完全用 Go 編寫。在此過程中,您將了解框架的各個核心部分是如何協同工作的:從即時 HTML 更新和事件處理到狀態管理、並發控制和導覽。

圖片描述

免責聲明。

  • 省略了一些程式碼,完整版本可在此處取得

  • dev.to 不支援templ突出顯示,因此我將貼上來自 vim 的螢幕截圖(順便說一下)。

  • 為了演示目的,我故意省略了錯誤處理以減少 LOC;但這不是應該做的事情!

這是一個深入的探索,它能為你提供足夠的知識,讓你開始建立自己的專案。如果你只想了解大概情況,可以滾動瀏覽,看看 GIF,如果你覺得有什麼有趣的地方,可以繼續閱讀上面的部分。

  1. 第一頁

通用頁面模板

./page_template.templ

此應用程式有一個包含多個路徑變體的頁面,因此不需要單獨的範本。

儘管如此,將焦點分開還是不錯的。

頁面介面

頁面必須向模板提供headbody內容:

圖片描述

範本

圖片描述

兩點說明:

  1. 我們包括框架的資產;這至關重要。

  2. 我們不僅使用了<link rel="stylesheet" href="..."> ,還使用了doors.ImportExternalStyle ,它還能收集資訊。 CSP頭產生。預設情況下,CSP 是禁用的,但這為我們做好了準備。

doors.Import...處理本地、嵌入和外部的 CSS 和 JS 資源。對於 JavaScript/TypeScript 模組,它啟用建置/打包步驟並產生導入映射。

./page.templ

頁面路徑

doors中,URI 被解碼為路徑模型。它支援路徑變體、參數和查詢值。

我們的路徑有兩種變體:

  • /位置選擇器

  • /:Id

一個參數:

  • 城市ID

以及兩個查詢值:

  • 預測天數

  • 單位(公制/英制)

我們現在將省略查詢值,稍後再新增它們。

我們的路徑模型:

圖片描述

  • 此框架使用path標籤將請求路徑與提供的模式進行比對。

  • 匹配變體的欄位設定為 true。

路徑結構被包裝在狀態原語( Beam )中並傳遞給頁面渲染函數::

圖片描述

為了與框架相容,頁面類型必須實作Render(),返回一個元件,並接受一個帶有路徑模型的Beam

頁面處理程序

路徑匹配時執行的函數。它讀取請求資料(doors.RPage)並選擇回應(doors.PageRouter)。

在我們的例子中,這很簡單:

圖片描述

doors.PageRouter也支援軟(內部)和硬(HTTP)重定向以及提供靜態頁面。

路由器

./main.go

建立一個門路由器,提供一個頁面處理程序,並啟動伺服器。

圖片描述

注意doors.Router是如何直接插入 Go 標準伺服器的! Go 太棒了。

我們還在直播!

圖片描述

  1. 位置選擇器

./location_selector.templ

搜尋輸入

編寫搜尋片段並將其呈現在頁面上。

分段

片段是具有 Render() templ.Component 方法的結構

圖片描述

輸入元件

將事件掛鉤附加到輸入欄位:

圖片描述

doors.AInput為該元素和事件建立一個臨時的私有端點。

選項元件

查詢並呈現搜尋結果。

圖片描述

每個doors.Door在 goroutine 池上並行渲染,因此渲染期間的資料查詢通常不會減慢頁面渲染速度。

把它放在頁面上

./page.templ

圖片描述

……盡情享受吧!

圖片描述


稍微想一下。我們剛剛實現了動態搜尋,無需自訂 JS 或 API,並且只在一個控制流中實現。


去抖動和載入器

您不想將每個擊鍵傳輸到伺服器。

在輸入配置中新增一行以使用Scopes API啟用去抖動過濾:

圖片描述

使用去抖動功能後,重複值出現的可能性會更大。在這種罕見的情況下,用相同的資料更新搜尋結果讓我很不爽。加入一個簡單的檢查來防止這種情況發生:

圖片描述

doors保證同一個鉤子的呼叫按順序執行,因此prevValue沒有並發存取問題。

實際上,回應不是即時的,因此要向使用者指示進度。

PicoCSS 為此提供了一個屬性。使用Indication API在待處理作業期間切換它。

最終程式碼:

圖片描述

去抖動和指示一起(模擬延遲):

圖片描述

所有鉤子觸發的變更應用於客戶端後,該指示將清除。

反應狀態

國家和城市選擇只能使用Door ,無需任何反應狀態。

然而,在多步驟表單和複雜 UI 中,這種「低階」方法會將邏輯分散到各個處理程序中,損害可讀性和可偵錯性。在這種情況下,單一資料來源可以顯著減少心理負擔。

國家選擇

加入源光束並在渲染函數中訂閱它:

圖片描述

國家選擇器元件(之前位於主渲染函數內):

圖片描述

顯示所選國家和重置按鈕:

圖片描述

點選選項更新光束:

圖片描述

BlockingScope會在處理前一個事件時取消所有新事件。它減少了不必要的請求,並明確了意圖。

另外,請注意,我們對所有搜尋選項使用了相同的範圍集,這實際上意味著來自所有處理程序的事件都通過單一管道,只允許一個活動處理程序。

讓我們看看選擇如何與反應狀態一起工作:

圖片描述

哎呀。搜尋結果未清除。這很正常;我們沒有清除它們。

使固定:

圖片描述

結果:

圖片描述

位置選擇器

國家選擇器是抽像地點選擇器的原型。暫時將其註解掉。

計劃:

  1. 來源光束新增至保存組合國家和城市資料的位置選擇器。

  2. 為國家和城市價值得出單獨的光束

  3. 將我們之前的國家選擇器轉換為抽象的“地點”選擇器。

  4. 使用地點選擇器編寫位置選擇器渲染函數。

我們走吧

建立包含國家和城市資料的來源光束

圖片描述

衍生鄉村和城市Beams

圖片描述

源 Beam是原始值,可以更新、變異和觀察。 Beam 只能被觀察。

抽像地點選擇器

結構:

圖片描述

我們之前的國家選擇器的方法進行了最小程度的修改(見評論):

主要渲染:

圖片描述

顯示所選內容:

圖片描述

選擇地點:

圖片描述

輸入:

圖片描述

和選項:

圖片描述

在位置選擇器渲染中使用我們的地點選擇器

圖片描述

具有反應狀態的動態形式:

圖片描述


Beam是一種帶有值流的通訊原語。您可以直接觀察它,也可以使用doors.Sub / doors.Inject來渲染一個會在變更時自動更新的 Door。

它具有一些重要屬性:

  1. 僅在值變更時觸發訂閱者。預設情況下,它使用==來判斷是否需要更新;您可以提供自訂的相等函數。

  2. 與渲染同步。在渲染過程中,所有參與節點都會觀察到相同的值。

  3. 自上而下地傳播更改。換句話說,負責 DOM 中更重要部分的訂閱者將首先被觸發。

  4. 取消過時傳播。如果值在傳播過程中發生變化,則取消過時傳播(如果需要,可使用來源光束上的NoSkip覆蓋)。

  5. 派生光束以群組的形式更新。訂閱處理程序在 goroutine 池上並行執行。

所有這些屬性結合在一起就使得它按預期工作 - 您幾乎不需要考慮它。

獎勵:改善使用者體驗(使用 JavaScript)

表單中缺少鍵盤支援很煩人。

加入鍵盤支援:

  1. 自動聚焦輸入。

  2. Tab 並輸入選項的支援。

連接一些 JS

透過 JS 根據 ID 獲得焦點:

圖片描述

更好的:使其成為可重複使用的元件:

圖片描述

光滑。

並在輸入後渲染:

圖片描述

doors.Script非常棒。它將內聯腳本轉換為精簡版(如果沒有其他配置),並可快取 src 腳本,保護全域作用域,並啟用await 。另外,如果你提供type="application/typescript"屬性,它還能編譯 TypeScript。

啟用 Tab + Enter

附加一個鍵事件鉤子並將tabindex新增至選項:

圖片描述

啟用鍵盤控制:

圖片描述

  1. 路徑和標題

應用選定的位置

以下是頁面程式碼:

圖片描述

它始終顯示位置選擇器。記住,我們有兩種路徑變體: //:Id 。後者用於選擇位置時。

現在看看這個傢伙:

圖片描述

你大概也猜到這是怎麼回事了(路徑訂閱)。我們也會新增查詢參數,但我們不希望每次查詢更改時整個頁面都重新渲染,所以derive

圖片描述

然後訂閱:

圖片描述

顯示選定的城市或 404:

圖片描述

現在位置選擇器必須動態改變路徑。

在位置選擇器中新增應用依賴項:

圖片描述

實現提交功能:

圖片描述

為選擇器提供應用功能:

圖片描述

我們可以不傳遞路徑變更函數,而是直接渲染一個連結。此範例展示如何以程式設計方式進行導航。

透過路徑變異選擇的位置:

圖片描述

動態標題

在 doors 中,你可以使用$d.on(...)在前端註冊一個 JS 處理程序,並使用doors.Call(...)在 Go 中呼叫它。實作動態標題非常簡單。

但是,您只需使用預製的doors.Head元件即可:

圖片描述

除了title之外,它還支援<meta>標籤。

  1. 進階作用域用法

實際上,提交處理程序不會立即回應。如果我們在處理過程中與 UI 互動會怎麼樣?

讓我們模擬衝突的行為:

圖片描述

它並沒有改變結果,但卻導致了奇怪的 UI 行為。

使用Scopes API中的Concurrent Scope可以輕鬆緩解此問題。

並發範圍只能被具有相同群組 ID 的事件「佔用」。

在位置選擇器中新增並發範圍:

圖片描述

在地點選擇器中新增父範圍屬性:

圖片描述

將群組 ID 1 指派給兩個位置選擇器:

圖片描述

在“更改位置”按鈕上使用它:

圖片描述

最後,將其套用到具有不同群組 ID 的提交按鈕:

圖片描述

此設定可確保提交事件或更改位置事件可以執行,而不是同時執行:

圖片描述

由於城市和國家屬於同一組,因此變更不會相互影響:

圖片描述

由於框架的非阻塞事件模型,並發控制是必要的。這是Doors相較於 Phoenix LiveView 或 Blazor Server 的一大優勢,它能夠在不犧牲使用者體驗的情況下實現高度互動的 UI。

  1. 菜單

查詢參數

在天氣 API 中,除了城市之外,我們還有兩個變數:單位(公制/英制)和預報天數。

將其加入到我們的路徑模型中:

./page.templ

圖片描述

注意我使用了引用類型。否則,查詢參數將獲得零值並始終出現在 URI 中。

儀表板片段

./dashboard.templ

分離儀表板片段

為了讓頁面保持簡單,我們將儀表板移到單獨的片段。

圖片描述

儀表板取決於位置 ID(頁面已提供)以及天數和單位查詢參數:

圖片描述

我們從以下路徑中得出這些:

圖片描述

在頁面上呈現儀表板:

圖片描述

動態連結

更改城市

為了顯示位置選擇器,我們需要渲染一個指向/連結。如果查詢參數能夠持久化就更好了,所以我們根據設定光束產生連結:

圖片描述

AHref也支援ScopesIndication API

透過動態連結切換到位置選擇器:

圖片描述

點擊後,查詢參數顯示為預設值。還可以,但並不理想。

為預設值提供 nil,以使行為保持一致:

圖片描述

現在我很高興:

圖片描述

單位

渲染一個連結來回切換單位:

圖片描述

加入一些樣式並渲染單位切換器:

圖片描述

查詢參數切換:

圖片描述

預報天數

預測天數連結必須保留單位查詢值。為了避免不必要的更新,請為下列單位衍生一個波束:

圖片描述

訂閱菜單吧:

圖片描述

並保持單位查詢值:

圖片描述

反應選單:

圖片描述

doors不會將整個 DOM 保存在記憶體中。使用波束推導,你可以明確地將特定的 HTML 部分與特定的資料片段綁定。差異資料,而不是 DOM。

獎勵:主動連結突出顯示

如果您在doors.AHref中配置了活動連結突出顯示,客戶端可以自動套用它:

圖片描述

預設情況下,它會檢查整個路徑和所有查詢值以套用指示,但您可以配置更窄的匹配策略。

活動連結突出顯示:

圖片描述


路徑作為狀態非常強大。它具有聲明性、類型安全,並且不限制路徑到 HTML 的映射方式。


6.圖表

溫度

讓我們準備一個溫度圖並看看情況如何。

我將使用doors.Inject輔助函數來取代doors.Sub 。它本質上的作用相同,但它不是對函數進行求值,而是使用包含 beam 值的上下文來渲染子級。

為了提供生成的 SVG,我將使用doors.ARawSrc ,它使用自訂請求處理程序建立src屬性:「

圖片描述

doors.ARawSrc (以及doors.ASrcdoors.AFileHrefdoors.ARawFileHref )使用鉤子機制並私下提供資源。

帶有動態 SVG 的溫度折線圖:

圖片描述

一切

抽象圖表元件,以便它可以被所有圖表重複使用:

圖片描述

所有圖表:

圖片描述

使用者體驗改進

影像預載器+參數開關指示:

圖片描述

在所有選單連結上包含此指示:

圖片描述

帶有預先載入器的圖表:

圖片描述

最終優化

您可能已經注意到天氣和濕度並不取決於units值。

像往常一樣近似 - 推導出不依賴單位的光束:

圖片描述

此外,我們不需要觸發該指示,因此請使其更具體:

圖片描述

具有days變化的圖表元件:

圖片描述

最終結果(慢速網路模擬):

圖片描述

頁面大小:

圖片描述

其中~13 KB 是 PicoCSS,~10 KB 是門客戶端。

結論

坦白說,我討厭寫 UI,它總是讓我感覺自己做錯了什麼。感覺就像擁有 10 種不同的工具,它們都不是為某項工作而設計的,所以你必須以一種笨拙的方式組合它們才能完成某項工作。這感覺已經不像程式了。

有了門,感覺就像程式設計一樣。可預測的解決路徑、已知的結果、自由。

我很享受編寫這個小應用程式的每一分鐘,我希望您能抽出時間親自體驗一下。

GitHub

官方網站

特別感謝 Adrian Hesketh 在templ上所做的出色工作,使得這個專案成為可能。


原文出處:https://dev.to/derstruct/go-devs-just-got-superpowers-2lb3


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

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝19   💬9   ❤️5
726
🥈
我愛JS
📝4   💬14   ❤️7
249
🥉
御魂
💬1  
3
#5
2
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付