İstemci tarafı şablon oluşturmayı standartlaştırma
Giriş
Şablon oluşturma kavramı, web geliştirmede yeni bir kavram değildir. Aslında Django (Python), ERB/Haml (Ruby) ve Smarty (PHP) gibi sunucu tarafı şablonlama dilleri/motorları uzun zamandır kullanılıyor. Ancak son birkaç yılda MVC çerçevelerinin patlama yaşadığını gördük. Hepsi birbirinden biraz farklı olsa da çoğu, sunum katmanını (yani görünümü) oluşturmak için ortak bir mekanizma kullanır: şablonlar.
Kabul edelim. Şablonlar harika. Sorun. Tanım bile sizi sıcak ve rahat hissettirir:
"…her seferinde yeniden oluşturulması gerekmez…" Sizi bilmem ama ben ek işten kaçınmayı seviyorum. Peki neden web platformunda geliştiricilerin önemsediği bir konu için yerel destek bulunmuyor?
WhatWG HTML Şablonları spesifikasyonu yanıttır. İstemci tarafı şablon oluşturma için standart DOM tabanlı bir yaklaşımı tanımlayan yeni bir <template>
öğesi tanımlar. Şablonlar, HTML olarak ayrıştırılan, sayfa yüklenirken kullanılmayan ancak daha sonra çalışma zamanında oluşturulabilen işaretleme parçalarını tanımlamanıza olanak tanır. Rafael Weinstein'dan alıntı yapmak için:
Tarayıcı tarafından herhangi bir nedenle değiştirilmesini istemediğiniz büyük bir HTML yığınını yerleştirebileceğiniz bir yerdir.
Rafael Weinstein (özet yazarı)
Özellik Algılama
<template>
özelliğini algılamak için DOM öğesini oluşturun ve .content
mülkünün mevcut olup olmadığını kontrol edin:
function supportsTemplate() {
return 'content' in document.createElement('template');
}
if (supportsTemplate()) {
// Good to go!
} else {
// Use old templating techniques or libraries.
}
Şablon içeriğini belirtme
HTML <template>
öğesi, işaretlemenizdeki bir şablonu temsil eder. Esasen klonlanabilir DOM parçaları olan "şablon içerikleri"ni içerir.
Şablonları, uygulamanızın kullanım ömrü boyunca kullanabileceğiniz (ve yeniden kullanabileceğiniz) iskele parçaları olarak düşünebilirsiniz.
Şablonlu içerik oluşturmak için biraz işaretleme tanımlayıp bunu <template>
öğesine sarmalayın:
<template id="mytemplate">
<img src="" alt="great image">
<div class="comment"></div>
</template>
Temeller
İçeriği <template>
içine sarmalamak bize birkaç önemli özellik sağlar.
İçeriği, etkinleştirilene kadar etkin değildir. Temel olarak, işaretlemeniz gizli DOM'dur ve oluşturulmaz.
Şablondaki içeriklerin yan etkisi yoktur. Şablon kullanılana kadar komut dosyası çalışmaz, resimler yüklenmez, ses çalmaz.
İçeriğin dokümanda olmadığı kabul edilir. Ana sayfada
document.getElementById()
veyaquerySelector()
kullanıldığında bir şablonun alt düğümleri döndürülmez.Şablonlar;
<head>
,<body>
veya<frameset>
içinde herhangi bir yere yerleştirilebilir ve bu öğelerde izin verilen her türlü içeriği barındırabilir. "Her yerde" ifadesinin,<template>
öğesinin HTML ayrıştırıcının izin vermediği yerlerde (içerik modeli alt öğeleri hariç) güvenli bir şekilde kullanılabileceği anlamına geldiğini unutmayın. Ayrıca,<table>
veya<select>
alt öğesi olarak da yerleştirilebilir:
<table>
<tr>
<template id="cells-to-repeat">
<td>some content</td>
</template>
</tr>
</table>
Şablon etkinleştirme
Şablonları kullanmak için etkinleştirmeniz gerekir. Aksi takdirde içeriği hiçbir zaman oluşturulmaz.
Bunu yapmanın en basit yolu, document.importNode()
kullanarak .content
öğesinin derin kopyasını oluşturmaktır. .content
mülkü, şablonun temel öğelerini içeren salt okunur bir DocumentFragment
öğesidir.
var t = document.querySelector('#mytemplate');
// Populate the src at runtime.
t.content.querySelector('img').src = 'logo.png';
var clone = document.importNode(t.content, true);
document.body.appendChild(clone);
Bir şablon oluşturulduktan sonra içeriği "yayınlanır". Bu örnekte, içerik klonlanır, resim isteği yapılır ve son işaretleme oluşturulur.
Demolar
Örnek: Etkin olmayan komut dosyası
Bu örnekte, şablon içeriğinin hareketsizliği gösterilmektedir. <script>
yalnızca düğmeye basıldığında çalışır ve şablonu damgalayarak çıkarır.
<button onclick="useIt()">Use me</button>
<div id="container"></div>
<script>
function useIt() {
var content = document.querySelector('template').content;
// Update something in the template DOM.
var span = content.querySelector('span');
span.textContent = parseInt(span.textContent) + 1;
document.querySelector('#container').appendChild(
document.importNode(content, true)
);
}
</script>
<template>
<div>Template used: <span>0</span></div>
<script>alert('Thanks!')</script>
</template>
Örnek: Şablondan gölge DOM oluşturma
Çoğu kullanıcı, bir işaretleme dizesini .innerHTML
olarak ayarlayarak Gölge DOM'u bir barındırıcıya ekler:
<div id="host"></div>
<script>
var shadow = document.querySelector('#host').createShadowRoot();
shadow.innerHTML = '<span>Host node</span>';
</script>
Bu yaklaşımın sorunu, Gölge DOM'unuz ne kadar karmaşık olursa o kadar fazla dize birleştirme işlemi yapmanızdır. Ölçeklendirilemez, işler hızla karmaşık hale gelir ve bebekler ağlamaya başlar. XSS, bu yaklaşım sayesinde ilk kez ortaya çıktı. <template>
yardımınıza koşar.
Şablon içeriğini bir gölge köküne ekleyerek DOM ile doğrudan çalışmak daha mantıklı olabilir:
<template>
<style>
:host {
background: #f8f8f8;
padding: 10px;
transition: all 400ms ease-in-out;
box-sizing: border-box;
border-radius: 5px;
width: 450px;
max-width: 100%;
}
:host(:hover) {
background: #ccc;
}
div {
position: relative;
}
header {
padding: 5px;
border-bottom: 1px solid #aaa;
}
h3 {
margin: 0 !important;
}
textarea {
font-family: inherit;
width: 100%;
height: 100px;
box-sizing: border-box;
border: 1px solid #aaa;
}
footer {
position: absolute;
bottom: 10px;
right: 5px;
}
</style>
<div>
<header>
<h3>Add a Comment
</header>
<content select="p"></content>
<textarea></textarea>
<footer>
<button>Post</button>
</footer>
</div>
</template>
<div id="host">
<p>Instructions go here</p>
</div>
<script>
var shadow = document.querySelector('#host').createShadowRoot();
shadow.appendChild(document.querySelector('template').content);
</script>
Dikkat edilmesi gereken noktalar
<template>
'ü gerçek hayatta kullanırken karşılaştığım bazı sorunlar aşağıda verilmiştir:
- modpagespeed kullanıyorsanız bu hataya dikkat edin. Satır içi
<style scoped>
öğesini tanımlayan şablonlar, PageSpeed'in CSS yeniden yazma kurallarıyla kolayca fark yaratır. - Bir şablonu "önceden görüntülemenin" bir yolu yoktur. Yani öğeleri önceden yükleyemez, JS'yi işleyemez, ilk CSS'yi indiremezsiniz vb. Bu durum hem sunucu hem de istemci için geçerlidir. Bir şablon yalnızca yayınlandığı zaman oluşturulur.
İç içe yerleştirilmiş şablonlara dikkat edin. Beklediğiniz gibi davranmazlar. Örneğin:
<template> <ul> <template> <li>Stuff</li> </template> </ul> </template>
Dış şablon etkinleştirildiğinde iç şablonlar etkin olmaz. Yani iç içe yerleştirilmiş şablonların alt şablonlarının da manuel olarak etkinleştirilmesi gerekir.
Standartlaşma yolunda
Nereden geldiğimizi unutmayalım. Standart tabanlı HTML şablonlarına ulaşmak uzun bir süreçti. Yıllar içinde, yeniden kullanılabilir şablonlar oluşturmak için oldukça akıllıca bazı hileler geliştirdik. Aşağıda, sık karşılaşılan iki örnek verilmiştir. Karşılaştırma için bu makaleye ekledim.
1. yöntem: Ekran dışı DOM
Kullanıcıların uzun süredir kullandığı bir yaklaşım, "ekran dışı" DOM oluşturmak ve hidden
özelliğini veya display:none
özelliğini kullanarak bunu gizlemektir.
<div id="mytemplate" hidden>
<img src="logo.png">
<div class="comment"></div>
</div>
Bu teknik işe yarasa da bazı dezavantajları vardır. Bu tekniğin özeti:
- DOM'u kullanma: Tarayıcı DOM'u bilir. Bu konuda iyidir. Kolayca klonlayabiliriz.
- Hiçbir şey oluşturulmaz:
hidden
eklemek, bloğun gösterilmesini engeller. - Eylemsiz değil: İçeriğimiz gizli olsa bile resim için yine de bir ağ isteği yapılır.
- Zorlu stil ve tema oluşturma: Stillerin kapsamını şablona indirgemek için yerleşik sayfanın tüm CSS kurallarına
#mytemplate
ön eklenmesi gerekir. Bu yöntem hassas olabilir ve gelecekte adlandırma çakışmalarıyla karşılaşmayacağımızın bir garantisi yoktur. Örneğin, yerleştirme sayfasında bu kimliğe sahip bir öğe varsa sorun yaşarız.
2. Yöntem: Aşırı yükleme komut dosyası
<script>
işlevini aşırı yüklemek ve içeriğini dize olarak değiştirmek de bir tekniktir. John Resig, Micro Templating yardımcı programı ile bunu 2008'de ilk gösteren kişi oldu.
Artık handlebars.js gibi yeni seçenekler de dahil olmak üzere birçok seçenek mevcut.
Örneğin:
<script id="mytemplate" type="text/x-handlebars-template">
<img src="logo.png">
<div class="comment"></div>
</script>
Bu tekniğin özeti:
- Hiçbir şey oluşturulmaz:
<script>
varsayılan olarakdisplay:none
olduğundan tarayıcı bu bloğu oluşturmaz. - Etkisiz: Türü "text/javascript" dışında bir değere ayarlandığı için tarayıcı, komut dosyası içeriğini JS olarak ayrıştırmaz.
- Güvenlik sorunları -
.innerHTML
kullanımını teşvik eder. Kullanıcı tarafından sağlanan verilerin çalışma zamanında dize ayrıştırması, XSS güvenlik açıklarına kolayca neden olabilir.
Sonuç
jQuery'nin DOM ile çalışmayı çok basit hale getirdiğini hatırlıyor musunuz? Sonuç olarak querySelector()
/querySelectorAll()
platforma eklendi. Açık bir kazanç, değil mi? Daha sonra CSS seçiciler ve standartlarla DOM getirmeyi popüler hale getiren bir kitaplık. Bu her zaman mümkün olmasa da işe yaradığında çok mutlu olurum.
<template>
'ün de benzer bir durum olduğunu düşünüyorum. Bu özellik, istemci tarafı şablon oluşturma yöntemimizi standartlaştırır ancak daha da önemlisi, 2008'deki hilelerimize olan ihtiyacı ortadan kaldırır.
Web içeriği oluşturma sürecinin tamamını daha mantıklı, daha sürdürülebilir ve daha çok özellikli hale getirmek benim açımdan her zaman iyi bir şeydir.
Ek kaynaklar
- WhatWG Spesifikasyonu
- Web bileşenlerine giriş
- <web>components</web> (video) - sizden muhteşem bir sunum.