برچسب الگوی جدید HTML

استانداردسازی قالب سمت مشتری

مقدمه

مفهوم قالب برای توسعه وب جدید نیست. در واقع، زبان‌ها/موتورهای قالب سمت سرور مانند جنگو (پایتون)، ERB/Haml (روبی) و اسمارتی (PHP) برای مدت طولانی وجود داشته‌اند. با این حال، در دو سال گذشته، شاهد انفجار چارچوب‌های MVC بوده‌ایم. همه آنها کمی متفاوت هستند، اما اکثر آنها یک مکانیک مشترک برای ارائه لایه ارائه خود دارند (معروف به نمای دا): الگوها.

بیایید با آن روبرو شویم. قالب ها فوق العاده هستند. برو جلو، از اطراف بپرس. حتی تعریف آن به شما احساس گرما و آرامش می دهد:

"...نیازی نیست که هر بار بازسازی شود..." در مورد شما نمی دانم، اما من دوست دارم از کار اضافی اجتناب کنم. پس چرا پلتفرم وب فاقد پشتیبانی بومی برای چیزی است که توسعه دهندگان به وضوح به آن اهمیت می دهند؟

مشخصات WhatWG HTML Templates پاسخ است. یک عنصر <template> جدید را تعریف می کند که یک رویکرد استاندارد مبتنی بر DOM را برای قالب سمت مشتری توصیف می کند. الگوها به شما این امکان را می‌دهند که بخش‌هایی از نشانه‌گذاری را که به‌عنوان HTML تجزیه می‌شوند، در بارگذاری صفحه بلااستفاده می‌شوند، اما می‌توانند بعداً در زمان اجرا نمونه‌سازی شوند، اعلام کنید. به نقل از رافائل واینستین :

آنها مکانی برای قرار دادن یک دسته بزرگ از HTML هستند که اصلاً نمی خواهید مرورگر به هر دلیلی با آن درگیر شود.

رافائل واینستین (نویسنده مشخصات)

تشخیص ویژگی

برای ویژگی تشخیص <template> ، عنصر DOM را ایجاد کنید و بررسی کنید که ویژگی .content وجود داشته باشد:

function supportsTemplate() {
    return 'content' in document.createElement('template');
}

if (supportsTemplate()) {
    // Good to go!
} else {
    // Use old templating techniques or libraries.
}

اعلام محتوای قالب

عنصر <template> HTML نشان دهنده یک الگو در نشانه گذاری شما است. این شامل "محتوای قالب" است. اساساً تکه های بی اثر DOM قابل شبیه سازی . الگوها را به عنوان قطعات داربستی در نظر بگیرید که می توانید در طول عمر برنامه خود از آنها استفاده کنید (و دوباره استفاده کنید).

برای ایجاد یک محتوای قالب، مقداری نشانه گذاری را اعلام کنید و آن را در عنصر <template> قرار دهید:

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

ستون ها

قرار دادن محتوا در یک <template> چند ویژگی مهم به ما می دهد.

  1. محتوای آن تا زمانی که فعال شود به طور موثر بی اثر است . در اصل، نشانه گذاری شما DOM مخفی است و رندر نمی شود.

  2. هر محتوایی در قالب عوارض جانبی نخواهد داشت. اسکریپت اجرا نمی شود، تصاویر بارگیری نمی شوند، صدا پخش نمی شود ، ... تا زمانی که از الگو استفاده شود.

  3. در نظر گرفته می شود که محتوا در سند نیست . استفاده از document.getElementById() یا querySelector() در صفحه اصلی، گره های فرزند یک الگو را بر نمی گرداند.

  4. الگوها را می توان در هر جایی از <head> ، <body> ، یا <frameset> قرار داد و می تواند حاوی هر نوع محتوایی باشد که در آن عناصر مجاز است. توجه داشته باشید که "هرجا" به این معنی است که <template> می توان با خیال راحت در مکان هایی استفاده کرد که تجزیه کننده HTML اجازه نمی دهد ... همه به جز کودکان مدل محتوا . همچنین می توان آن را به عنوان فرزند <table> یا <select> قرار داد:

<table>
  <tr>
    <template id="cells-to-repeat">
      <td>some content</td>
    </template>
  </tr>
</table>

فعال سازی یک قالب

برای استفاده از قالب، باید آن را فعال کنید. در غیر این صورت محتوای آن هرگز ارائه نخواهد شد. ساده ترین راه برای انجام این کار، ایجاد یک کپی عمیق از .content آن با استفاده از document.importNode() است. ویژگی .content یک DocumentFragment فقط خواندنی است که حاوی اصل قالب است.

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);

پس از چاپ کردن یک الگو، محتوای آن "زنده می شود". در این مثال خاص، محتوا شبیه سازی می شود، درخواست تصویر انجام می شود و نشانه گذاری نهایی ارائه می شود.

دموها

مثال: اسکریپت بی اثر

این مثال بی اثر بودن محتوای قالب را نشان می دهد. <script> فقط زمانی اجرا می شود که دکمه فشار داده شود و الگو را حذف کند.

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

مثال: ایجاد Shadow DOM از یک الگو

اکثر مردم Shadow DOM را با تنظیم یک رشته نشانه گذاری به .innerHTML به یک میزبان متصل می کنند:

<div id="host"></div>
<script>
  var shadow = document.querySelector('#host').createShadowRoot();
  shadow.innerHTML = '<span>Host node</span>';
</script>

