مقدمه

معماری مبتنی بر کامپوننت، یکی از پارادایم‌های اصلی در توسعه وب مدرن است. فریم‌ورک‌هایی مانند React، Vue و Angular همگی بر اساس این ایده ساخته شده‌اند که رابط کاربری را به قطعات کوچک، مستقل و قابل استفاده مجدد تقسیم کنند. Web Components یا «کامپوننت‌های وب»، مجموعه‌ای از استانداردهای وب است که توسط W3C توسعه داده شده و به ما اجازه می‌دهد تا این قابلیت را به صورت بومی (native) و بدون نیاز به هیچ فریم‌ورکی، مستقیماً در مرورگر داشته باشیم.

این تکنولوژی بر سه ستون اصلی استوار است:

  • عناصر سفارشی (Custom Elements): به ما اجازه می‌دهد تا تگ‌های HTML کاملاً جدید با منطق و رفتار سفارشی خودمان را تعریف کنیم.
  • Shadow DOM: یک درخت DOM کپسوله‌شده و جدا برای یک عنصر فراهم می‌کند. استایل‌ها و اسکریپت‌های داخل آن به بیرون نشت نمی‌کنند و استایل‌های صفحه نیز روی آن تأثیر نمی‌گذارند.
  • HTML Templates: تگ‌های <template> و <slot> به ما اجازه می‌دهند تا قطعاتی از markup را تعریف کرده و به صورت پویا از آنها استفاده کنیم.

۱. عناصر سفارشی (Custom Elements)

این API، هسته اصلی کامپوننت‌های وب است. با استفاده از آن، می‌توانیم یک تگ HTML جدید (مثلاً <user-profile>) را به مرورگر معرفی کرده و رفتار آن را با یک کلاس جاوااسکریپت کنترل کنیم.

تعریف و ثبت یک عنصر جدید

برای این کار، ابتدا یک کلاس جاوااسکریپت می‌سازیم که از HTMLElement ارث‌بری می‌کند. سپس، با استفاده از متد customElements.define()، این کلاس را به یک نام تگ سفارشی متصل می‌کنیم. نام تگ سفارشی باید حتماً شامل یک خط تیره (-) باشد تا از تداخل با تگ‌های استاندارد HTML جلوگیری شود.

چرخه حیات (Lifecycle Callbacks)

کلاس عنصر سفارشی ما می‌تواند متدهای خاصی به نام «متدهای چرخه حیات» را پیاده‌سازی کند که در مراحل مختلف عمر یک عنصر به صورت خودکار فراخوانی می‌شوند:

  • connectedCallback(): زمانی فراخوانی می‌شود که عنصر به DOM اصلی سند اضافه شود. این بهترین مکان برای انجام تنظیمات اولیه، دریافت داده یا رندر کردن DOM داخلی است.
  • disconnectedCallback(): زمانی فراخوانی می‌شود که عنصر از DOM حذف شود. این مکان برای پاک‌سازی منابع (مانند حذف شنوندگان رویداد) مناسب است.
  • attributeChangedCallback(name, oldValue, newValue): زمانی فراخوانی می‌شود که یکی از اتریبیوت‌های «مشاهده‌شده» (observed) عنصر تغییر کند.

۲. کپسوله‌سازی با Shadow DOM

یکی از بزرگترین مشکلات در توسعه وب، مدیریت CSS در مقیاس بزرگ است. از آنجایی که CSS به صورت سراسری (global) اعمال می‌شود، احتمال تداخل استایل‌ها بین کامپوننت‌های مختلف بسیار بالاست. Shadow DOM این مشکل را با ایجاد یک «درخت DOM سایه» حل می‌کند. این یک درخت DOM مخفی و جداگانه است که به یک عنصر متصل می‌شود.

