HTML's Yeni Şablon Etiketi

İ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.

Bunu 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 çalışmalardan 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'in dediği gibi:

Tarayıcı tarafından herhangi bir nedenle değiştirilmesini istemediğiniz büyük bir HTML paketini 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. "Şablon içerikleri" içerir. Bunlar temel olarak klonlanabilir DOM'un hareketsiz parçalarıdır. Ş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 bazı işaretlemeler tanımlayın ve bunları <template> öğesine sarın:

<template id="mytemplate">
    <img src="" alt="great image">
    <div class="comment"></div>
</template>

Sütunlar

İçeriği <template> içine sarmalamak bize birkaç önemli özellik sağlar.

  1. İçeriği etkinleştirilene kadar etkin değildir. Temel olarak, işaretlemeniz gizli DOM'dur ve oluşturulmaz.

  2. Şablondaki içeriklerin yan etkisi olmaz. Şablon kullanılana kadar komut dosyası çalışmaz, resimler yüklenmez, ses çalmaz.

  3. İçeriğin dokümanda olmadığı kabul edilir. Ana sayfada document.getElementById() veya querySelector() kullanıldığında bir şablonun alt düğümleri döndürülmez.

  4. Şablonlar <head>, <body> veya <frameset> öğelerinin herhangi bir yerine yerleştirilebilir ve bu öğelerde izin verilen her tür içeriği içerebilir. "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. <table> veya <select> öğesinin 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 yapısını 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 nihai 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 çabuk karmaşık hale gelir ve bebekler ağlamaya başlar. XSS de bu yaklaşımla ortaya çıkmıştır. <template> yardımınıza koşar.

Daha mantıklı bir yaklaşım, şablon içeriğini gölge köke ekleyerek doğrudan DOM ile çalışmaktır:

<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> tanımlayan şablonlar, PageSpeed'in CSS yeniden yazma kurallarıyla üstbilgiye taşınabilir.
  • Bir şablonu "ön oluşturma"nın hiçbir yolu yoktur. Diğer bir deyişle, öğeleri önceden yükleyemez, JS'yi işleyemez, ilk CSS'yi indiremezsiniz. Bu durum hem sunucu hem de istemci için geçerlidir. Şablonlar yalnızca yayınlandığında oluşturulur.
  • İç içe yerleştirilmiş şablonlarla ilgili dikkatli olun. Beklendiğiniz gibi davranmazlar. Örneğin:

    <template>
      <ul>
        <template>
          <li>Stuff</li>
        </template>
      </ul>
    </template>
    

    Dış şablon etkinleştirildiğinde iç şablonlar etkinleştirilmez. 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 yoldu. 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 yarar olsa 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.
  • Etkisiz değil: İçeriğimiz gizli olsa bile görsel için bir ağ isteği gönderilir.
  • 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 hassastır ve gelecekte ad çakışması ile karşılaşmayacağımız garanti edilmez. Örneğin, yerleştirme sayfasında bu kimliğe sahip bir öğe varsa işimiz zordur.

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 olarak display:none olduğundan tarayıcı bu bloğu oluşturmaz.
  • Inert: 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. Bu, kazançlı bir durum, değil mi? CSS seçicileriyle DOM getirmeyi popülerleştiren bir kitaplık ve daha sonra standartlar tarafından benimsendi. 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