مشکل این رویکرد این است که هرچه Shadow DOM شما پیچیده‌تر شود، الحاق رشته‌ها بیشتر است. مقیاس نمی شود، همه چیز سریع به هم می ریزد و نوزادان شروع به گریه می کنند. این رویکرد همچنین نحوه تولد XSS در وهله اول است! <template> برای نجات.

کار منطقی تر این است که مستقیماً با افزودن محتوای قالب به ریشه سایه با DOM کار کنید:

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

گوچاس

در اینجا چند تا گوچا وجود دارد که من هنگام استفاده از <template> در طبیعت با آنها روبرو شده ام:

  • اگر از modpagespeed استفاده می کنید، مراقب این باگ باشید. الگوهایی که <style scoped> درون خطی را تعریف می کنند، بسیاری از آنها با قوانین بازنویسی CSS PageSpeed ​​به قسمت اصلی منتقل می شوند.
  • هیچ راهی برای "پیش اجرا" یک الگو وجود ندارد، به این معنی که شما نمی توانید دارایی ها را از قبل بارگذاری کنید، JS را پردازش کنید، CSS اولیه را دانلود کنید، و غیره. این برای سرور و مشتری صدق می کند. تنها زمانی که یک الگو رندر می شود، زمانی است که به صورت زنده منتشر می شود.
  • مراقب الگوهای تو در تو باشید. آنها آنطور که شما انتظار دارید رفتار نمی کنند. به عنوان مثال:

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

    فعال کردن قالب بیرونی، الگوهای داخلی را فعال نخواهد کرد. به این معنا که الگوهای تو در تو مستلزم این هستند که فرزندانشان نیز به صورت دستی فعال شوند.

راه رسیدن به یک استاندارد

فراموش نکنیم از کجا آمده ایم. راه رسیدن به قالب‌های HTML مبتنی بر استاندارد طولانی بوده است. در طول سال ها، ما به ترفندهای بسیار هوشمندانه ای برای ایجاد قالب های قابل استفاده مجدد رسیده ایم. در زیر دو مورد رایجی که من با آنها برخورد کرده ام آورده شده است. من آنها را برای مقایسه در این مقاله گنجانده ام.

روش 1: DOM خارج از صفحه

یکی از روش‌هایی که مردم برای مدت طولانی استفاده می‌کنند، ایجاد DOM «خارج از صفحه» و پنهان کردن آن از دید با استفاده از ویژگی hidden یا display:none .

<div id="mytemplate" hidden>
  <img src="logo.png">
  <div class="comment"></div>
</div>

در حالی که این تکنیک کار می کند، تعدادی از معایب نیز وجود دارد. خلاصه این تکنیک:

  • با استفاده از DOM - مرورگر DOM را می شناسد. در آن خوب است. ما به راحتی می توانیم آن را شبیه سازی کنیم.
  • هیچ چیزی رندر نمی شود - اضافه کردن hidden از نمایش بلوک جلوگیری می کند.
  • بی اثر نیست - حتی اگر محتوای ما پنهان است، هنوز یک درخواست شبکه برای تصویر ارسال می شود.
  • استایل‌سازی و قالب‌بندی دردناک - یک صفحه جاسازی باید تمام قوانین CSS خود را با #mytemplate پیشوند کند تا سبک‌ها را به قالب محدود کند. این شکننده است و هیچ تضمینی وجود ندارد که در آینده با تداخل نام‌گذاری مواجه نشویم. به عنوان مثال، اگر صفحه جاسازی قبلاً عنصری با آن شناسه داشته باشد، ما شیلنگ هستیم.

روش 2: بارگذاری بیش از حد اسکریپت

تکنیک دیگر بارگذاری <script> و دستکاری محتوای آن به عنوان یک رشته است. جان رسیگ احتمالاً اولین کسی بود که این را در سال 2008 با ابزار Micro Templating خود نشان داد. اکنون بسیاری دیگر، از جمله برخی از بچه های جدید در بلوک مانند handlebars.js وجود دارند.

به عنوان مثال:

<script id="mytemplate" type="text/x-handlebars-template">
  <img src="logo.png">
  <div class="comment"></div>
</script>

خلاصه این تکنیک:

  • هیچ چیزی رندر نمی شود - مرورگر این بلوک را رندر نمی کند زیرا <script> بصورت پیش فرض display:none است.
  • بی اثر - مرورگر محتوای اسکریپت را به عنوان JS تجزیه نمی کند زیرا نوع آن روی چیزی غیر از "text/javascript" تنظیم شده است.
  • مسائل امنیتی - استفاده از .innerHTML را تشویق می کند. تجزیه رشته در زمان اجرا داده های ارائه شده توسط کاربر می تواند به راحتی منجر به آسیب پذیری های XSS شود.

نتیجه گیری

به یاد دارید زمانی که jQuery کار با DOM را ساده کرد؟ نتیجه querySelector() / querySelectorAll() به پلتفرم اضافه شد. برد آشکار، درست است؟ کتابخانه ای واکشی DOM با انتخابگرهای CSS را رایج کرد و استانداردها بعداً آن را پذیرفتند. همیشه اینطور کار نمی کند، اما من عاشق وقتی هستم.

من فکر می کنم <template> یک مورد مشابه است. روشی را که ما قالب‌بندی سمت مشتری انجام می‌دهیم استاندارد می‌کند، اما مهمتر از آن، نیاز به هک‌های سال 2008 ما را از بین می‌برد. ساختن کل فرآیند تالیف وب عاقلانه تر، قابل نگهداری تر، و با ویژگی های کامل تر همیشه یک چیز خوب در کتاب من است.

منابع اضافی