- Go’nun
html/template tabanlı sunucu tarafı render yapısını korurken HTMX ile dinamik etkileşimler ekler ve tek bir renderer üzerinden tam sayfa ile HTML parçalarını seçmeli olarak döndürür
- İşaretlemeyi
base.tmpl, sayfa şablonları ve yeniden kullanılabilir parçalara ayırır; Go 1.16’nın embed.FS yapısını ve paylaşılan şablon kümesini kullanarak dağıtım ve render yapısını sadeleştirir
HX-Request durumuna göre arama sonuçlarını tam sayfa ya da tablo satırları olarak döndürür; Vary: HX-Request ve historyRestoreAsHxRequest: false ile önbellek ve geri gitme geri yükleme hatalarını önler
- HTMX isteklerini başka bir sayfaya yönlendirirken normal 3xx yerine
HX-Redirect ve 2xx yanıtı kullanır; 204·422·4xx·5xx için responseHandling ile değiştirme kapsamını ve hata gösterimini kontrol eder
- Yerel depolama önbelleğini ve öznitelik kalıtımını kapatıp istek zaman aşımını ayarlayan yapılandırmayı başlangıç noktası olarak alır; ölçek büyüdüğünde yönetim alanı gibi ayrı ekran düzenlerini desteklemek için ara layout şablonları eklenebilir
Go’nun sunucu tarafı render yapısını HTMX ile birleştirme nedeni
- HTMX, çok az JavaScript ile dinamik etkileşimler eklerken Go
html/template yapısının sağladığı sunucu tarafı HTML render tutarlılığını ve güvenliğini korumayı sağlar
- Uygulamanın ele aldığı üç eksen vardır
- Tam sayfa ve kısmi HTML yanıtlarını birlikte destekleyen şablon yapısı
- HTMX ortamında yönlendirme ve hata işleme
- HTMX varsayılanlarını değiştiren yapılandırma ve bunun nedenleri
- Düğmeyi bir görselle değiştiren temel işlevden başlayıp, kullanıcı adı veya e-posta girilirken listeyi filtreleyen bir arama ekranına doğru genişler
- Aynı şablon deseni yalnızca HTMX için değil, Unpoly ve Hotwire gibi HTML-over-the-wire araçları için de uygulanabilir
- HTMX’in temel kullanımını bilmiyorsanız önce resmi dokümantasyona bakmak daha uygundur
Proje dizini ve statik dosya yapısı
- Proje, rolüne göre aşağıdaki alanlara ayrılır
assets/html/base.tmpl: tüm sayfaların paylaştığı HTML iskeleti
assets/html/pages/: sayfaya özgü içerik
assets/html/partials/: birden fazla yerde yeniden kullanılan HTML parçaları
assets/static/css, assets/static/img, assets/static/js: statik varlıklar
cmd/web/: sunucu başlatma, handler ve HTML renderer kodu
- Temel iskelet aşağıdaki komutlarla oluşturulur
go mod init example.com/htmx
mkdir -p assets/static/css assets/static/img assets/static/js assets/html/partials assets/html/pages cmd/web
touch assets/efs.go assets/html/base.tmpl assets/html/partials/images.tmpl assets/html/pages/home.tmpl cmd/web/main.go cmd/web/handlers.go cmd/web/html.go
- HTMX, CDN veya NPM ile de kurulabilir; ancak genellikle bir kopyası indirilip uygulamanın statik dosyası olarak doğrudan sunulur
- Yapılandırma daha basittir ve CDN kullanımının dezavantajlarından kaçınmayı sağlar
htmx.org@2.0.10 ve sınıfsız CSS framework’ü bamboo.css@1.4.0 kullanılır
wget -P assets/static/js https://cdn.jsdelivr.net/npm/htmx.org@2.0.10/dist/htmx.min.js
wget -P assets/static/css https://cdn.jsdelivr.net/npm/bamboo.css@1.4.0/dist/bamboo.min.css
Temel, sayfa ve parça şablonlarını ayırma
base.tmpl, <head>, ortak başlık, <main> gibi tüm belge iskeletini tanımlar ve page:title ile page:content bölümlerini yerleştirir
- Bamboo CSS ve HTMX script’i temel şablonda yüklenir; HTMX script’ine
defer niteliği verilir
- Tarayıcı HTML’i parse ederken script’i paralel olarak getirir
- HTML parse işlemi ve DOM oluşturma bittikten sonra script’i çalıştırır
- Tüm şablonlara dosya adına bağlı kalmadan
{{define}}...{{end}} ile açık ad verilir
- Go kodu yalnızca tanımlı adlarla şablonlara tutarlı şekilde başvurabilir
page:title içindeki : keyfi bir ayraçtır; page_title, page-title, pageTitle, title gibi adlar da kullanılabilir
- Ana sayfadaki düğmede iki davranış tanımlanır
hx-get="/gopher": tıklanınca GET /gopher isteği gönderir
hx-swap="outerHTML": yanıt HTML’i ile düğmenin kendisini değiştirir
partial:image:gopher, yeniden kullanılabilir bir görsel parçasıdır ve width="{{.}}" ile çağrı anında görsel genişliğini alır
- Yalnızca belirli bir sayfada kullanılan HTML parçaları ortak
partials dizininde değil, ilgili sayfa dosyasında tutulabilir
- Birden fazla sayfada kullanılan parçalar ortak dizinde, sayfaya özel parçalar ise ilgili içeriğin yanında konumlandırılır
HTML ve statik varlıkları Go binary’sine gömme
- Go 1.16 ile dosya embedding eklendikten sonra HTML ve statik varlıklar çalışma anında diskten okunmak yerine Go binary’sine dahil edilebilir
//go:embed "html" "static" yönergesiyle iki dizin embed.FS içine alınır ve fs.Sub() kullanılarak iki alt dosya sistemine ayrılır
HTMLFiles: kök olarak html kullanan şablon dosya sistemi
StaticFiles: kök olarak static kullanan statik dosya sistemi
- Bu ayrımın iki avantajı vardır
- HTML kodu ve statik dosya kodu birbirinin dosyalarına gereksiz erişim yapmaz
- Dosya açarken
html/ veya static/ önekini eklemek gerekmez
sub() içinde panic() kullanmak istemiyorsanız hata döndürüp iki değişkeni main() içinde de başlatabilirsiniz
- Mevcut uygulamada
fs.Sub() ancak dizin değeri geçerli bir yol değilse başarısız olabilir; fakat sabit string "html" ve "static" bu denetimi geçtiği için çalışma zamanı panic riski çok düşüktür
Paylaşılan şablon kümesi ve htmlRenderer
htmlRenderer, render akışını şu şekilde yönetir
- Başlangıçta paylaşılan şablon kümesini bir kez parse eder
- Her render işleminde paylaşılan kümeyi clone eder
- İstek için gereken sayfa şablonlarını ek olarak parse eder
- Belirtilen adlı şablonu çalıştırıp HTTP yanıtı olarak gönderir
newHTMLRenderer(), template.FuncMap kaydettikten sonra ortak şablonları parse eder
now, time.Now fonksiyonuna bağlanır
- Diğer özel şablon fonksiyonları da aynı yere eklenebilir
render(), sharedTemplates.Clone() ile istek başına bir şablon kümesi oluşturur ve additionalTemplateFiles varsa ParseFS() ile genişletir
- Şablon önce
bytes.Buffer içine çalıştırılır, ardından durum kodu yazılır ve yanıt gövdesi olarak gönderilir
- Sunucu başlatılırken
"base.tmpl" ve "partials/*.tmpl" paylaşılan küme olarak parse edilir
- Temel şablon ve tüm ortak parçalar renderer içinde her zaman kullanılabilir olur
- Yalnızca sayfaya özgü şablonlar handler çağrısında eklenir
- Statik dosyalar
http.FileServerFS(assets.StaticFiles) ve /static/ yolu üzerinden sunulur; sunucu :5051 üzerinde çalışır
Tam sayfa ve HTML parçaları render etme
- Ana sayfa handler’ı aşağıdaki çağrıyla tam belgeyi döndürür
app.html.render(w, 200, nil, "base", "pages/home.tmpl")
- Bu çağrı,
pages/home.tmpl dosyasını paylaşılan kümeye ekler ve base şablonunu 200 OK ile çalıştırır
/gopher handler’ı ek dosya parse etmeden, paylaşılan kümede zaten bulunan partial:image:gopher şablonunu çalıştırır
width := 100
app.html.render(w, http.StatusOK, width, "partial:image:gopher")
- HTMX,
/gopher adresinden alınan görseli hx-swap="outerHTML" ayarına göre butonla değiştirir
- Bu yapı şu avantajları sağlar
- Şablonlar ve statik varlıklar ikili dosyaya gömülür, böylece dağıtım kolaylaşır
- Aynı
render() ile hem tam HTML sayfası hem de belirli HTML parçaları döndürülebilir
- Temel şablonlar ve parça şablonlar sayesinde işaretleme tekrarları azalır
- Parça şablonlar temel şablonda, sayfa içeriğinde ve diğer parça şablonlarda yeniden kullanılabilir
Yazarken anında güncellenen kullanıcı araması
- Arama özelliği iki yola ayrılır
GET /users: tüm kullanıcı bilgilerini içeren tam HTML sayfası
GET /users/search: adı veya e-postası arama terimiyle eşleşen kullanıcıların yalnızca tablo satırlarını döndüren HTML parçası
- Arama giriş kutusu şu HTMX özelliklerini birleştirir
hx-get="/users/search" ile arama isteği gönderir
hx-trigger="input changed delay:500ms, keyup[key=='Enter']" ile giriş değiştikten 500ms sonra veya Enter'a basılınca istek yapar
- Arama terimi
GET /users/search?query=foo biçiminde sorgu dizesi olarak iletilir
hx-target="#search-results" ile yanıtı <tbody id="search-results"> içine yerleştirir
hx-push-url="true" ile her istekte adres çubuğunu günceller ve tarayıcı geçmişine bir kayıt ekler
users:rows, yalnızca kullanıcı satırlarını render eden ayrı bir şablondur
- Her satırda ad ve e-posta gösterilir
IsGopher true ise partial:image:gopher 24 genişlikle render edilir
listUsers ve searchUsers her ikisi de pages/users.tmpl dosyasını paylaşılan kümeye ekler, ancak çalıştırdıkları şablon farklıdır
listUsers: base çalıştırılır ve tam sayfa döndürülür
searchUsers: users:rows çalıştırılır ve yalnızca eşleşen satırlar döndürülür
- Arama terimi boşsa tüm kullanıcı listesi kullanılır, doluysa ad ve e-posta
strings.Contains() ile kontrol edilir
- Gerçek bir uygulamada şu iyileştirmeler düşünülebilir
- Liste ve arama handler'larını birleştirmek
- Hata işleme ve loglama yardımcılarını merkezileştirmek
- Middleware ile Content Security Policy başlıkları ve panic kurtarma eklemek
- Sunucu zaman aşımı sınırlarını uygun şekilde ayarlamak
HX-Request ile tam sayfa ve parça yanıtlarını ayırma
- Kullanıcı
/users/search?query=leo adresini tarayıcıda doğrudan açar ya da paylaşılan bir bağlantıyla ziyaret ederse, yalnızca tablo satırlarına karşılık gelen parça HTML'yi görebilir
- HTMX istekleri her zaman
HX-Request: true başlığını içerdiğinden, yanıt biçimi buna göre belirlenir
func isHTMXRequest(r *http.Request) bool {
return r.Header.Get("HX-Request") == "true"
}
- Arama handler'ının varsayılan yürütme şablonu
base olarak bırakılır, yalnızca HTMX isteği olduğunda users:rows olarak değiştirilir
- Doğrudan ziyaret edilirse arama sonuçlarını içeren tam sayfa alınır
- HTMX isteğinde yalnızca
<tbody> içine yerleştirilecek satır parçaları döndürülür
Vary başlığı ve geri gitme sırasında geri yükleme
- Aynı URL,
HX-Request değerine göre farklı yanıtlar döndürdüğü için önbelleğe de bu fark bildirilmelidir
- Tüm render yanıtlarına
Vary: HX-Request eklenir
- Bunu yalnızca gereken yerlerde tek tek handler'larda ayarlamak teknik olarak daha az israf yaratır
- Render katmanında topluca ayarlamak, unutma nedeniyle oluşabilecek hataları önler
hx-push-url veya hx-boost tarayıcı geçmişine kayıt eklediğinde HTMX, tamamlanmış sayfa HTML'sini tarayıcının yerel depolamasında önbelleğe alır
- Varsayılan önbellek en fazla 10 sayfadır
- Önbellekte olmayan bir noktaya kadar geri gidilirse HTMX ilgili URL'yi sunucudan yeniden ister
- Varsayılan geri yükleme isteği
HX-Request: true içerdiğinden, sunucu tam sayfa yerine parça HTML döndürebilir
- Yanıt türü
HX-Request ile seçiliyorsa historyRestoreAsHxRequest: false ayarlanmalıdır
- Önbellek ıskalamalı geri yükleme isteklerinde
HX-Request: true gönderilmez
- Böylece sunucu bu isteğe tam HTML sayfası döndürür
- HTMX yapılandırması
base.tmpl içindeki htmx-config meta etiketine konur
HTMX isteklerini başka bir sayfaya yönlendirme
- HTMX, başarı mesajı gibi HTML parçalarını anında değiştirebildiği için klasik Post/Redirect/Get akışına duyulan ihtiyaç çoğu durumda azalır
- Giriş sonrası profile gitmek gibi tamamen farklı bir sayfaya yönlendirmek gerektiğinde yalnızca normal 3xx yanıtlarıyla bu yapılamaz
- Tarayıcı, 3xx yanıtını HTMX'ten önce takip eder
- HTMX ise yönlendirme sonrasındaki nihai yanıtı görür ve içeriğini mevcut hedefe yerleştirir
- HTMX isteklerinde 2xx yanıtla birlikte
HX-Redirect başlığı kullanılır
HX-Redirect: /foo/bar tarayıcıyı ilgili URL'ye götürür ve tam sayfayı yeniden yükler
- Hedefe yapılan istekte
HX-Request: true bulunmaz
- JavaScript'in kapalı olduğu veya HTMX'in yüklenmediği ortamları da desteklemek isteniyorsa, HTMX olmayan isteklerde normal 3xx yanıtları korunmalıdır
func redirect(w http.ResponseWriter, r *http.Request, url string, code int) {
if isHTMXRequest(r) {
w.Header().Set("HX-Redirect", url)
w.WriteHeader(http.StatusNoContent)
return
}
http.Redirect(w, r, url, code)
}
- Girişten sonra
/profile sayfasına gitmek için şöyle çağrılabilir
redirect(w, r, "/profile", http.StatusSeeOther)
HX-Redirect ile HX-Location arasındaki fark
HX-Location, tam sayfayı yeniden yüklemeden yönlendirmeye benzer bir davranış sergiler
- Hedef URL'nin HTML'sini alır
- Yanıtı
<body> ile değiştirir
- Tarayıcı geçmişine yeni bir kayıt ekler
- Hedef URL istenirken
HX-Request: true gönderilir
- Hedef yol
HX-Request değerine göre parça HTML döndürüyorsa sorun çıkabilir
- Handler,
HX-Location ile gelen istekle normal HTMX isteğini ayırt edemez
- İlkinde tam sayfa gerekirken ikincisinde parça yanıt gerekir
- Geçmiş geri yüklemeden farklı olarak,
HX-Location geçişlerinde HX-Request: true göndermeyi kapatan bir ayar yoktur
- Çoğu durumda tam sayfa yeniden yüklemeyi kabul edip
HX-Redirect kullanmak daha kolay ve güvenlidir
- Hedef yol her zaman yalnızca tam HTML döndürecek şekilde yapılandırıldıysa
HX-Location da kullanılabilir
204·422·4xx·5xx yanıtlarını işleme
- HTMX varsayılan olarak 4xx veya 5xx yanıtlarını DOM'a yerleştirmez
- Mevcut ekran olduğu gibi kalır
- Hata, geliştirici araçları konsoluna kaydedilir
- Sunucu, var olmayan bir şablon adı nedeniyle
500 Internal Server Error döndürse bile, varsayılan ayarlarda kullanıcı hatayı ekranda göremez
- Hata mesajını kullanıcıya göstermek için 4xx ve 5xx yanıtları tüm
<body> üzerinde değiştirilecek şekilde ayarlanır
- İstisna
422 Unprocessable Content durumudur
- Doğrulama hataları içeren formu geri döndürmek için kullanılır
- Yanıtın mevcut HTMX hedefine normal şekilde yerleştirilmesi gerekir
responseHandling, durum koduna göre şöyle yapılandırılır
204 No Content: DOM değiştirilmez
422 Unprocessable Content: mevcut hedefte normal değişim yapılır
- Diğer 4xx ve 5xx: yanıt
<body> ile değiştirilir
- Bunun dışındaki yanıtlar: mevcut hedefte normal değişim yapılır
"responseHandling":[
{"code":"204", "swap": false},
{"code":"422", "swap": true},
{"code":"[45]..", "swap": true, "target": "body"},
{"code":"...", "swap": true}
]
- Belirli etkileşimlerde hataların
<body> yerine başka bir hedefe yerleştirilmesi gerekiyorsa, varsayılan ayarı geçersiz kılmak için response target eklentisi kullanılabilir
Tarayıcı adres çubuğundaki mevcut URL’yi kontrol etme
- HTMX,
hx-boost, hx-push-url, hx-replace-url kullanılmadığı sürece AJAX isteği ve yanıt değiştirme sürecinde tarayıcı URL’sini değiştirmez
- Bu nedenle Go işleyicisindeki
r.URL ile kullanıcının adres çubuğunda gördüğü URL farklı olabilir
- HTMX, her isteğin
HX-Current-URL başlığında tarayıcının o anda gösterdiği URL’yi iletir
- Yardımcı işlev bu başlığı
url.URL olarak ayrıştırır; başlık yoksa r.URL döndürülür
func browserURL(r *http.Request) (*url.URL, error) {
cu := r.Header.Get("HX-Current-URL")
if cu != "" {
return url.Parse(cu)
}
return r.URL, nil
}
HTMX varsayılanlarını değiştiren yapılandırma
historyCacheSize: 0 ile HTMX’in local storage sayfa önbelleği tamamen kapatılır
- local storage önbelleği hata ve güvenlik sorunlarının kaynağı olabilir
- Geri düğmesine basıldığında önbellek yerine sunucudan HTML yeniden alınır
- Gelecekteki HTMX sürümlerinde de aynı nedenle local storage önbelleği varsayılan olarak devre dışı olacak
disableInheritance: true ile HTMX öznitelik kalıtımı devre dışı bırakılır
- Öznitelikleri her zaman açıkça tanımlamak daha nettir
- Hata veya istenmeyen davranış riskini azaltır
- Gelecekteki HTMX sürümlerinde öznitelik kalıtımı da varsayılan olarak devre dışı olacak
includeIndicatorStyles: false ile HTMX’in gösterge stillerini enjekte etmesi engellenir
- Tutarlılığı korumak için gösterge stilleri diğer CSS kurallarıyla birlikte doğrudan tanımlanır
- HTMX istekleri varsayılan olarak zaman aşımına sahip değildir; sunucu yanıt verene kadar bekler
- Go uygulamasının zaman aşımı ve deadline yönetimine göre
timeout milisaniye cinsinden belirtilebilir
- Başlangıç noktası olarak
timeout: 5000 kullanılır
- Varsayılan yapılandırmada
historyRestoreAsHxRequest: false ve durum koduna göre responseHandling da yer alır
<meta
name="htmx-config"
content='{
"includeIndicatorStyles": false,
"historyCacheSize": 0,
"historyRestoreAsHxRequest": false,
"responseHandling":[
{"code":"204", "swap": false},
{"code":"422", "swap": true},
{"code":"[45]..", "swap": true, "target": "body"},
{"code":"...", "swap": true}
],
"timeout": 5000
}'
>
Büyük uygulamalar için sayfa bazlı düzenler
- Temel şablon ve sayfa bazlı şablonlar yeterli değilse, iki katman arasına bir düzen şablonu eklenebilir
- Yönetici sayfaları gibi, normal sayfalardan farklı başlık, gezinme menüsü ve gövde yapısı gerektiren alanlar için uygundur
base, page:content çağrısını doğrudan yapmak yerine layout çağırır
<body>
{{template "layout" .}}
</body>
layouts/admin.tmpl, yönetici alanının ortak yapısını tanımlar ve içeride page:content çağırır
- yönetici alanı başlığı
- kullanıcılar ve siparişlere giden gezinme menüsü
- sayfaya özgü içeriğin yer alacağı
<main>
- İşleyici,
render() için kullanılacak düzeni ve sayfa şablonunu birlikte geçirir
app.html.render(
w,
200,
nil,
"base",
"layouts/admin.tmpl",
"pages/admin-orders.tmpl",
)
- Paylaşılan temel şablon korunurken rota bazlı düzenler ve sayfa içeriği birleştirilebilir; böylece yönetici alanı gibi ayrı yapı gerektiren ekranlar da aynı render kalıbıyla genişletilebilir
1 yorum
Hacker News yorumları
Go + HTMX kombinasyonunu seviyor ve şablonlar, bileşenler ve kısmi HTML için tür güvenliğini artırmak amacıyla bunu a-h/templ ile birlikte kullanıyor Go·Unix·SQLite’tan oluşan tüm araç setine GUS stack adını verip paylaşmış: https://housecat.com/blog/the-gus-stack-go-unix-sqlite exe.dev’in GUTS stack’inden büyük ölçüde ilham almış ama TypeScript yerine HTMX kullanıyor: https://exe.dev/docs/guts Stack trace hataları için cockroachdb/errors, tür güvenli HTML için templ, OpenAPI şeması üretimi için fuego, SQL kod üretimi için sqlc, saf Go SQLite için modernc.org/sqlite, migration için goose, kalıcı iş akışları için dbos, Chrome/CDP testleri ve otomasyonu için rod kullanıyor Bu kombinasyon hem doğrudan kod yazarken ya da bir ajanın yardımıyla kod yazarken, hem de tek bir binary build edip deploy etme sürecinde verimli oluyor
go generate ./...komutunu build aşamasının bir parçası olarak kabul edince çok şey kazanılıyor; ayrıca goverter ile sqlc modelleri, şablon nesneleri ve dönüş değerleri arasındaki dönüşümleri de üretiyor Tekrarlayan kodun yaklaşık %50’si otomatik üretiliyor ve tür güvenliği çok iyi oluyorHTMX pek çok kullanım için harika ama ekip arkadaşları aynı fikirde değilse bunu benimsetmek çok zor Bunun ciddi bir teknoloji olmadığı yönünde itirazlar sürüyor ve türlü bug’ların suçu önce HTMX’e atılıyor; sonradan bunun bir yanlış anlama olduğu ortaya çıksa bile güven çoktan zedelenmiş oluyor Çalıştığı en iyi ekipte bile böyle olmuş ve sonuçta hangi savaşın verileceğini seçmek gerektiği dersini çıkarmış Go’nun html/template paketinin arayüzünün de özellikle yapay göründüğünü düşünüyor. 《A Philosophy of Software Design》’ın önerdiği gibi karmaşıklığı içeri itmek gerekirken, her HTML render’ında şablon kopyalamayı dert etmek zorunda bırakan yapı gereksiz yere karmaşık
base.tmpldosyasınıBASE_BEGINveBASE_ENDolarak ayırarak son şablonda kullanmak yeterli Sayfaya özgü başlıkları çalışma anında geçirmek gerekir ama orijinal yaklaşım da çoklu dil desteği başlatılınca zaten sonunda bozulacaktırYakın tarihli bir projede HTMX kullanmaktan çok keyif almış AngularJS ve React öncesi web’i bilen biri olarak, gerçekten sayfa üretip JavaScript miktarını minimumda tutabilmek özellikle hoşuna gidiyor Saf JavaScript ile de yapılabilir ama HTMX tekrarlayan event handling boilerplate’ini üstleniyor Modern frontend felsefesini sevmiyorsanız denemeye değer; resmi sitedeki ilk örnekler oldukça temel ama biraz daha öğrenince çok daha güçlü hale geliyor
Oldukça büyük bir projeyi HTMX + Go ile yapmayı denedim ama yine de yeterli gelmedi; ileride istediğim seviyeye ulaşıp ulaşmayacağından da emin değilim Basit CRUD uygulamaları ve yönetim panelleri için harika, ancak birbirine bağlı bileşenler, paylaşılan durum ve karmaşık etkileşimler arttıkça yönetmesi hızla zorlaşıyor React’tan hoşlanmadığım için HTMX’i seçmiştim ama sonunda backend’i Go’da tutup frontend’i SvelteKit SPA moduna geçirdim; iki tarafı da temizce ayırınca karmaşık UI’leri geliştirmek ve sürdürmek çok daha kolay oldu Svelte, JSX gibi bambaşka bir dilden ziyade HTML’in doğal bir uzantısı gibi hissettiriyor; durum yönetimi ve bileşen modeli basit, yeni
$statesözdizimi de özellikle çok iyiBu aralar yan projelerimin neredeyse tamamını, LLM yardımı olsun ya da olmasın, Go + HTMX ile yazıyorum Opus ve GPT de bu kombinasyonu çok iyi kullanıyor; hızlıca geliştirip başlayabiliyorsunuz, tek bir binary olduğu için dağıtım ve barındırma da rahat Hızlı yinelemeli geliştirme için çok iyi bir stack
HTMX kullanacaksanız, backend tarafında da React’taki gibi ortak HTML parçalarını fonksiyonlara çıkarıp bileşenleştirmeyi kolaylaştıran bir HTML üretim yaklaşımını güçlü biçimde öneririm HTMX, duruma göre belirli HTML’nin hemen yanına etiket ekleyip çıkarmanız gibi backend’in ürettiği HTML’de esneklik gerektiriyor. Örneğin en üst seviyede `` etiketini bulursa sayfa başlığını güncelliyor Geleneksel string tabanlı template motorlarında bu tür işler zordur ama dile gömülü HTML kütüphaneleri ile basittir; ayrıca dilin tüm soyutlama olanaklarını kullanabildiğiniz için HTMX ile özellikle iyi uyum sağlar JavaScript backend’te https://github.com/WebReflection/uhtml-ssr gibi tagged template literal fonksiyonları, Go’da https://www.gomponents.com/, Scala’da ise ScalaTags kullanabilirsiniz
renderToStaticMarkupvar ama sadece backend için yazılmış bir JSX kütüphanesi seçmek muhtemelen daha iyi olurGo ile ilk tanışmam Alex Edwards’ın kitapları ve Learn Go with Tests sayesinde oldu; hâlâ tavsiye ediyorum Eski projelerimden bazılarını HTMX’e dönüştürmeyi denemek istiyorum
Kendi kullanımım için Kotlin + HTMX tabanlı bir framework yaptım: https://github.com/reubenfirmin/zoned Amacım, tüm web uygulamasını uçtan uca tip güvenli hale getirmenin mümkün olup olmadığını görmekti; HTML yazmak için JSX’e benzer bir DSL sunan Kotlinx.html kullandım İlk %90’ını kendim yaptım, son %10 ve README için ise yayını tamamlamak amacıyla Claude’dan yardım aldım
Go’yu da Alex Edwards’ı da seviyorum ama HTMX ile elde ettiğim sonuçlar beni hep hayal kırıklığına uğrattı Kod tabanının karmaşıklığı uygulamanın kendisinden 2 kat daha hızlı büyüyormuş gibi geliyor ve tuhaf geçici çözümler olmadan çıkamadığınız sınır durumlarla hep karşılaşıyorsunuz Node paketlerinden neden hoşlanılmadığını anlıyorum ama HTMX buna verilmiş aşırı bir tepki gibi duruyor. JSON’la uğraşmayarak kazandığınız zamandan 3 kat fazlasını uygulamanın görünümünü ve hissini düzgün hale getirmeye harcıyorsunuz Mantine template ile 2 dakikada kurulum yapıp harika UI bileşenlerinden yararlanabiliyorsunuz; derlenmiş statik asset’leri dahil ederseniz bunu yine tek bir Go binary’si olarak sunabilirsiniz: https://github.com/mantinedev/vite-min-template
Go + Datastar daha basit olduğu için onu çok daha fazla tercih ediyorum