| مقياس الأداء والمقارنة | الاستراتيجية الافتراضية (Default Change Detection) | استراتيجية الأداء المحسن (OnPush Strategy) | |
|---|---|---|---|
| متى يتم فحص المكون؟ | مع كل حدث غير متزامن في التطبيق (نقرات، مؤقتات، حركات ماوس) | فقط عند تغيير خصائص الإدخال (Inputs)، وقوع حدث داخل المكون، أو تغيير إشارة مرتبطة به | |
| معدل استهلاك المعالج والذاكرة | أعلى، نظراً لعمليات الفحص المتكررة لكل شجرة المكونات | أقل بكثير، لتخطي المكونات المستقرة وعدم فحصها بلا داعٍ | |
| التوافق مع المكونات الكبرى | يسبب بطئاً ملحوظاً في التطبيقات الضخمة وذات البيانات المتعددة | مثالي جداً للتطبيقات الكبرى ورفع سرعة الاستجابة وتجربة المستخدم | |
| طريقة الإعلان في الكود | @Component({ ... }) بدون تحديد الاستراتيجية | @Component({ changeDetection: ChangeDetectionStrategy.OnPush | ... }) |
ننتقل في هذا الدرس الثالث من الفصل السادس لنتناول إحدى أهم التقنيات الكلاسيكية والفعالة في تحسين أداء تطبيقات أنجولر، ألا وهي استراتيجية OnPush (ChangeDetectionStrategy.OnPush) في إصدار Angular 22.1. عندما يتم تعيين المكون ليعمل باستراتيجية OnPush، فإن محرك اكتشاف التغييرات يتوقف عن فحص هذا المكون تلقائياً مع كل حدث عابر في التطبيق، ويقوم فقط بإعادة فحصه وتحديثه في حالات محددة ودقيقة للغاية، مما يرفع كفاءة الأداء بشكل مذهل.
لكي تنجح استراتيجية OnPush في عملها، فإنها تفترض أن بيانات المكون (Inputs) لا تتغير بطريقة التعديل المباشر (Mutation)، بل بتمرير مراجع جديدة كلياً أو الاعتماد على نظام الإشارات (Signals) الحديث. عندما تتغير الإشارة المرتبطة بمكون OnPush، يتعرف أنجولر فوراً على وجوب تحديث هذا المكون وتخطى باقي أجزاء الشجرة المستقرة.
إذا تلقى المكون مرجعاً جديداً لخاصية إدخال (@Input()) من المكون الأب.
إذا ضغط المستخدم على زر أو تفاعل مع عنصر DOM يقع حصراً داخل هذا المكون أو أبنائه.
إذا كانت الإشارات المستخدمة داخل قوالب أو منطق المكون قد حدثت قيمتها.
عبر استخدام مرجع كاشف التغييرات (ChangeDetectorRef.markForCheck()).
| 1 | import { Component, Input, ChangeDetectionStrategy, signal } from '@angular/core'; |
| 2 | |
| 3 | @Component({ |
| 4 | selector: 'app-onpush-child', |
| 5 | standalone: true, |
| 6 | changeDetection: ChangeDetectionStrategy.OnPush, // تفعيل استراتيجية OnPush للأداء العالي |
| 7 | template: ` |
| 8 | <div class="card-onpush"> |
| 9 | <h4>المكون الفرعي (OnPush)</h4> |
| 10 | <p>العنوان المستقبل من الأب: <strong>{{ title }}</strong></p> |
| 11 | <p>العداد المحلي (عبر إشارة): <strong>{{ localCounter() }}</strong></p> |
| 12 | |
| 13 | <button (click)="incrementLocal()">زيادة العداد المحلي</button> |
| 14 | </div> |
| 15 | ` |
| 16 | }) |
| 17 | export class OnPushChildComponent { |
| 18 | @Input() title = ''; |
| 19 | |
| 20 | // الإشارات تتوافق بسلاسة تامة مع OnPush وتحدث المكون تلقائياً عند تغيرها |
| 21 | localCounter = signal(0); |
| 22 | |
| 23 | incrementLocal() { |
| 24 | this.localCounter.update(c => c + 1); |
| 25 | } |
| 26 | } |
| 1 | import { Component, signal } from '@angular/core'; |
| 2 | import { OnPushChildComponent } from './onpush-child.component'; |
| 3 | |
| 4 | @Component({ |
| 5 | selector: 'app-onpush-parent', |
| 6 | standalone: true, |
| 7 | imports: [OnPushChildComponent], |
| 8 | template: ` |
| 9 | <div class="parent-box"> |
| 10 | <h3>المكون الرئيسي (Parent Component)</h3> |
| 11 | <p>رقم الإصدار الحالي: <strong>{{ appVersion() }}</strong></p> |
| 12 | |
| 13 | <!-- تمرير البيانات للمكون الفرعي OnPush --> |
| 14 | <app-onpush-child [title]="appVersion()"></app-onpush-child> |
| 15 | |
| 16 | <button (click)="updateVersion()">تحديث الإصدار (تغيير الـ Input)</button> |
| 17 | </div> |
| 18 | ` |
| 19 | }) |
| 20 | export class OnPushParentComponent { |
| 21 | appVersion = signal('Angular v22.1.0-beta'); |
| 22 | |
| 23 | updateVersion() { |
| 24 | // تغيير القيمة يرسل مرجعاً جديداً للمكون الفرعي، مما يحفز OnPush لتحديثه |
| 25 | this.appVersion.set('Angular v22.1.1-final'); |
| 26 | } |
| 27 | } |