10 puan yazan GN⁺ 2025-04-23 | 3 yorum | WhatsApp'ta paylaş
  • Writing JavaScript Views the Hard Way: framework olmadan, yalnızca saf JavaScript ile görünüm oluşturma yöntemini anlatan bir yazı
  • Doğrudan imperative yaklaşım sayesinde performans, bakım kolaylığı ve taşınabilirlik sağlanır
  • Durum güncellemeleri ile DOM güncellemeleri net biçimde ayrılır ve her rol için katı adlandırma kuralları ve yapısal kalıplar izlenir
  • Bu yaklaşımın büyük avantajları arasında hata ayıklamanın kolay olması, tüm tarayıcılarla uyumluluk ve 0 dependencies bulunur
  • Yeni başlayanlar için zor olabilir, ancak öğrenildiğinde gerçek sistemlerin nasıl çalıştığına dair derin bir anlayış kazandırır

JavaScript görünümlerini 'Hard Way' ile yazmak

Bu nedir?

  • Bu yaklaşım, React, Vue, lit-html gibi framework'ler olmadan yalnızca JavaScript ile görünüm kurma kalıbıdır
  • Belirli bir kütüphane ya da araç değil, kodlama kalıbının kendisidir ve spaghetti code sorununu önler
  • Doğrudan imperative yaklaşım kullanarak soyutlamayı azaltır ve sezgiselliği artırır

Framework'lere kıyasla avantajları

  • Performans: Imperative kod sayesinde gereksiz işlemler olmadan çalışır; hem hot path hem de cold path için uygundur
  • 0 dependencies: Kütüphane yükseltmeleri veya uyumluluk sorunlarından bağımsızdır
  • Taşınabilirlik: Yazılan kod herhangi bir framework'e taşınabilir
  • Bakım kolaylığı: Net bölüm yapısı ve adlandırma kuralları sayesinde kodun yerini bulmak kolaydır
  • Tarayıcı desteği: IE9 ve üzeri çoğu tarayıcıyla uyumludur; bazı düzenlemelerle IE6'ya kadar da desteklenebilir
  • Kolay hata ayıklama: Ara katmanlar olmadan sığ stack trace sağlar
  • Fonksiyonel yapı: Immutable değildir ama tüm bileşenler fonksiyon tabanlıdır

Yapının açıklaması

Genel yapı

  • templateclone()init() fonksiyonlarından oluşur
  • init() fonksiyonu; durum değişkenleri, DOM referansları, güncelleme fonksiyonları, event listener'lar gibi öğeleri içeren tek bir görünüm örneği oluşturur

Örnek kod yapısı (Hello World)

const template = document.createElement('template');  
template.innerHTML = `<div>Hello <span id="name">world</span>!</div>`;  
  
function clone() {  
  return document.importNode(template.content, true);  
}  
  
function init() {  
  let frag = clone();  
  let nameNode = frag.querySelector('#name');  
  let name;  
  
  function setNameNode(value) {  
    nameNode.textContent = value;  
  }  
  
  function setName(value) {  
    if(name !== value) {  
      name = value;  
      setNameNode(value);  
    }  
  }  
  
  function update(data = {}) {  
    if(data.name) setName(data.name);  
    return frag;  
  }  
  
  return update;  
}  

init() fonksiyonunun iç yapısı

1. DOM değişkenleri

  • frag, clone() tarafından üretilen template parçasıdır
  • İç öğelere querySelector() ile erişilir ve değişken adları fooNode biçiminde kullanılır

2. DOM görünümü

  • Diğer görünümleri içeren bölüm (yeniden kullanılabilir alt görünüm)
  • Örnek:
let updateChildView = childView();  
  • Görünüm güncelleme fonksiyonları updateFoo biçiminde adlandırılır

3. Durum değişkenleri

  • Görünüm içinde değişebilen veri değerleri
  • DOM güncellemelerini verimli yapmak için mevcut değerle karşılaştırılır ve yalnızca gerektiğinde DOM değiştirilir

4. DOM güncelleme fonksiyonları

  • DOM öğelerinin durumunu değiştirmek için kullanılır
  • Örnek:
function setNameNode(value) {  
  nameNode.textContent = value;  
}  
  • DOM manipülasyonu yalnızca bu fonksiyonların içinde yapılmalıdır

5. Durum güncelleme fonksiyonları

  • Durum değiştirme mantığını ve buna karşılık gelen DOM yansıtmasını içerir
  • Değişmemiş değerler göz ardı edilerek gereksiz DOM değişiklikleri önlenir
  • Örnek:
function setName(value) {  
  if(name !== value) {  
    name = value;  
    setNameNode(value);  
  }  
}  

template ve clone() fonksiyonları

template

  • <template> öğesiyle statik HTML yapısı oluşturulur
  • Doğrudan DOM'a eklenmez; clone üzerinden kopyası üretilir

clone()

  • document.importNode(template.content, true) ile çoğaltılır
  • Gerekirse .firstElementChild kullanılarak kök öğe döndürülebilir

Etkileşim biçimi

Ebeveyn → çocuk veri akışı

  • Ebeveyn, çocuğun init() fonksiyonunu çağırarak güncelleme fonksiyonunu alır ve bunu update({ name: 'foo' }) biçiminde çağırır

Event tabanlı veri iletimi

  • Temel olarak props down, events up modelini izler
  • Alt görünümler, iletişim kurmak için event'leri üst katmana dispatch eder

