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. भारी कंप्यूटेशन (हिसाब लगाना) को सटीक पाइप पर ले जाएं, ताकि फ़्रेमवर्क तय की गई वैल्यू को कैश मेमोरी में सेव कर सके.