فيما يتعلق بوصف مؤشرات أداء الواجهة الأمامية، فإن الصناعة لها آرائها الخاصة، باختصار، يتعلق الأمر بأداء الشاشة الأولى وطلاقة الصفحة منظور تحليل الصفحة الطلاقة. [توصية تعليمية ذات صلة: "Angular Tutorial"]
ما هي طلاقة الصفحة؟
يتم تحديد سلاسة الصفحة من خلال معدل الإطارات في الثانية (إطارات في الثانية - الإطارات التي يتم إرسالها في الثانية). بشكل عام، يبلغ معدل تحديث الشاشة للمتصفحات الرئيسية 60 هرتز (يتم تحديثه 60 مرة في الثانية)، ومعدل الإطارات الأمثل هو 60 إطارًا في الثانية كلما زاد معدل الإطارات، كلما كانت الصفحة أكثر سلاسة بمعدل 60 هرتز، يعني أنه سيتم تحديث شاشة العرض كل 16.6 مللي ثانية، مما يعني أن عرض كل صفحة يجب أن يكتمل خلال 16.6 مللي ثانية، وإلا ستفقد الصفحة الإطارات وتتجمد.根因在于:浏览器中的JavaScript 执行和页面渲染会相互阻塞
.
في أدوات تطوير Chrome، يمكننا تنفيذ Cmd+Shift+P وإدخال show fps لفتح لوحة fps بسرعة، كما هو موضح في الشكل التالي:
من خلال مراقبة لوحة FPS، يمكننا بسهولة مراقبة طلاقة الصفحة الحالية.
1 العوامل التي تؤثر على أداء الصفحة
يعتمد ما إذا كان تفاعل الصفحة سلسًا على ما إذا كانت استجابة الصفحة سلسة، واستجابة الصفحة هي في الأساس عملية إعادة عرض التغييرات في حالة الصفحة على الصفحة.
تتم عملية استجابة الصفحة تقريبًا كما يلي:
بشكل عام، لا يستهلك منطق معالجة الأحداث في معالج الأحداث الكثير من الوقت، لذا فإن العوامل التي تؤثر على أداء Angular تكمن بشكل أساسي في异步事件触发
变更检测
. بشكل عام، لا يستهلك منطق معالجة الأحداث في معالج الأحداث الكثير من الوقت، لذا فإن العوامل التي تؤثر على أداء Angular تكمن بشكل أساسي في تشغيل الأحداث غير المتزامنة واكتشاف التغيير.
بالنسبة إلى Angular، فإن عملية عرض الصفحة هي عملية اكتشاف التغيير، ويمكن أن نفهم أن اكتشاف التغيير في Angular يجب أن يكتمل في غضون 16.6 مللي ثانية لتجنب فقدان إطار الصفحة وتأخره.
يمكن تحسين أداء استجابة الصفحة من الجوانب الثلاثة التالية.
(1) بالنسبة لمرحلة حدث التشغيل، يمكن تقليل تشغيل الأحداث غير المتزامنة لتقليل العدد الإجمالي لاكتشافات التغيير وإعادة العرض؛
(2) بالنسبة لمرحلة منطق تنفيذ معالج الأحداث، يمكن تقليل وقت التنفيذ عن طريق تحسين التعقيد منطق الكود
(3) بالنسبة لربط بيانات اكتشاف التغيير ومرحلة تحديث DOM، يمكن تقليل عدد حسابات اكتشاف التغيير وبيانات القالب لتقليل وقت العرض
لـ (2) معالج الأحداث، يجب تحليل مشكلات محددة لن تتم مناقشة التفاصيل بشكل أساسي على (1) (3) ) التحسين
2 خطة التحسين
2.1 تقليل تشغيل الأحداث غير المتزامنة
Angular في وضع اكتشاف التغيير الافتراضي، ستؤدي الأحداث غير المتزامنة إلى اكتشاف التغيير العام سيحسن أداء Angular بشكل كبير.
تتضمن الأحداث غير المتزامنة أحداث مهمة الماكرو وأحداث المهام الصغيرة
إن تحسين الأحداث غير المتزامنة مخصص بشكل أساسي لأحداث الاستماع إلى المستندات. على سبيل المثال، استمع للنقر، ورفع الماوس، وتحريك الماوس... وغيرها من الأحداث في المستند.
سيناريو حدث الاستماع:
Renderer2.listen(document,...)
fromEvent(document,...)
document.addEventListener(...)
يجب إزالة حدث الاستماع DOM عندما لا تكون هناك حاجة إلى تشغيله.
على سبيل المثال:
سيناريوهات استخدام أمر مربع المطالبة [pop]: تصفية أعمدة الجدول، أو النقر في مكان آخر غير الرمز، أو تمرير الصفحة، أو إخفاء المربع المنبثق لمرشح العمود.
كانت الطريقة السابقة هي مراقبة حدث النقر وحدث التمرير مباشرة من المستند في أمر البوب بهذه الطريقة: العيب الوحيد هو عدم عرض مربع المطالبة، ولكن لا تزال هناك أحداث مراقبة، وهو أمر غير معقول للغاية.
الحل المعقول: استمع فقط إلى أحداث النقر والتمرير عندما يتم عرض مربع المطالبة، وقم بإزالة أحداث الاستماع عندما تكون مخفية.
بالنسبة لأحداث استماع DOM التي يتم تشغيلها بشكل متكرر، يمكنك استخدام عوامل rjx لتحسين الأحداث. راجع مشغلي Rjx للحصول على التفاصيل. رخام RxJS.
2.2 اكتشاف التغيير
ما هو اكتشاف التغيير؟
لفهم اكتشاف التغيير، يمكننا البحث عن إجابات من أهداف اكتشاف التغيير. الهدف من اكتشاف التغيير الزاوي هو الحفاظ على مزامنة النموذج (رمز TypeScript) والقالب (HTML). لذلك، يمكن فهم اكتشاف التغيير على أنه: اكتشاف تغييرات النموذج وتحديث القالب ( DOM ) في نفس الوقت .
ما هي عملية الكشف عن التغيير؟
من خلال إجراء الكشف عن التغيير بترتيب من أعلى إلى أسفل في شجرة المكونات، أي أنه يتم إجراء اكتشاف التغيير على المكون الأصلي أولاً، ثم على المكونات الفرعية. تحقق أولاً من تغييرات البيانات الخاصة بالمكون الأصلي، ثم قم بتحديث قالب المكون الأصلي. عند تحديث القالب، عند مواجهة المكون الفرعي، سيتم تحديث القيمة المرتبطة بالمكون الفرعي، ثم أدخل المكون الفرعي لمعرفة ما إذا كان تم تغيير فهرس قيمة إدخال @Input. إذا تغير، قم بتمييز المكون الفرعي على أنه متسخ، الأمر الذي يتطلب اكتشاف التغيير لاحقًا. بعد وضع علامة على المكون الفرعي، استمر في تحديث القالب الموجود خلف المكون الفرعي في المكون الأصلي تم تحديثها، وإجراء تغييرات على الكشف عن المكون الفرعي.
2.2.1 مبدأ اكتشاف التغيير الزاوي
في الوضع الافتراضي لاكتشاف التغيير الافتراضي، مبدأ الأحداث غير المتزامنة التي تؤدي إلى اكتشاف التغيير في Angular هو أن angular تستدعي طريقة التجزئة () الخاصة بـ ApplicationRef عند معالجة الأحداث غير المتزامنة باستخدام Zone.js للتنفيذ من المكون الجذر إلى المكون الفرعي. أثناء عملية تهيئة التطبيق Angular، يتم إنشاء منطقة (NgZone)، ثم يتم تشغيل كل المنطق في الكائن _inner الخاص بالكائن.
تقوم Zone.js بتنفيذ الفئات التالية:
مبدأ عملية الكشف تقريبًا كما يلي:
تؤدي عمليات المستخدم إلى أحداث غير متزامنة (على سبيل المثال: أحداث DOM، طلبات الواجهة...)
=> تتعامل فئة ZoneTask مع الأحداث. يتم استدعاء أسلوب runTask () الخاص بالمنطقة في وظيفة invocateTask (). في أسلوب runTask، تقوم المنطقة بتمرير سمة مثيل _zoneDelegate واستدعاء ربط ZoneSpec
=> وتمرير الخطافات الثلاثة لـ ZoneSpec (onInvocTask، onInvoc، onHasTask) إلى checkStable () وظيفة Triggerzone.onMicrotaskEmpty.emit(null) notification
=> يستدعي المكون الجذر Tick() بعد الاستماع إلى onMicrotaskEmpty، ويستدعي detectChanges()
في أسلوب التجزئة لبدء الكشف من المكون الجذر
=> ··· refreshView()
يستدعي executeTemplate()
، في طريقة executeTemplate
يستدعي templateFn()
لتحديث القيمة المرتبطة بالقالب والمكون الفرعي (这时候会去检测子组件的
输入引用是否改变,如果有改变,会将子组件标记为
قذر ,也就是该子组件需要变更检测
)
مخطط تدفق مبدأ اكتشاف التغيير التفصيلي:
تبسيط العملية:
تشغيل حدث غير متزامن
=> يعالج ZoneTask الحدث
=> يستدعي ZoneDelegate خطاف ZoneSpec لتشغيل إشعار onMicrotaskEmpty
=> يتلقى المكون الجذر إشعار onMicrotaskEmpty، وينفذ القراد ()، ويبدأ في اكتشاف وتحديث dom
كما يتبين من الكود أعلاه،当微任务为空的时候才会触发变更检测
.
مخطط تدفق بسيط لمبدأ الكشف عن التغيير:
مدونة مرجعية لتحليل كود المصدر الزاوي Zone.js.
2.2.2 حل تحسين اكتشاف التغيير
1) استخدم
مبدأ وضع OnPush: تقليل استهلاك الوقت لاكتشاف تغيير واحد.
الفرق بين وضع OnPush والوضع الافتراضي هو أن أحداث استماع DOM وأحداث المؤقت والوعود لن تؤدي إلى اكتشاف التغيير. تكون حالة المكون في الوضع الافتراضي دائمًا هي CheckAlways، مما يعني أنه يجب اختبار المكون في كل دورة اكتشاف.
في وضع OnPush: ستؤدي المواقف التالية إلى تشغيل اكتشاف التغيير
S1 ومرجع @Input الخاص بالمكون المراد تغييره.
S2. الأحداث المرتبطة بـ DOM للمكون، بما في ذلك الأحداث المرتبطة بـ DOM لمكوناته الفرعية، مثل النقر والإرسال والماوس لأسفل. @HostListener()
ملاحظة:
لن تؤدي أحداث DOM التي تتم مراقبتها من خلال renderer2.listen() إلى اكتشاف التغيير.
لن تؤدي طريقة الاستماع الأصلية لـ dom.addEventListener() إلى اكتشاف التغيير،
كما تم تعيين أحداث الاشتراك القابلة للملاحظة في نفس الوقت.
S4 استخدم الطرق التالية لتشغيل اكتشاف التغيير يدويًا:
ChangeDetectorRef.detectChanges(): تشغيل اكتشاف التغيير للمكون الحالي والمكونات الفرعية غير التابعة لـ OnPush.
ChangeDetectorRef.markForCheck(): قم بتمييز العرض الحالي وجميع أسلافه على أنه متسخ، وسيتم تشغيل الاكتشاف في دورة الكشف التالية.
ApplicationRef.tick(): لن يؤدي إلى اكتشاف التغيير
2)
مبدأ استخدام NgZone.runOutsideAngular(): تقليل عدد اكتشافات التغيير
وكتابة مراقبة حدث dom العام في رد الاتصال الخاص بأسلوب NgZone.runOutsideAngular() لا يؤدي إلى اكتشاف التغيير. إذا لم يتم تحديث المكون الحالي، فيمكنك تنفيذ ربط DetectChanges() الخاص بـ ChangeDetectorRef في وظيفة رد الاتصال لتشغيل اكتشاف التغيير للمكون الحالي يدويًا.
مثال: مكون الرمز الديناميكي app-icon-react
2.2.3 طريقة تصحيح الأخطاء
1: يمكنك استخدام المكون الإضافي Angular DevTools في وحدة تحكم المتصفح لعرض حدث DOM معين وحالة اكتشاف angular:
الطريقة الثانية: يمكنك إدخال: ng.profiler.timeChangeDetection() مباشرة في وحدة التحكم لعرض وقت الكشف. يمكن لهذه الطريقة عرض وقت الكشف العام. مدونة مرجعية لتحديد ملامح الكشف عن التغيير الزاوي
2.3 تحسين القالب (HTML)
2.3.1 تقليل عرض DOM: ngFor plus TrackBy
باستخدام سمة TrackBy الخاصة بـ *ngFor، يقوم Angular فقط بتغيير الإدخالات التي تم تغييرها وإعادة عرضها دون الحاجة إلى إعادة تحميل قائمة الإدخال بأكملها.
على سبيل المثال: سيناريو فرز الجدول. إذا تمت إضافة TrackBy إلى ngFor، فسيتم نقل عناصر dom للصف فقط عند عرض الجدول. إذا لم تتم إضافة TrackBy، فسيتم حذف عناصر dom الموجودة في الجدول أولاً، ثم ستتم إضافة عناصر dom للصف. من الواضح أن أداء تحريك عناصر dom فقط سيكون أفضل بكثير.
2.3.2 لا تستخدم الوظائف في تعبيرات القالب.
لا تستخدم استدعاءات الوظائف في تعبيرات القالب Angular. يمكنك استخدام الأنبوب بدلاً من ذلك، أو يمكنك استخدام متغير بعد الحساب اليدوي. عند استخدام الوظائف في القوالب، بغض النظر عما إذا كانت القيمة قد تغيرت أم لا، سيتم تنفيذ الوظيفة في كل مرة يتم فيها اكتشاف التغيير، مما سيؤثر على الأداء.
سيناريوهات استخدام الوظائف في القوالب:
2.3.3 تقليل استخدام ngFor
سيؤثر استخدام ngFor على الأداء عندما تكون كمية البيانات كبيرة.
مثال:
استخدام ngFor:
عدم استخدام ngFor: تم تحسين الأداء بنحو 10 مرات