Firefox 135 در ۴ فوریهٔ ۲۰۲۵ منتشر شد، اما خبر «پشتیبانی کامل از Web Components v2» توصیف دقیقی از آن انتشار نیست. Web Components مجموعهای از قابلیتهای جداگانه است که هرکدام مسیر استانداردسازی و جدول سازگاری خود را دارند. در یادداشت توسعهدهندگان Firefox 135 چنین بستهای معرفی نشده است. بنابراین برای تصمیم دربارهٔ استفاده از یک قابلیت، باید نام همان API و نسخهٔ مرورگر مقصد را بررسی کرد؛ شمارهٔ بزرگ یک مرورگر بهتنهایی پاسخ این سؤال را نمیدهد.
این اصلاح برای تیمی که یک Design System مشترک میسازد کاربرد عملی دارد. ممکن است کارت محصول با Custom Elements معمولی درست کار کند، اما یک روش تازه برای ثبت نام کامپوننتها در مرورگر دیگری در دسترس نباشد. اگر این دو را یک قابلیت واحد فرض کنیم، یک تصمیم درست دربارهٔ کارت ساده به مجوز نادرست برای استفاده از همهٔ APIهای مرتبط تبدیل میشود. بهتر است قرارداد کامپوننت و قابلیت لازم برای اجرای آن جدا نوشته شوند.
Web Components چه مسئلهای را حل میکند؟
فرض کنید سه بخش یک سازمان با ابزارهای متفاوت ساخته شدهاند و همگی به یک کارت معرفی دوره نیاز دارند. کپی کردن HTML و CSS در هر پروژه ساده شروع میشود، ولی اصلاح فاصلهها یا متن دکمه بعداً در چند محل تکرار خواهد شد. یک Custom Element اجازه میدهد کارت با یک نام مشخص وارد صفحه شود و رفتار خود را همراه بیاورد. معرفی Web Components در MDN این مجموعه را با Custom Elements، Shadow DOM و HTML templates توضیح میدهد.
Custom Element نام و رفتار عنصر را تعریف میکند. Shadow DOM مرزی برای ساختار و سبکهای داخلی فراهم میکند. template یک قطعهٔ قابل استفادهٔ مجدد نگه میدارد و slot محل نمایش محتوایی است که صفحه به کامپوننت میدهد. لازم نیست هر کامپوننت از تمام این ابزارها استفاده کند. برای یک نمایشگر ساده، گاهی Custom Element بدون Shadow DOM کافی است. انتخاب مرز داخلی باید با نیاز واقعی به جداسازی سبک و ساختار مرتبط باشد.
Web Components جای قرارداد محصول را نمیگیرد. هنوز باید معلوم باشد عنوان کارت از کجا میآید، دکمه چه کاری انجام میدهد و محتوای خالی چگونه نمایش داده میشود. یک نام سفارشی در HTML این پرسشها را حل نمیکند. مزیت زمانی دیده میشود که چند مصرفکننده با نیازهای مشابه، یک قرارداد کوچک و پایدار داشته باشند. اگر هر صفحه رفتار کاملاً متفاوتی میخواهد، قراردادن همهٔ آن رفتارها داخل یک عنصر عمومی به پیچیدگی تازهای منجر میشود.
یک نمونهٔ مستقل برای فهم مرز کامپوننت
نمونهٔ زیر کارت معرفی یک دوره است. تمام HTML، CSS و JavaScript در همان فایل قرار دارد و به package، تصویر، فونت خارجی یا API نیاز ندارد. آن را با نام course-card.html ذخیره کنید و با مرورگری که Custom Elements و Shadow DOM را پشتیبانی میکند باز کنید. دو کارت، یک تعریف مشترک دارند؛ عنوان و توضیح هر کارت در HTML صفحه نوشته شدهاند و slot آنها را داخل ساختار کارت نمایش میدهد.
<!doctype html>
<html lang="fa" dir="rtl">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>نمونه کارت دوره</title>
<style>body{font-family:system-ui;max-width:48rem;margin:2rem auto;padding:1rem}tara-course{display:block;margin-block:1rem}</style>
<tara-course><h2 slot="title">آشنایی با HTML</h2><p>تمرین ساخت صفحه با عناصر معنایی.</p></tara-course>
<tara-course><h2 slot="title">تمرین CSS</h2><p>ساخت یک چیدمان ساده برای محتوا.</p></tara-course>
<script>
class TaraCourse extends HTMLElement {
constructor() {
super();
const root = this.attachShadow({mode:'open'});
root.innerHTML = `<style>
article{border:1px solid #888;border-radius:12px;padding:1rem}
button{font:inherit;padding:.5rem 1rem;cursor:pointer}
button:focus-visible{outline:3px solid #1648aa;outline-offset:3px}
</style><article><slot name="title"></slot><slot></slot>
<button type="button">نمایش وضعیت</button><p role="status"></p></article>`;
root.querySelector('button').addEventListener('click', () => {
root.querySelector('[role="status"]').textContent = 'این نمونه ثبتنام واقعی انجام نمیدهد.';
});
}
}
customElements.define('tara-course', TaraCourse);
</script>
</html>
در این مثال خود عنصر tara-course یک host است و ساختار داخلی آن در Shadow DOM قرار دارد. selector مربوط به article در CSS داخلی، همان article ساختهشده در template را انتخاب میکند. هیچ class فرضی بیرون از نمونه لازم نیست. دکمه یک button واقعی است؛ تبدیل عنصر تزئینی به دکمه با رویداد کلیک، رفتار صفحهکلید و معنای لازم برای ابزارهای کمکی را خودبهخود ایجاد نمیکند. رفتار سادهٔ مثال فقط متن وضعیت را عوض میکند و ثبتنام واقعی انجام نمیدهد.
متن توضیح دوره در Light DOM میماند؛ یعنی همان بخش معمولی صفحه که نویسندهٔ HTML کنترل میکند. این انتخاب برای کارت محتوایی مناسب است، چون محتوا به یک فایل JavaScript پنهان منتقل نشده است. اگر تعریف عنصر اجرا نشود، عنوان و توضیح همچنان در صفحه وجود دارند. چنین fallback محدودی همهٔ نیازهای محصول را پوشش نمیدهد، اما از خالی شدن کامل محتوای نمونه جلوگیری میکند. برای عملیات اصلی سایت باید رفتار بدون JavaScript را جدا طراحی کرد.
چرخهٔ عمر را با عمر صفحه اشتباه نگیرید
کامپوننت ممکن است به DOM اضافه شود، حذف شود و دوباره برگردد. این اتفاق در فهرستهای پویا و جابهجایی بخشهای صفحه معمول است. پس کدی که هنگام اتصال اجرا میشود، لزوماً فقط یک بار در عمر عنصر اجرا نخواهد شد. در نمونه، ایجاد Shadow DOM داخل constructor انجام شده و listener دکمه نیز همانجا ثبت میشود. connectedCallback برای ساخت دوبارهٔ کارت استفاده نشده تا اتصال مجدد، listenerهای تکراری نسازد.
اگر نسخهٔ بعدی کارت به رویداد window، timer یا اتصال خارجی وابسته شد، مسئولیت جمعکردن آن وابستگیها را مشخص کنید. حذف عنصر از صفحه به معنی فراموش شدن هر مرجعی که کد نگه داشته نیست. مثلاً listener روی window ممکن است همچنان به عنصر اشاره کند. راهنمای چرخهٔ عمر Custom Elements محل callbackهای مربوط را توضیح میدهد. برای مثال حاضر چنین منبع خارجیای وجود ندارد و افزودن cleanup نمایشی فقط خواندن کد را سختتر میکند.
از ابتدا تعیین کنید کدام ورودی ثابت است و کدام ورودی بعد از ساخت تغییر میکند. اگر عنوان از محتوای slot میآید، تغییر متن همان محتوا مسیر طبیعی است. اگر ورودی با attribute کنترل میشود، باید تغییر آن attribute را نیز در رفتار کامپوننت در نظر گرفت. مخلوط کردن چند مسیر بدون اولویت مشخص دردسرساز است؛ مثلاً عنوانی که هم از property و هم از attribute و هم از slot خوانده میشود، هنگام اختلاف ورودیها نیاز به تصمیم روشن دارد.
Shadow DOM و امنیت دو موضوع جدا هستند
Shadow DOM برای جلوگیری از برخورد ناخواستهٔ selectorها مفید است، اما مرز امنیتی قابل اتکا برای مخفی کردن اطلاعات حساس نیست. نباید رمز، token یا دادهٔ محرمانه را به امید دور ماندن از دسترس در ساختار داخلی یک کامپوننت قرار داد. محتوایی که مرورگر دریافت میکند در اختیار همان محیط اجرای کاربر قرار دارد. حالت closed نیز جای کنترل دسترسی در سرور را نمیگیرد؛ بررسی مجوز باید در محل دریافت و تغییر داده انجام شود.
برای ورودی کاربران، نمونهٔ آموزشی را با یک قالبساز بیحساب جایگزین نکنید. template حاضر ثابت است و توضیح دوره در HTML نوشته شده است. اگر بعداً اطلاعات از یک پاسخ شبکه بیاید، قرار دادن متن با textContent با تفسیر کردن رشته بهعنوان HTML تفاوت دارد. دادهٔ HTML نیاز به سیاست مشخص و پاکسازی متناسب با کاربرد دارد. وجود Shadow DOM این مسئولیت را حذف نمیکند و نباید بهعنوان مجوز استفادهٔ مستقیم از innerHTML برای هر ورودی تلقی شود.
سازگاری را در سطح قابلیت بررسی کنید
Declarative Shadow DOM و Scoped Custom Element Registry نام قابلیتهای مشخص هستند. بررسی آنها باید از بررسی یک Custom Element معمولی جدا باشد. نام «v2» اگر به منبع و تعریف مشخص متصل نباشد، معلوم نمیکند کدام رفتار مورد نیاز پروژه است. در گزارش فنی بهتر است بنویسیم کارت به customElements.define و attachShadow وابسته است؛ این عبارت قابل پیگیری است و به همکار بعدی میگوید دقیقاً چه چیزی را باید بررسی کند.
برای تصمیم تحریری دربارهٔ یک نسخهٔ قدیمی، جدول سازگاری امروز کافی نیست. ممکن است مستندات فعلی قابلیت تازهتری را توضیح دهند که هنگام انتشار آن نسخه وجود نداشته است. تاریخ انتشار، یادداشت همان نسخه و تاریخ اضافهشدن API باید کنار هم خوانده شوند. این احتیاط به معنی استفاده نکردن از امکانات تازه نیست؛ فقط مانع انتقال وضعیت امروز به یک خبر تاریخی میشود. عنوان این مقاله نیز برای همین از ادعای «پشتیبانی کامل» فاصله گرفته است.
ادعای قدیمی دربارهٔ «Total Cookie Protection نسخهٔ ۳» و غیرممکن شدن ردگیری نیز در خبر رسمی مورد استناد تأیید نشده بود. یک قابلیت حریم خصوصی را نباید به تضمین مطلق تبدیل کرد. ردگیری میتواند به روشهای متفاوتی انجام شود و توضیح یک سازوکار مشخص فقط همان محدوده را پوشش میدهد. برای معرفی تغییر مرورگر، عبارت دقیق منبع از یک نتیجهٔ تبلیغاتی قابل اعتمادتر است؛ بهخصوص وقتی خواننده قرار است بر اساس آن انتخاب امنیتی انجام دهد.
قرارداد کوچک برای استفادهٔ مشترک
پیش از گسترش کارت، قرارداد آن را در چند جمله بنویسید: محتوای عنوان از slot نامدار میآید، توضیح از slot پیشفرض و دکمه فقط وضعیت درخواست را نمایش میدهد. اگر محصول به ارسال رویداد نیاز داشت، نام و دادهٔ رویداد باید مستند شوند. اگر شخصیسازی ظاهر لازم شد، یک مجموعهٔ محدود از CSS custom properties تعریف کنید. قرار دادن دهها selector داخلی در قرارداد عمومی، مصرفکنندهها را به جزئیات ساخت وابسته میکند.
برای ادامهٔ بحث معماری میتوانید مقالهٔ Web Components و آیندهٔ آن را بخوانید. در یک پروژهٔ واقعی، کارت را با عنوان طولانی فارسی، متن خالی، دو جهت نوشتار و استفادهٔ صفحهکلید مرور کنید. اینها مثالهایی از نیازهای محصولاند، نه شاهدی برای ادعای پشتیبانی یک نسخه. نتیجهٔ بررسی باید به همان مرورگرها و همان قابلیتهای بررسیشده محدود بماند. تعریف دقیق نیاز، معمولاً از انتخاب یک برچسب کلی برای فناوری سود بیشتری دارد.
هنوز دیدگاهی ثبت نشده
اولین نفری باشید که نظر میدهد!