استایل‌های تعریف شده در داخل Shadow DOM، فقط به عناصر داخل همان محدوده اعمال می‌شوند و به بیرون نشت نمی‌کنند. به همین ترتیب، استایل‌های صفحه اصلی نیز (به جز برخی پراپرتی‌های قابل ارث‌بری) روی عناصر داخل Shadow DOM تأثیر نمی‌گذارند. برای ایجاد آن، از متد element.attachShadow({ mode: 'open' }) استفاده می‌کنیم. حالت 'open' به این معنی است که جاوااسکریپت صفحه اصلی می‌تواند به Shadow DOM دسترسی داشته باشد.

۳. استفاده از <template> و <slot>

نوشتن کدهای HTML طولانی در قالب رشته‌های جاوااسکریپت کار جالبی نیست. تگ <template> به ما اجازه می‌دهد تا یک قطعه از markup را در HTML تعریف کنیم که توسط مرورگر رندر نمی‌شود و تا زمانی که ما آن را فعال نکنیم، «بی‌اثر» (inert) باقی می‌ماند. ما می‌توانیم محتوای این تمپلیت را با جاوااسکریپت کپی کرده و در کامپوننت خود استفاده کنیم.

تگ <slot> یک ابزار قدرتمند برای ساخت کامپوننت‌های انعطاف‌پذیر است. این تگ به عنوان یک «جایگاه» (placeholder) در داخل Shadow DOM عمل می‌کند. این جایگاه به ما اجازه می‌دهد تا محتوایی را که کاربر در داخل تگ کامپوننت ما قرار می‌دهد (که به آن Light DOM می‌گویند)، در مکان مشخصی از Shadow DOM نمایش دهیم.

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

بیایید تمام این مفاهیم را با ساخت یک کامپوننت <user-card> کنار هم قرار دهیم.

Copy Icon HTML
<!-- 1. Define the template for our component -->
<template id="user-card-template">
    <style>
        .card { border: 1px solid #ccc; padding: 16px; border-radius: 8px; }
        .card h3 { color: navy; margin: 0; }
    </style>
    <div class="card">
        <h3>
            <slot name="username">Default User</slot>
        </h3>
        <p>
            Email: <slot name="email">Not Provided</slot>
        </p>
    </div>
</template>

<!-- 2. Use the custom element in the page -->
<user-card>
    <span slot="username">Alice</span>
    <span slot="email">alice@example.com</span>
</user-card>
Copy Icon JAVASCRIPT
// 3. Define the component's behavior
class UserCard extends HTMLElement {
    constructor() {
        super();
        // Attach a shadow root
        this.attachShadow({ mode: 'open' });
        // Get the template content
        const template = document.getElementById('user-card-template');
        const content = template.content.cloneNode(true);
        // Append the template content to the shadow root
        this.shadowRoot.appendChild(content);
    }
}
// 4. Register the new element
customElements.define('user-card', UserCard);

در این مثال کامل، ابتدا یک <template> با ساختار داخلی و استایل‌های کپسوله‌شده کامپوننت خود تعریف می‌کنیم. در داخل آن، از تگ‌های <slot> با نام‌های مشخص به عنوان جایگاه استفاده کرده‌ایم. سپس در بدنه اصلی صفحه، از کامپوننت <user-card> استفاده کرده و با قرار دادن عناصری با اتریبیوت slot مطابق، محتوای دلخواه را به آن تزریق می‌کنیم. در نهایت، کلاس جاوااسکریپت ما در constructor() خود، این تمپلیت را پیدا، کپی و به Shadow DOM خود اضافه می‌کند و به این ترتیب کامپوننت را رندر می‌کند.

در این درس با تکنولوژی‌های Web Components آشنا شدیم و دیدیم که چگونه می‌توان با ترکیب سه استاندارد Custom Elements، Shadow DOM و HTML Templates، کامپوننت‌های بومی، قابل استفاده مجدد و کپسوله‌شده ساخت. این یک پارادایم قدرتمند در معماری وب است که به ما اجازه می‌دهد رابط‌های کاربری پیچیده را به صورت ماژولار و قابل مدیریت توسعه دهیم. در درس پایانی این فصل، به سراغ `Web Cryptography API` خواهیم رفت تا با ابزارهای استاندارد مرورگر برای انجام عملیات رمزنگاری در سمت کلاینت آشنا شویم.