Angular के बदलाव का पता लगाने की सुविधा ऑप्टिमाइज़ करें

बेहतर उपयोगकर्ता अनुभव के लिए तेज़ी से बदलाव का पता लगाने की सुविधा लागू करें.

Angular समय-समय पर अपनी बदलाव का पता लगाने की प्रक्रिया को चलाता है, ताकि डेटा मॉडल में किए गए बदलाव ऐप्लिकेशन के व्यू में दिखें. बदलाव का पता लगाने की सुविधा, मैन्युअल तरीके से या किसी एसिंक्रोनस इवेंट (उदाहरण के लिए, उपयोगकर्ता इंटरैक्शन या XHR पूरा होने) के ज़रिए ट्रिगर की जा सकती है.

बदलाव की पहचान एक बेहतरीन टूल है. हालांकि, अगर इसे बार-बार चलाया जाता है, तो यह बहुत सारी कंप्यूटेशन (कंप्यूटेशन) ट्रिगर कर सकता है और मुख्य ब्राउज़र थ्रेड को ब्लॉक कर सकता है.

इस पोस्ट में आप अपने ऐप्लिकेशन के कुछ हिस्सों को छोड़कर और ज़रूरत होने पर ही बदलाव पहचानने के तरीके को चलाकर, बदलाव का पता लगाने की सुविधा को नियंत्रित और ऑप्टिमाइज़ करने का तरीका जानेंगे.

Angular के बदलाव का पता लगाने की सुविधा के अंदर

यह समझने के लिए कि Angular के बदलाव का पता लगाने की सुविधा कैसे काम करती है, आइए एक सैंपल ऐप्लिकेशन पर नज़र डालते हैं!

आपको GitHub के इस डेटा स्टोर करने की जगह में ऐप्लिकेशन का कोड मिल सकता है.

ऐप में किसी कंपनी के दो विभागों—बिक्री और आर ऐंड डी—में दो कॉम्पोनेंट शामिल हैं:

  • AppComponent, जो ऐप्लिकेशन का रूट कॉम्पोनेंट है और
  • EmployeeListComponent के दो इंस्टेंस, एक बिक्री के लिए और एक रिसर्च ऐंड डेवलपमेंट के लिए.

सैंपल ऐप्लिकेशन

AppComponent के लिए टेंप्लेट में, EmployeeListComponent के दो इंस्टेंस देखे जा सकते हैं:

<app-employee-list
  [data]="salesList"
  department="Sales"
  (add)="add(salesList, $event)"
  (remove)="remove(salesList, $event)"
></app-employee-list>

<app-employee-list
  [data]="rndList"
  department="R&D"
  (add)="add(rndList, $event)"
  (remove)="remove(rndList, $event)"
></app-employee-list>

हर कर्मचारी के लिए, एक नाम और एक न्यूमेरिक वैल्यू होती है. यह ऐप्लिकेशन, कर्मचारी की न्यूमेरिक वैल्यू को कारोबार का हिसाब लगाने के लिए पास करता है और नतीजे को स्क्रीन पर दिखाता है.

अब EmployeeListComponent पर एक नज़र डालें:

const fibonacci = (num: number): number => {
  if (num === 1 || num === 2) {
    return 1;
  }
  return fibonacci(num - 1) + fibonacci(num - 2);
};

@Component(...)
export class EmployeeListComponent {
  @Input() data: EmployeeData[];
  @Input() department: string;
  @Output() remove = new EventEmitter<EmployeeData>();
  @Output() add = new EventEmitter<string>();

  label: string;

  handleKey(event: any) {
    if (event.keyCode === 13) {
      this.add.emit(this.label);
      this.label = '';
    }
  }

  calculate(num: number) {
    return fibonacci(num);
  }
}

EmployeeListComponent, इनपुट के तौर पर कर्मचारियों की सूची और डिपार्टमेंट का नाम स्वीकार करता है. जब कोई उपयोगकर्ता किसी कर्मचारी को हटाने या जोड़ने की कोशिश करता है, तो कॉम्पोनेंट एक आउटपुट ट्रिगर करता है. कॉम्पोनेंट में, calculate तरीके के बारे में भी जानकारी दी गई है. इससे, कारोबार का हिसाब लगाने में मदद मिलती है.

EmployeeListComponent का टेंप्लेट यहां है:

<h1 title="Department">{{ department }}</h1>
<mat-form-field>
  <input placeholder="Enter name here" matInput type="text" [(ngModel)]="label" (keydown)="handleKey($event)">
</mat-form-field>
<mat-list>
  <mat-list-item *ngFor="let item of data">
    <h3 matLine title="Name">
      {{ item.label }}
    </h3>
    <md-chip title="Score" class="mat-chip mat-primary mat-chip-selected" color="primary" selected="true">
      {{ calculate(item.num) }}
    </md-chip>
  </mat-list-item>
