प्रश्न और कार्यक्षेत्र
आपको React, Vue, या सर्वर टेम्पलेट्स द्वारा रेंडर किए गए होस्ट्स के लिए एक <account-summary> कस्टम एलिमेंट शिप करना होगा। यह अकाउंट स्टेटस प्रदर्शित करता है, status एट्रिब्यूट पर प्रतिक्रिया देता है, एक पब्लिक इवेंट डिस्पैच करता है, और डॉक्यूमेंट से हटाए जाने पर सब्सक्रिप्शन्स को रिलीज़ करता है। शैलियों (styles) को कीबोर्ड या एक्सेसिबिलिटी तकनीकों के सेमेंटिक्स को खोए बिना समाहित (contained) रहना चाहिए।
पहले एक ऑटोनॉमस कस्टम एलिमेंट (autonomous custom element) मानकर चलें। यदि आवश्यकता किसी नेटिव button या किसी अन्य बिल्ट-इन को विस्तारित करने की है, तो कस्टमाइज़्ड बिल्ट-इन एलिमेंट्स का अलग से मूल्यांकन करें क्योंकि ब्राउज़र सपोर्ट डिज़ाइन को बदल देता है। यह इंटरव्यू लाइफसाइकिल सीमाओं और रिसोर्स ओनरशिप का परीक्षण करता है, न कि फ्रेमवर्क कंपोनेंट सिंटैक्स का।
इंटरव्यूअर क्या मूल्यांकन कर रहा है
- क्या आप constructor, connectedCallback, disconnectedCallback, और एट्रिब्यूट ऑब्जर्वेशन को अलग-अलग जिम्मेदारियां दे सकते हैं।
- क्या आप यह मानने के बजाय कि प्रत्येक कॉलबैक केवल एक बार चलता है, पुनः कनेक्ट होने (reconnects), मूव्स, अपग्रेड्स और शुरुआती एट्रिब्यूट्स को संभालते हैं।
- क्या आप Shadow DOM एनकैप्सुलेशन को एक्सेसिबिलिटी, इवेंट प्रोपेगेशन और होस्ट इंटीग्रेशन से जोड़ते हैं।
- क्या आप आइडम्पोटेंट (idempotent) रजिस्ट्रेशन, कम्पैटिबिलिटी, क्लीनअप और टेस्ट्स का प्रस्ताव दे सकते हैं।
सिर्फ कॉलबैक के नामों को दोहराना पर्याप्त नहीं है। एक मजबूत उत्तर पहले रिसोर्स ओनरशिप को परिभाषित करता है, फिर बताता है कि कनेक्शन, एट्रिब्यूट और रेंडर स्टेट कैसे इंटरैक्ट करते हैं।
पहले स्पष्ट करने योग्य प्रश्न
- क्या एलिमेंट को किसी नेटिव सेमेंटिक एलिमेंट को विस्तारित करना चाहिए? यदि हां, तो
isसिंटैक्स और ब्राउज़र सपोर्ट निर्णय को प्रभावित करते हैं। - क्या इनपुट्स स्ट्रिंग्स हैं, या कॉलर्स को ऑब्जेक्ट्स और कॉलबैक्स पास करने होंगे? जटिल मानों (complex values) के लिए एक स्पष्ट प्रॉपर्टी या मेथड कॉन्ट्रैक्ट की आवश्यकता होती है।
- क्या स्थिति (state) को उसी डॉक्यूमेंट के भीतर किसी मूव के बाद भी बने रहना चाहिए? यह निर्धारित करता है कि
connectedMoveCallbackका आकलन करना है या स्टेट को स्पष्ट रूप से सुरक्षित रखना है। - क्या इवेंट्स को Shadow DOM बाउंड्री को पार करना चाहिए? इवेंट API चुनने से पहले
bubbles,composed, और पेलोड पर सहमति बनाएं।
30-सेकंड का उत्तर ढांचा (Framework)
“मैं डेफिनिशन, कनेक्शन, एट्रिब्यूट सिंक्रोनाइज़ेशन, रेंडरिंग और क्लीनअप को अलग करता हूँ। constructor केवल हल्के फ़ील्ड्स को इनिशियलाइज़ करता है और चाइल्ड नोड्स या बाहरी डॉक्यूमेंट पर निर्भर नहीं करता है। connectedCallback सब्सक्रिप्शन्स स्थापित करता है और आइडम्पोटेंट तरीके से रेंडर करता है; disconnectedCallback लिसनर्स, टाइमर्स और ऑब्जर्वर्स को रिलीज़ करता है। मैं केवल आवश्यक एट्रिब्यूट्स को ऑब्जर्व करता हूँ, attributeChangedCallback में उनके स्ट्रिंग मानों को सामान्य (normalize) करता हूँ, और एक ही रेंडर पाथ के माध्यम से अपडेट्स को रूट करता हूँ। Shadow DOM इम्प्लीमेंटेशन शैलियों को समाहित करता है, जबकि पब्लिक सेमेंटिक्स, फोकस और इवेंट्स टेस्ट किए गए होस्ट कॉन्ट्रैक्ट का हिस्सा बने रहते हैं। मैं पुनः कनेक्ट होने, मूव्स, शुरुआती एट्रिब्यूट्स, रिमूवल, अपग्रेड्स और असमर्थित ब्राउज़रों के परीक्षणों के साथ समाप्त करता हूँ।”
चरण-दर-चरण विश्लेषण
1. स्टेट और रिसोर्स ओनरशिप को परिभाषित करें
कनेक्टेड, सबस्क्राइब और रेंडर की गई स्टेट को अलग से ट्रैक करें। constructor को बाहरी DOM को नहीं पढ़ना चाहिए और न ही नेटवर्क कार्य शुरू करना चाहिए: एक ब्राउज़र किसी एलिमेंट को डॉक्यूमेंट से कनेक्ट करने से पहले उसका निर्माण कर सकता है।
class AccountSummary extends HTMLElement {
static observedAttributes = ["status"];
#connected = false;
#unsubscribe = null;
#root;
constructor() {
super();
this.#root = this.attachShadow({ mode: "open" });
}
}2. connectedCallback को दोहराए जाने के लिए सुरक्षित बनाएं
एक एलिमेंट को हटाया जा सकता है और फिर से डाला जा सकता है, इसलिए connectedCallback को बिना शर्त दूसरा लिसनर रजिस्टर नहीं करना चाहिए। कनेक्शन स्टेट की जांच करें, सब्सक्रिप्शन स्थापित करें, और रेंडरिंग को बार-बार इनवोक करने के लिए सुरक्षित बनाएं।
connectedCallback() {
if (this.#connected) return;
this.#connected = true;
this.#unsubscribe = accountStore.subscribe(() => this.#render());
this.#render();
}3. disconnectedCallback में प्रत्येक साइड इफेक्ट को रिलीज़ करें
इवेंट लिसनर्स, setInterval, ResizeObserver, AbortController, और स्टोर सब्सक्रिप्शन्स को स्पष्ट ओनर्स की आवश्यकता होती है। क्लीनअप के बाद संदर्भों (references) को साफ़ करें ताकि बाद का कनेक्शन ठीक एक नया रिसोर्स सेट बनाए।
disconnectedCallback() {
this.#unsubscribe?.();
this.#unsubscribe = null;
this.#connected = false;
}पुराने मूव पैटर्न्स डिस्कनेक्ट और कनेक्ट कॉलबैक्स को क्रम से ट्रिगर कर सकते हैं। यदि मूव्स के दौरान स्टेट को संरक्षित रखना महत्वपूर्ण है, तो जहाँ समर्थित हो वहाँ connectedMoveCallback का आकलन करें, या कॉलबैक ऑर्डरिंग पर निर्भर रहने के बजाय एट्रिब्यूट्स और टिकाऊ स्टेट से फिर से निर्माण करें।
4. एट्रिब्यूट्स को ऑब्जर्व करें और प्रॉपर्टीज़ को अलग करें
observedAttributes को केवल उन मानों को सूचीबद्ध करना चाहिए जिन्हें सिंक्रोनाइज़ेशन की आवश्यकता है। attributeChangedCallback पार्सिंग के दौरान शुरुआती एट्रिब्यूट के लिए चल सकता है, इसलिए “changed” का अर्थ अनिवार्य रूप से यह नहीं है कि उपयोगकर्ता ने इसे अभी संपादित किया है। शुरुआती और बाद के मानों को एक ही पाथ से संभालें और इसके कॉलबैक से उसी एट्रिब्यूट को वापस लिखने से बचें।
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue) return;
if (name === "status") this.#renderStatus(newValue ?? "unknown");
}स्ट्रिंग एट्रिब्यूट्स डिक्लेरेटिव HTML के लिए अच्छी तरह से काम करते हैं। मनमाने JSON स्ट्रिंग्स को असीमित प्रोटोकॉल के रूप में मानने के बजाय, डॉक्यूमेंटेड प्रॉपर्टीज़ या मेथड्स के माध्यम से एरेज़, ऑब्जेक्ट्स और कॉलबैक्स पास करें।
5. Shadow DOM और होस्ट कॉन्ट्रैक्ट डिज़ाइन करें
Shadow DOM आंतरिक CSS और DOM प्रश्नों (queries) को अलग करता है, लेकिन यह स्वचालित रूप से सेमेंटिक्स प्रदान नहीं करता है। सही रोल्स, एक्सेसिबल नाम, फोकस क्रम और दृश्यता (visibility) का उपयोग करें। यदि होस्ट को लेआउट में भाग लेना चाहिए, तो :host, स्लॉट्स, या CSS कस्टम प्रॉपर्टीज़ के माध्यम से एक संकीर्ण कॉन्ट्रैक्ट को एक्सपोज़ करें।
:host { display: block; }
:host([hidden]) { display: none; }उन इवेंट्स के लिए जिन्हें होस्ट्स द्वारा देखा जाना चाहिए, bubbles और composed को सोच-समझकर सेट करें और पेलोड को स्थिर रखें। किसी बाउंड्री को पार करने के लिए आंतरिक नोड्स को एक्सपोज़ करने की आवश्यकता नहीं है।
6. रजिस्ट्रेशन, अपग्रेड्स और कम्पैटिबिलिटी को संभालें
एक नाम को ग्लोबल रजिस्ट्री में केवल एक बार परिभाषित किया जा सकता है। साझा लाइब्रेरीज़ को नेमस्पेस प्रीफिक्स का उपयोग करना चाहिए और परिभाषित करने से पहले customElements.get(name) की जांच करनी चाहिए; किसी एक्सेप्शन को पकड़कर टकराव को न छिपाएं। रजिस्ट्रेशन से पहले पार्स किए गए एलिमेंट्स को डेफिनिशन के बाद अपग्रेड किया जाता है, इसलिए इम्प्लीमेंटेशन को उस क्रम का समर्थन करना चाहिए।
const name = "account-summary";
if (!customElements.get(name)) {
customElements.define(name, AccountSummary);
}ऑटोनॉमस एलिमेंट्स को आमतौर पर ब्राउज़रों में डिप्लॉय करना आसान होता है। MDN कस्टमाइज़्ड बिल्ट-इन एलिमेंट्स के लिए Safari की सीमाओं को प्रलेखित करता है; यदि किसी नेटिव एलिमेंट का विस्तार करना अनिवार्य है, तो लक्षित ब्राउज़र मैट्रिक्स को सत्यापित करें और एक फॉलबैक तैयार करें।
उच्च-गुणवत्ता वाला नमूना उत्तर
मैं पहले एक ओनरशिप टेबल लिखूंगा: constructor Shadow Root और डिफ़ॉल्ट फ़ील्ड्स बनाता है, connectedCallback सबस्क्राइब करता है और रेंडर करता है, disconnectedCallback अनसबस्क्राइब करता है, और attributeChangedCallback केवल ऑब्जर्व किए गए डिक्लेरेटिव कॉन्फ़िगरेशन को संभालता है। प्रत्येक चरण दोहराने योग्य है या सुरक्षित रूप से छोड़ने योग्य है; “एक बार चलता है” को लाइफसाइकिल गारंटी के रूप में नहीं माना जाता है।
मैं एक ऑटोनॉमस कस्टम एलिमेंट चुनूंगा और customElements.get के साथ रजिस्ट्रेशन को सुरक्षित करूंगा। Shadow DOM में इम्प्लीमेंटेशन शैलियाँ शामिल हैं, जबकि कंपोनेंट अभी भी एक एक्सेसिबल नाम, फोकस व्यवहार और इवेंट कॉन्ट्रैक्ट को एक्सपोज़ करता है। क्रॉस-बाउंड्री इवेंट्स एक सीमित पेलोड के साथ स्पष्ट bubbles और composed सेटिंग्स का उपयोग करते हैं। एट्रिब्यूट्स प्रिमिटिव डिक्लेरेटिव कॉन्फ़िगरेशन ले जाते हैं; जटिल ऑब्जेक्ट्स प्रॉपर्टीज़ या मेथड्स का उपयोग करते हैं।
सत्यापन में पार्स-बिफ़ोर-डिफ़ाइन अपग्रेड्स, शुरुआती एट्रिब्यूट्स, पुनः कनेक्ट होने, मूव्स, क्लीनअप, क्रॉस-बाउंड्री इवेंट्स, कीबोर्ड व्यवहार और स्क्रीन-रीडर सेमेंटिक्स शामिल हैं। यदि कस्टमाइज़्ड बिल्ट-इन्स की आवश्यकता है, तो मैं पहले ब्राउज़र मैट्रिक्स को मान्य करूंगा, फिर उस पाथ को केवल तभी चुनूंगा जब कम्पैटिबिलिटी की लागत स्वीकार्य हो।
सामान्य गलतियाँ और सुधार
- गलती → constructor में चाइल्ड नोड्स को पढ़ना या रिक्वेस्ट्स शुरू करना → यह क्यों विफल होता है → एलिमेंट अभी कनेक्ट नहीं हुआ हो सकता है → सुधार → डॉक्यूमेंट और नेटवर्क कार्य को connectedCallback में ले जाएं और रिक्वेस्ट्स को एक AbortController से बाइंड करें।
- गलती → हर connectedCallback पर लिसनर्स जोड़ना → यह क्यों विफल होता है → पुनः कनेक्ट होने पर काम डुप्लिकेट होता है और मेमोरी लीक होती है → सुधार → एक सब्सक्रिप्शन हैंडल रखें और सेटअप/क्लीनअप को आइडम्पोटेंट जोड़े बनाएं।
- गलती → एट्रिब्यूट कॉलबैक से बिना शर्त setAttribute को कॉल करना → यह क्यों विफल होता है → यह एक कॉलबैक लूप बना सकता है, और शुरुआती एट्रिब्यूट्स को पार्स करने पर भी कॉलबैक इनवोक होता है → सुधार → पुराने और नए मानों की तुलना करें, एक बार सामान्य करें, और उसी एट्रिब्यूट को दोबारा लिखने से बचें।
- गलती → Shadow DOM को एक्सेसिबिलिटी समाधान के रूप में मानना → यह क्यों विफल होता है → एनकैप्सुलेशन नाम, फोकस या सेमेंटिक्स नहीं बनाता है → सुधार → कीबोर्ड और एक्सेसिबिलिटी टेक्नोलॉजी फ्लोज़ के साथ पब्लिक कॉन्ट्रैक्ट का परीक्षण करें।
फॉलो-अप प्रश्न और उत्तर
एलिमेंट को परिभाषित करने से पहले पार्स किया जाता है। आप फ़्लैश (flash) से कैसे बचते हैं?
अपरिभाषित एलिमेंट को एक अपग्रेड करने योग्य स्टेट मानें और पढ़ने योग्य फॉलबैक सामग्री या लोडिंग स्थिति प्रदान करें। ब्राउज़र रजिस्ट्रेशन के बाद इंस्टेंस को अपग्रेड करता है। यदि कोड को प्रतीक्षा करनी होगी, तो होस्ट customElements.whenDefined("account-summary") का await कर सकता है, लेकिन पूरे पेज को कंपोनेंट स्क्रिप्ट पर ब्लॉक नहीं होना चाहिए।
क्या जटिल ऑब्जेक्ट्स को एट्रिब्यूट्स में रखा जाना चाहिए?
आमतौर पर नहीं। एट्रिब्यूट्स स्ट्रिंग्स होते हैं और सीरियलाइज़ेबल डिक्लेरेटिव कॉन्फ़िगरेशन के अनुकूल होते हैं; ऑब्जेक्ट्स और कॉलबैक्स को स्पष्ट प्री- और पोस्ट-कनेक्शन व्यवहार के साथ प्रलेखित प्रॉपर्टीज़ या मेथड्स का उपयोग करना चाहिए। यदि सीरियलाइज़ेशन की आवश्यकता है, तो वर्शन, आकार और पार्स-एरर हैंडलिंग निर्दिष्ट करें।
किसी होस्ट को Shadow DOM के अंदर एक बटन से क्लिक प्राप्त क्यों नहीं होता है?
क्रॉस-बाउंड्री प्रोपेगेशन composed पर निर्भर करता है; ऊपर की ओर बबलिंग bubbles पर निर्भर करती है। दोनों विकल्पों को स्पष्ट रूप से चुनकर एक स्थिर सेमेंटिक इवेंट डिस्पैच करें। बाउंड्री के बाहर, टारगेट को शैडो होस्ट पर फिर से लक्षित (retargeted) किया जा सकता है, इसलिए होस्ट्स को आंतरिक नोड्स पर निर्भर नहीं होना चाहिए।
आप कैसे साबित करते हैं कि पुनः कनेक्ट होने पर मेमोरी लीक नहीं होती है?
एक ही इंस्टेंस को बार-बार इन्सर्ट और रिमूव करें, सब्सक्रिप्शन्स, इवेंट डिलीवरी और टाइमर हैंडल्स की गणना करें, फिर मेमोरी स्नैपशॉट्स का उपयोग करके सत्यापित करें कि पुराने नोड्स को कलेक्ट किया जा सकता है। शुरुआती एट्रिब्यूट कॉलबैक्स, मूव्स और पार्स-बिफ़ोर-डिफ़ाइन अपग्रेड्स को कवर करें; केवल पहला रेंडर बहुत कम साबित करता है।