- 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ı
template → clone() → 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
Sonunda web bileşenlerine...
Tebrikler. Bir başka js framework'ü daha doğdu.
Hacker News yorumu
Birçok JS geliştiricisi için sapkınlık sayılabilir ama
statedeğişkenlerinin bir anti-pattern olduğunu düşünüyorumvalue/textContent/checkedgibi DOM öğesi özelliklerini tek gerçek kaynağı olarak kullanıyorumBelgelerde bu yaklaşımın çok bakımı kolay olduğu söyleniyor ama buna katılmıyorum
Son zamanlarda vite ile birlikte saf 'vanilla' TypeScript kullanarak uygulama yazıyorum ve frontend 'en iyi' pratiklerini giderek daha fazla sorguluyorum
Bu yaklaşım bana eski backbone js kütüphanesini hatırlatıyor
Kısa süre önce benzer bir şey düşündüm ama template öğeleri kullanmıyorum
innerHTML'ine koyuyor ya da yeni birdivöğesi oluşturup içine yerleştiriyorumBu 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
React.createElementbenzeri bir helper kullanıyorumHTML tabanlı araçlar için bir JS toolkit oluşturmaya yönelik bir girişim olarak deja-vu.junglecoder.com üzerinde çalışıyorum
grab/patcholdukça iyi durumdaÜniversiteden mezun olduktan sonraki ilk resmi işimde Delphi yazılımının web sürümünü yapıyordum
tiny.jsadlı başka bir framework denedim ve onu kişisel projelerimde kullanıyorum