</mat-list>

यह कोड सूची में मौजूद सभी कर्मचारियों के लिए फिर से लागू होता है और हर कर्मचारी के लिए, सूची आइटम रेंडर करता है. इसमें, EmployeeListComponent में बताए गए इनपुट और label प्रॉपर्टी को दो-तरफ़ा डेटा बाइंडिंग के लिए, ngModel डायरेक्टिव भी शामिल किया गया है.

EmployeeListComponent के दो इंस्टेंस से, ऐप्लिकेशन यह कॉम्पोनेंट ट्री बनाता है:

कॉम्पोनेंट ट्री

AppComponent, ऐप्लिकेशन का रूट कॉम्पोनेंट है. इसके चाइल्ड कॉम्पोनेंट, EmployeeListComponent के दो इंस्टेंस होते हैं. हर इंस्टेंस में ऐसे आइटम (E1, E2 वगैरह) की सूची होती है जो डिपार्टमेंट के अलग-अलग कर्मचारियों के बारे में बताते हैं.

जब उपयोगकर्ता किसी EmployeeListComponent के इनपुट बॉक्स में नए कर्मचारी का नाम डालना शुरू करता है, तो AppComponent से शुरू होने वाले पूरे कॉम्पोनेंट ट्री के लिए, Angular ट्रिगर बदलावों का पता लगाता है. इसका मतलब है कि जब उपयोगकर्ता टेक्स्ट इनपुट में टाइप कर रहा होता है, तब Angular हर कर्मचारी के साथ जुड़े न्यूमेरिक वैल्यू की बार-बार फिर से गणना कर रहा है, ताकि यह पुष्टि की जा सके कि पिछली जांच के बाद से उनमें कोई बदलाव नहीं हुआ है.

यह कितना धीमा हो सकता है यह देखने के लिए, StackBlitz पर प्रोजेक्ट का गैर-ऑप्टिमाइज़ वर्शन खोलें और किसी कर्मचारी का नाम डालकर देखें.

Chrome DevTools का परफ़ॉर्मेंस टैब खोलकर, इस बात की पुष्टि की जा सकती है कि ट्रैफ़िक fibonacci फ़ंक्शन की वजह से आया है या उदाहरण प्रोजेक्ट सेट अप करके.

  1. DevTools खोलने के लिए, `Control+Shift+J` (या Mac पर `Command+Option+J`) दबाएं.
  2. परफ़ॉर्मेंस टैब पर क्लिक करें.

(परफ़ॉर्मेंस पैनल के सबसे ऊपर बाएं कोने में) पर क्लिक करें और ऐप्लिकेशन में किसी एक टेक्स्ट बॉक्स में टाइप करना शुरू करें. पर फिर से क्लिक करें. जब Chrome DevTools, प्रोफ़ाइल बनाने के लिए इकट्ठा किए गए सभी डेटा को प्रोसेस कर लेगा, तब आपको कुछ ऐसा दिखेगा:

परफ़ॉर्मेंस प्रोफ़ाइलिंग

अगर सूची में कई कर्मचारी हैं, तो यह प्रोसेस ब्राउज़र का यूज़र इंटरफ़ेस (यूआई) थ्रेड ब्लॉक कर सकती है. इससे फ़्रेम में गिरावट आ सकती है, जिससे उपयोगकर्ता का अनुभव खराब हो सकता है.

कॉम्पोनेंट सबट्री को छोड़ा जा रहा है

जब कोई उपयोगकर्ता बिक्री EmployeeListComponent के लिए टेक्स्ट इनपुट में टाइप करता है, तब आपको पता होता है कि आर ऐंड डी डिपार्टमेंट के डेटा में बदलाव नहीं हो रहा है. इसलिए, इसके कॉम्पोनेंट पर बदलाव का पता लगाने की कोई ज़रूरत नहीं है. यह पक्का करने के लिए कि R&D इंस्टेंस, बदलाव का पता न लगाए, EmployeeListComponent के changeDetectionStrategy को OnPush पर सेट करें:

import { ChangeDetectionStrategy, ... } from '@angular/core';

@Component({
  selector: 'app-employee-list',
  template: `...`,
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrls: ['employee-list.component.css']
})
export class EmployeeListComponent {...}

अब जब उपयोगकर्ता किसी टेक्स्ट इनपुट में टाइप करता है, तो बदलाव का पता लगाने की सुविधा सिर्फ़ उसी डिपार्टमेंट के लिए ट्रिगर होती है:

कॉम्पोनेंट सबट्री में बदलाव की पहचान करना

मूल ऐप्लिकेशन पर लागू किए गए इस ऑप्टिमाइज़ेशन को यहां देखा जा सकता है.