React ile karşılaştırma

  • constructor() (React)init() (Hard Way)
    • Bileşenin ilk kurulumundan sorumludur
  • render() (React)update(data) (Hard Way)
    • Ekranı yenileme ve UI güncelleme görevini üstlenir
  • this.setState() (React)setX(value) (Hard Way)
    • Durum değerini doğrudan ayarlayan yöntemle değiştirilir
  • props (React)update(data) ile aktarılan değerler (Hard Way)
    • Üst bileşenden gelen verilerin işlenme biçimi
  • JSX / Virtual DOM (React)HTML template + DOM API (Hard Way)
    • Deklaratif UI yerine manuel DOM manipülasyonu ve template kullanılır

Sonuç

  • Bu yaklaşım, alışılmış framework'lere kıyasla ilk giriş eşiği daha yüksek olsa da şu güçlü yönlere sahiptir:
    • Performans optimizasyonu
    • Tam kontrol
    • Öğrenme yoluyla derin kavrayış
  • Rol bazlı fonksiyon ayrımı ve adlandırma kuralları sayesinde, framework olmadan da bakımı yapılabilir bir UI oluşturmak mümkündür

Uyumluluk

  • Güncel örnekler modern tarayıcı API'lerini kullanır, ancak IE9 ve altına kadar da fonksiyon tabanlı alternatiflerle destek sağlanabilir
  • Event yerine props ile fonksiyon aktarma yaklaşımı kullanılarak IE6'ya kadar genişletilebilir

3 yorum

 
wfedev 2025-04-24

Sonunda web bileşenlerine...

 
ahwjdekf 2025-04-23

Tebrikler. Bir başka js framework'ü daha doğdu.

 
GN⁺ 2025-04-23
Hacker News yorumu
  • Birçok JS geliştiricisi için sapkınlık sayılabilir ama state değişkenlerinin bir anti-pattern olduğunu düşünüyorum

    • Web Components kullanıyorum ve 'düz' değişken türleri için state değişkenleri eklemek yerine value/textContent/checked gibi DOM öğesi özelliklerini tek gerçek kaynağı olarak kullanıyorum
    • Gerektiğinde setter ve getter ekliyorum
    • Az miktarda kodla bile doğal olarak birçok şeyin doğru çalıştığını görüyorum
    • WebComponents kullanınca nesne ile ona bitişik HTML şablonu ayrışıyor; ortaya spagetti kod değil, daha çok fusilli ya da makarna benzeri bir parçalanma çıkıyor
  • Belgelerde bu yaklaşımın çok bakımı kolay olduğu söyleniyor ama buna katılmıyorum

    • Tasarım deseni tamamen teamüle dayanıyor
    • Karmaşık bir uygulamada birden fazla geliştirici aynı anda çalışıyorsa, en az birinin bu teamülün dışına çıkması muhtemel
    • iOS'un UIKit'i gibi sınıf tabanlı UI framework'leri, tüm geliştiricileri standart bir API kümesi kullanmaya zorlayarak kodu öngörülebilir ve bakımı kolay hale getiriyor
  • Son zamanlarda vite ile birlikte saf 'vanilla' TypeScript kullanarak uygulama yazıyorum ve frontend 'en iyi' pratiklerini giderek daha fazla sorguluyorum

    • Ölçeklenebilirlik konusunda kesin bir yargıya varamam ama performans açısından büyük avantajı var
    • Eğlenceli, çok şey öğreniyorum, debug etmek basit ve mimariyi anlamak kolay
    • En çok özlediğim şey templating
  • Bu yaklaşım bana eski backbone js kütüphanesini hatırlatıyor

    • Web platformuna uyarlanmış MVC desenine dair örnekler içeren bir GitHub deposu da var
  • Kısa süre önce benzer bir şey düşündüm ama template öğeleri kullanmıyorum

    • Fonksiyonlar ve template literal'lar kullanarak string döndürüyorum; sonra bunu mevcut bir öğenin innerHTML'ine koyuyor ya da yeni bir div öğesi oluşturup içine yerleştiriyorum
    • Fonksiyonlar iç içe geçiyor ve bunu makul bir şekilde organize etmek zorlaşıyor
  • Bu kod, reaktif view kütüphanelerinin yerine geçmeye çalıştığı manuel güncelleme kodunun aynısı gibi görünüyor

  • Yaklaşık 20 yıldır programlama yapıyorum ama frontend framework'lerine bir türlü alışamadım

    • Backend tarafında daha güçlüyüm, bu yüzden güvenlikle ilgili etkileşimlerin sunucu üzerinden geçmesi gerektiğini düşünüyorum
    • JS'yi, sağlam bir HTML ve CSS temelinin üstüne istemci tarafı işlevsellik eklemek olarak görüyorum
  • React.createElement benzeri bir helper kullanıyorum

    • Sahte bir sunucu dashboard'unun çalışan bir örneği var
  • HTML tabanlı araçlar için bir JS toolkit oluşturmaya yönelik bir girişim olarak deja-vu.junglecoder.com üzerinde çalışıyorum

    • Uygun reactivity/iki yönlü veri bağlama kısmını hâlâ çözemedim ama grab/patch oldukça iyi durumda
    • Template kullanma biçimi, template'in bazı kısımlarını taşımayı çok kolaylaştırıyor
  • Üniversiteden mezun olduktan sonraki ilk resmi işimde Delphi yazılımının web sürümünü yapıyordum

    • Ekip frontend'i zaten üçüncü kez baştan yazıyordu ve framework değiştirmeleri gerekiyordu
    • Kendi framework'ümüzü yazmamız gerektiğini savundum ama ekip önerimi beğenmedi
    • Sonra başka bir şirketten daha iyi bir teklif alıp ayrıldım
    • Daha sonra tiny.js adlı başka bir framework denedim ve onu kişisel projelerimde kullanıyorum