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 के लिए टेक्स्ट इनपुट में टाइप कर रहा होता है, तब आपको पता होता है कि अनुसंधान और विकास विभाग का डेटा नहीं बदल रहा है. इसलिए, इसके कॉम्पोनेंट पर बदलाव का पता लगाने की सुविधा को चलाने की कोई वजह नहीं है. यह पक्का करने के लिए कि 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 {...}

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

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

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

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

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