OnPush में बदलाव का पता लगाने की रणनीति के बारे में ज़्यादा जानने के लिए, आधिकारिक Angular दस्तावेज़ देखें.

इस ऑप्टिमाइज़ेशन का असर देखने के लिए, StackBlitz पर ऐप्लिकेशन में नए कर्मचारी का नाम डालें.

शुद्ध पाइप का उपयोग करना

भले ही, EmployeeListComponent के लिए बदलाव का पता लगाने की रणनीति अब OnPush पर सेट है, लेकिन उपयोगकर्ता किसी डिपार्टमेंट के सभी कर्मचारियों के लिए, ऐंग्युलर अब भी उससे जुड़े टेक्स्ट इनपुट में न्यूमेरिक वैल्यू टाइप करता है.

इस व्यवहार को बेहतर बनाने के लिए आप प्योर पाइप का फ़ायदा ले सकते हैं. शुद्ध और गंदी, दोनों तरह के पाइप, इनपुट को स्वीकार करते हैं और नतीजे देते हैं, जिनका इस्तेमाल टेम्प्लेट में किया जा सकता है. दोनों के बीच अंतर यह है कि एक शुद्ध पाइप अपने परिणाम की केवल तभी दोबारा गणना करती है जब इसे इसके पिछले बोले गए तरीके से कोई भिन्न इनपुट मिलता है.

याद रखें कि ऐप्लिकेशन कर्मचारी के न्यूमेरिक मान के आधार पर दिखाए जाने वाले मान का हिसाब लगाता है और EmployeeListComponent में बताए गए calculate तरीके का इस्तेमाल करता है. अगर आप कैलकुलेशन को पूरी पाइप पर ले जाते हैं, तो Angular सिर्फ़ पाइप एक्सप्रेशन की फिर से गणना तब करेगा, जब इसके तर्क बदलेंगे. फ़्रेमवर्क यह तय करेगा कि रेफ़रंस की जांच करके, पाइप के आर्ग्युमेंट बदले गए हैं या नहीं. इसका मतलब है कि Angular तब तक कोई दोबारा हिसाब नहीं करेगा, जब तक किसी कर्मचारी की न्यूमेरिक वैल्यू को अपडेट नहीं किया जाता.

यहां कारोबार की गिनती को CalculatePipe नाम की पाइप पर ले जाने का तरीका बताया गया है:

import { Pipe, PipeTransform } from '@angular/core';

const fibonacci = (num: number): number => {
  if (num === 1 || num === 2) {
    return 1;
  }
  return fibonacci(num - 1) + fibonacci(num - 2);
};

@Pipe({
  name: 'calculate'
})
export class CalculatePipe implements PipeTransform {
  transform(val: number) {
    return fibonacci(val);
  }
}

पाइप का transform तरीका, fibonacci फ़ंक्शन को शुरू करता है. ध्यान दें कि पाइप साफ़ है. Angular सभी पाइपों को सटीक मानेगा, जब तक कि आप कोई दूसरी चीज़ तय नहीं करते.

आखिर में, EmployeeListComponent के लिए टेंप्लेट में एक्सप्रेशन अपडेट करें:

<mat-chip-list>
  <md-chip>
    {{ item.num | calculate }}
  </md-chip>
</mat-chip-list>

हो गया! अब उपयोगकर्ता के किसी भी डिपार्टमेंट से जुड़े टेक्स्ट इनपुट में टाइप करने पर, ऐप्लिकेशन अलग-अलग कर्मचारियों के लिए अंकों वाली वैल्यू का फिर से हिसाब नहीं लगाएगा.

नीचे दिए गए ऐप्लिकेशन में आप देख सकते हैं कि टाइप करना कितना आसान है!

पिछले ऑप्टिमाइज़ेशन का असर देखने के लिए, StackBlitz पर यह उदाहरण आज़माएं.

ओरिजनल ऐप्लिकेशन के प्योर पाइप ऑप्टिमाइज़ेशन वाला कोड यहां उपलब्ध है.

नतीजा

अगर आपको Angular ऐप्लिकेशन में रनटाइम के धीमे चलने की समस्या का सामना करना पड़ रहा है, तो:

  1. Chrome DevTools की मदद से ऐप्लिकेशन की प्रोफ़ाइल बनाएं और देखें कि गड़बड़ियां कहां से आ रही हैं.
  2. किसी कॉम्पोनेंट के सबट्री में काट-छांट करने के लिए, OnPush में बदलाव का पता लगाने की रणनीति के बारे में जानकारी दें.
  3. भारी कंप्यूटेशन (हिसाब लगाना) को सटीक पाइप पर ले जाएं, ताकि फ़्रेमवर्क तय की गई वैल्यू को कैश मेमोरी में सेव कर सके.