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

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

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

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

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

एंगुलर के बदलाव का पता लगाने के दौरान

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

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

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

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

पर दोबारा क्लिक करें. जब Chrome DevTools आपके इकट्ठा किए गए सभी प्रोफ़ाइलिंग डेटा को प्रोसेस कर लेता है, तब आपको कुछ ऐसा दिखेगा:

परफ़ॉर्मेंस की प्रोफ़ाइल बनाना

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

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

जब उपयोगकर्ता sales 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 में हुए बदलाव का पता लगाने की रणनीति के बारे में ज़्यादा जानने के लिए, Android का आधिकारिक दस्तावेज़ पढ़ें.

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

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

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

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

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

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

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

नतीजा

जब कोई Angular ऐप्लिकेशन में रनटाइम के धीमे ट्रैफ़िक की समस्या का सामना कर रहा हो, तो:

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