पिछले साल, Angular में कई नई सुविधाएं जोड़ी गई हैं. जैसे, हाइड्रेशन और देर से लोड होने वाले व्यू. इनकी मदद से, डेवलपर अपनी वेबसाइट की परफ़ॉर्मेंस की अहम जानकारी को बेहतर बना सकते हैं. साथ ही, असली उपयोगकर्ताओं को बेहतर अनुभव दे सकते हैं. इस सुविधा के आधार पर, सर्वर साइड रेंडरिंग से जुड़ी अन्य सुविधाओं पर भी काम चल रहा है. जैसे, स्ट्रीमिंग और आंशिक हाइड्रेशन.
माफ़ करें, एक ऐसा पैटर्न है जिसकी वजह से आपके ऐप्लिकेशन या लाइब्रेरी को इन सभी नई और आने वाली सुविधाओं का पूरा फ़ायदा नहीं मिल पाएगा: DOM स्ट्रक्चर में मैन्युअल तरीके से बदलाव करना. ऐंग्युलर के लिए यह ज़रूरी है कि सर्वर पर किसी कॉम्पोनेंट को क्रम में लगाए जाने के समय से लेकर डीओएम का स्ट्रक्चर तब तक एक जैसा बना रहे, जब तक कि वह ब्राउज़र पर हाइड्रेट न हो जाए. हाइड्रेशन से पहले, DOM में मैन्युअल रूप से नोड जोड़ने, उन्हें हटाने या उन्हें एक जगह से दूसरी जगह ले जाने के लिए, ElementRef
, Renderer2
या DOM API का इस्तेमाल करने पर, इन सुविधाओं के काम न करने की समस्याएं आ सकती हैं.
हालांकि, मैन्युअल रूप से DOM में बदलाव करने और उसे ऐक्सेस करने से हमेशा समस्या नहीं होती. कभी-कभी ऐसा करना ज़रूरी भी होता है. डीओएम को सुरक्षित तरीके से इस्तेमाल करने के लिए, इसकी ज़रूरत को कम से कम करें. इसके बाद, इसका इस्तेमाल ज़्यादा से ज़्यादा देर तक न करें. यहां दिए गए दिशा-निर्देशों में बताया गया है कि ऐसा कैसे किया जा सकता है. साथ ही, ऐसे यूनिवर्सल और आने वाले समय के हिसाब से काम करने वाले Angular कॉम्पोनेंट बनाने का तरीका भी बताया गया है जो Angular की सभी नई और आने वाली सुविधाओं का पूरा फ़ायदा ले सकते हैं.
मैन्युअल डीओएम में बदलाव से बचें
मैन्युअल डीओएम मैनिपुलेशन से होने वाली समस्याओं से बचने का सबसे अच्छा तरीका यह है कि जहां भी हो सके, वहां इसका इस्तेमाल न करें. Angular में पहले से मौजूद एपीआई और पैटर्न हैं, जो DOM के ज़्यादातर पहलुओं में बदलाव कर सकते हैं: आपको सीधे DOM को ऐक्सेस करने के बजाय, इनका इस्तेमाल करना चाहिए.
कॉम्पोनेंट के अपने डीओएम एलिमेंट में बदलाव करना
कॉम्पोनेंट या डायरेक्टिव लिखते समय, आपको होस्ट एलिमेंट (यानी, कॉम्पोनेंट या डायरेक्टिव के सिलेक्टर से मैच करने वाला DOM एलिमेंट) में बदलाव करना पड़ सकता है. उदाहरण के लिए, किसी क्लास, स्टाइल या एट्रिब्यूट को जोड़ने के लिए, रैपर एलिमेंट को टारगेट करने या उसे शामिल करने के बजाय. पहले से मौजूद डीओएम एलिमेंट में बदलाव करने के लिए, सिर्फ़ ElementRef
तक पहुंचना आकर्षक लगता है. इसके बजाय, वैल्यू को एक्सप्रेशन से एलान करके बांधने के लिए, होस्ट बाइंडिंग का इस्तेमाल करें:
@Component({
selector: 'my-component',
template: `...`,
host: {
'[class.foo]': 'true'
},
})
export class MyComponent {
/* ... */
}
उदाहरण के लिए, ��चटीएमएल में डेटा बाइंडिंग की तरह ही, एट्रिब्यूट और स्टाइल के साथ भी बाइंड किया जा सकता है. साथ ही, 'true'
को किसी ऐसे अलग एक्सप्रेशन में बदला जा सकता है जिसका इस्तेमाल, Angular ज़रूरत के मुताबिक वैल्यू को अपने-आप जोड़ने या हटाने के लिए करेगा.
कुछ मामलों में, कुंजी को डाइनैमिक तौर पर कैलकुलेट करना होगा. आपके पास ऐसे सिग्नल या फ़ंक्शन से भी बाइंड करने का विकल्प होता है जो वैल्यू का सेट या मैप दिखाता है:
@Component({
selector: 'my-component',
template: `...`,
host: {
'[class.foo]': 'true',
'[class]': 'classes()'
},
})
export class MyComponent {
size = signal('large');
classes = computed(() => {
return [`size-${this.size()}`];
});
}
ज़्यादा जटिल ऐप्लिकेशन में, ExpressionChangedAfterItHasBeenCheckedError
से बचने के लिए, मैन्युअल रूप से DOM में बदलाव करने का मन हो सकता है. इसके बजाय, पिछले उदाहरण की तरह ही वैल्यू को किसी सिग्नल से बंधा जा सकता है. यह काम ज़रूरत के मुताबिक किया जा सकता है. साथ ही, इसके लिए आपके पूरे कोड बेस में सिग्नल अपनाने की ज़रूरत नहीं होती.
टेंप्लेट के बाहर डीओएम एलिमेंट को बदलें
आम तौर पर, ऐक्सेस नहीं किए जा सकने वाले एलिमेंट ऐक्सेस करने के लिए, डीओएम का इस्तेमाल किया जाता है. उदाहरण के लिए, वे एलिमेंट जो अन्य पैरंट या चाइल्ड कॉम्पोनेंट से जुड़े होते हैं. हालांकि, इसमें गड़बड़ी होने की संभावना होती है. साथ ही, यह एन्कैप्सलेशन का उल्लंघन करता है. साथ ही, आने वाले समय में उन कॉम्पोनेंट ���ो बदलना या अपग्रेड करना मुश्किल हो जाता है.
इसके बजाय, आपके कॉम्पोनेंट को हर दूसरे कॉम्पोनेंट को ब्लैक बॉक्स मानना चाहिए. ध्यान दें कि अन्य कॉम्पोनेंट (किसी एक ही ऐप्लिकेशन या लाइब्रेरी में भी) को कब और कहां पर इंटरैक्ट करने की ज़रूरत पड़ सकती है या वह आपके कॉम्पोनेंट के काम करने के तरीके या लुक को पसंद के मुताबिक बना सकता है. इसके बाद, ऐसा करने का एक सुरक्षित और दस्तावेज़ किया गया तरीका इस्तेमाल करें. जब आसान @Input
और @Output
प्रॉपर्टी काफ़ी न हों, तो सबट्री में एपीआई उपलब्ध कराने के लिए हैरारिकल डिपेंडेंसी इंजेक्शन जैसी सुविधाओं का इस्तेमाल करें.
पहले, <body>
या किसी दूसरे होस्ट एलिमेंट के आखिर में एलिमेंट जोड़कर, मॉडल डायलॉग या टूलटिप जैसी सुविधाएं लागू करना आम बात थी. इसके बाद, उसमें कॉन्टेंट को मूव या प्रोजेक्ट किया जाता था. हालांकि, इन दिनों अपने टेंप्लेट में, <dialog>
एलिमेंट को रेंडर किया जा सकता है:
@Component({
selector: 'my-component',
template: `<dialog #dialog>Hello World</dialog>`,
})
export class MyComponent {
@ViewChild('dialog') dialogRef!: ElementRef;
constructor() {
afterNextRender(() => {
this.dialogRef.nativeElement.showModal();
});
}
}
��ीओएम में मैन्युअल तरीके से बदलाव करने की प्रोसेस को बाद के लिए छोड़ना
��ीओएम में ��ीधे तौर पर बदलाव करने और ज़्यादा से ज़्यादा ऐक्सेस करने के लिए, पिछले दिशा-निर्देशों का इस्तेमाल करने के बाद भी, आपके पास कुछ ऐसा डेटा रह सकता है जिसे हटाया नहीं जा सकता. ऐसे मामलों में, इसे ज़्यादा से ज़्यादा समय तक टालना ज़रूरी है. ऐसा करने के लिए, afterRender
और afterNextRender
कॉलबैक एक अच्छा तरीका है. ऐसा इसलिए, क्योंकि Angular किसी भी बदलाव की जांच करने और उन्हें डीओएम के लिए प्रतिबद्ध करने के बाद, सिर्फ़ ब्राउज़र पर चलता है.
सिर्फ़ ब्राउज़र में JavaScript चलाना
कुछ मामलों में, आपके पास ऐसी लाइब्रेरी या एपीआई होगा जो सिर्फ़ ब्राउज़र में काम करता है. उदाहरण के लिए, चार्ट लाइब्रेरी, IntersectionObserver
का कुछ इस्तेमाल वगैरह. ब्राउज़र पर चल रहा है या नहीं, यह देखने या सर्वर पर स्टबिंग की प्रोसेस बंद करने के बजाय, सिर्फ़ afterNextRender
का इस्तेमाल किया जा सकता है:
@Component({
/* ... */
})
export class MyComponent {
@ViewChild('chart') chartRef: ElementRef;
myChart: MyChart|null = null;
constructor() {
afterNextRender(() => {
this.myChart = new MyChart(this.chartRef.nativeElement);
});
}
}
कस्टम लेआउट लागू करना
कभी-कभी, आपको टारगेट किए गए ब्राउज़र पर काम न करने वाले लेआउट को लागू करने के लिए, डीओएम को पढ़ने या उसमें बदलाव करने की ज़रूरत पड़ सकती है. जैसे, टूलटिप की पोज़िशन तय करना. इसके लिए afterRender
एक बेहतरीन विकल्प है, क्योंकि इस बात पर भरोसा किया जा सकता है कि डीओएम एक जैसा है. afterRender
और afterNextRender
, EarlyRead
, Read
या Write
की phase
वैल्यू स्वीकार करते हैं. लिखने के बाद DOM लेआउट को पढ़ने से, ब्राउज़र को लेआउट का हिसाब फिर से लगाना पड़ता है. इससे परफ़ॉर्मेंस पर काफ़ी बुर�� असर पड़ सकता है (देखें: लेआउट थ्रैशिंग). इसलिए, यह ज़रूरी है कि अपने लॉजिक को सही चरणों में सावधानी से बांटा जाए.
उदाहरण के लिए, जो टूलटिप कॉम्पोनेंट पेज पर किसी दूसरे एलिमेंट के मुकाबले टूलटिप दिखाना चाहता है वह दो चरणों का इस्तेमाल कर सकता है. EarlyRead
फ़ेज़ का इस्तेमाल, सबसे पहले एलिमेंट के साइज़ और पोज़िशन का पता लगाने के लिए किया जाएगा:
afterRender(() => {
targetRect = targetEl.getBoundingClientRect();
tooltipRect = tooltipEl.getBoundingClientRect();
}, { phase: AfterRenderPhase.EarlyRead },
);
इसके बाद, Write
फ़ेज़, टूलटिप की जगह बदलने के लिए, पहले से पढ़ी गई वैल्यू का इस्तेमाल करेगा:
afterRender(() => {
tooltipEl.style.setProperty('left', `${targetRect.left + targetRect.width / 2 - tooltipRect.width / 2}px`);
tooltipEl.style.setProperty('top', `${targetRect.bottom - 4}px`);
}, { phase: AfterRenderPhase.Write },
);
अपने लॉजिक को सही चरणों में बांटकर, Angular ऐप्लिकेशन के हर दूसरे कॉम्पोनेंट में डीओएम में बदलाव को असरदार तरीके से बैच कर सकता है. इससे, परफ़ॉर्मेंस पर कम से कम असर पड़ता है.
नतीजा
Angular के सर्वर-साइड रेंडरिंग में कई नए और दिलचस्प सुधार किए जा रहे हैं. इनका मकसद, आपके लिए अपने उपयोगकर्ताओं को बेहतर अनुभव देना है. हमें उम्मीद है कि पिछली सलाह से आपको अपने ऐप्लिकेशन और लाइब्रेरी में इनका पूरा फ़ायदा पाने में मदद मिलेगी!