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 में इनपुट बॉक्स में नए कर्मचारी का नाम डालना शुरू करता है, तो Angular AppComponent से शुरू होकर पूरे कॉम्पोनेंट ट्री के लिए बदलाव का पता लगाने की सुविधा को ट्रिगर करता है. इसका मतलब है कि जब उपयोगकर्ता टेक्स्ट इनपुट टाइप कर रहा होता है, तब Angular हर कर्मचारी से जुड़ी संख्या वाली वैल्यू को बार-बार फिर से कैलकुलेट करता है. इससे यह पुष्टि की जा सकती है कि पिछली जांच के बाद से इन वैल्यू में कोई बदलाव नहीं हुआ है.

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

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

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

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

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

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

कॉम्पोनेंट के सबट्री को स्किप करना

जब उपयोगकर्ता बिक्री EmployeeListComponent के लिए टेक्स्ट इनपुट टाइप कर रहा है, तो आपको पता है कि आरएंडडी विभाग का डेटा नहीं बदल रहा है. इसलिए, इसके कॉम्पोनेंट पर बदलाव का पता लगाने की सुविधा को चलाने की कोई ज़रूरत नहीं है. यह पक्का करने के लिए कि आर&डी इंस्टेंस, बदलाव का पता लगाने की सुविधा को ट्रिगर न करे, 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 पर सेट है, लेकिन जब उपयोगकर्ता उससे जुड़ा टेक्स्ट इनपुट टाइप करता है, तब भी Angular किसी विभाग के सभी कर्मचारियों की संख्या वाली वैल्यू का फिर से हिसाब लगाता है.

इस व्यवहार को बेहतर बनाने के लिए, प्योर पाइप का फ़ायदा लिया जा सकता है. प्योर और इंप्योर पाइप, दोनों ही इनपुट स्वीकार करते हैं और ऐसे नतीजे दिखाते हैं जिनका इस्तेमाल टेंप्लेट में किया जा सकता है. इन दोनों के बीच का अंतर यह है कि प्योर पाइप अपने नतीजे का फिर से हिसाब सिर्फ़ तब लगाएगा, जब उसे पिछले कॉल से अलग इनपुट मिलेगा.

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