كيفية تحسين أداء تطبيقات الويب الأمامية: خمس نصائح



في كثير من مشاريعي الأمامية ، واجهت في مرحلة ما انخفاضًا في الأداء - يحدث هذا عادة عندما يزداد تعقيد التطبيق ، وهذا أمر طبيعي. ومع ذلك ، لا يزال المطورون مسؤولين عن الأداء ، لذا في مقالتي ، سأقدم خمس نصائح لتحسين التطبيقات التي أطبقها بنفسي: قد يبدو بعضها واضحًا ، وبعضها يؤثر على المبادئ الأساسية للبرمجة - لكن أعتقد أن تحديث الذاكرة ليس ضروريًا سوف يكون. يتم دعم كل نصيحة من خلال الاختبارات: يمكنك تشغيلها بنفسك واختبار الأداء.

ترجمت إلى ألكونوست

مقدمة


تذكر: إذا كانت الشفرة لا تحتاج إلى التحسين ، فلا تدخل فيها. بالطبع ، يجب أن تعمل الشفرة التي تكتبها بسرعة ، ويمكنك دائمًا التوصل إلى خوارزمية أسرع - ولكن يجب أن تظل الشفرة المكتوبة واضحة للمطورين الآخرين. في محاضرة "البرمجة كفن" ، عبر دونالد نوث عن فكرة مهمة للغاية حول تحسين الكود:

كانت المشكلة الحقيقية هي أن المبرمجين قضوا الكثير من الوقت في القلق بشأن الكفاءة في الأماكن غير المناسبة وفي الأوقات غير المناسبة. التحسين السابق لأوانه هو أصل كل أخطاء البرمجة (أو على الأقل معظمها).

1. البحث: بدلاً من المصفوفات العادية - الكائنات والمصفوفات الترابطية


عند العمل مع البيانات ، تنشأ المواقف غالبًا عندما تحتاج ، على سبيل المثال ، إلى العثور على كائن ، والقيام بشيء ما به ، ثم البحث عن كائن آخر ، وما إلى ذلك. تعد بنية البيانات الأكثر شيوعًا في JS عبارة عن مصفوفة ، لذلك يعد تخزين البيانات فيها ممارسة عادية. ومع ذلك ، كلما احتجت إلى العثور على شيء ما في الصفيف ، يجب عليك استخدام طرق مثل "find" أو "indexOf" أو "filter" أو التكرار باستخدام حلقات - أي ، ستحتاج إلى تكرارها عبر العناصر من البداية إلى النهاية. وبالتالي ، نجري بحثًا خطيًا ، يكون تعقيده هو 0 (n) (في أسوأ الحالات ، سوف نحتاج إلى إجراء العديد من المقارنات نظرًا لوجود عناصر في المصفوفة). إذا قمت بهذه العملية عدة مرات على صفائف صغيرة ، فسيكون التأثير على الأداء ضئيلًا. ومع ذلك ، إذا كان لدينا الكثير من العناصر ، وتم تنفيذ العملية عدة مرات ، فسيفشل الأداء بالتأكيد.

في هذه الحالة ، سيكون حلًا جيدًا تحويل صفيف عادي إلى كائن أو صفيف اقتران وإجراء بحث رئيسي: في هذه الهياكل ، يمكنك الوصول إلى العناصر ذات التعقيد O (1) - سنحصل على مكالمة ذاكرة واحدة ، بغض النظر عن الحجم. يتم تحقيق تحسين سرعة العمل من خلال استخدام بنية بيانات تسمى جدول التجزئة .

يمكنك اختبار الأداء هنا: https://jsperf.com/finding-element-object-vs-map-vs-array/1 . فيما يلي نتائجي:



الفرق مهم للغاية: بالنسبة للصفيف الترابطي والكائن ، حصلت على ملايين العمليات في الثانية ، بينما بالنسبة للصفيف ، فإن أفضل نتيجة هي أكثر من مائة عملية بقليل. بالطبع ، لا يتم أخذ تحويل البيانات في الاعتبار هنا ، ولكن حتى مع الأخذ في الاعتبار أن تشغيله سيكون أسرع بكثير.

2. بدلا من الاستثناءات - المشغل الشرطي "إذا"


في بعض الأحيان ، يبدو من الأسهل تخطي التحقق الفارغ والتقاط الاستثناءات المقابلة. هذه ، بطبيعة الحال ، عادة سيئة - لا تحتاج إلى القيام بذلك ، وإذا كان لديك واحدة في التعليمات البرمجية الخاصة بك ، فقط أعد كتابة الأقسام المقابلة. ولكن من أجل إقناعك تمامًا ، سأدعم هذه التوصية من خلال الاختبارات. قررت اختبار ثلاث طرق لإجراء الاختبارات: التعبير "try-catch" ، وحالة "if" ، وحساب "ماس كهربائى".

اختبار: https://jsperf.com/try-catch-vs-conditions/1 . فيما يلي نتائجي:



أعتقد أنه من الواضح من هنا أن التحقق من "لاغٍ" ضروري. بالإضافة إلى ذلك ، كما ترون ، لا يوجد فرق تقريبًا بين شرط "if" وحساب "الدائرة القصيرة" - ثم تنطبق على ما تكمن فيه الروح.

3. دورات أقل ، كان ذلك أفضل


آخر واضح ، ولكن ربما مثيرة للجدل النظر. هناك العديد من الوظائف المريحة للصفائف: "الخريطة" ، "التصفية" ، "التقليل" ، لذلك يبدو استخدامها جذابًا ، والرمز معهم يبدو أكثر سهولة ويسهل قراءته. ولكن عندما يطرح السؤال حول تحسين الإنتاجية ، يمكنك محاولة تقليل عدد الوظائف التي يتم استدعاءها. قررت تحليل حالتين: 1) "مرشح" ، ثم "خريطة" ، و 2) "مرشح" ، ثم "تقليل" - ومقارنتها مع السلسلة الوظيفية ، "forEach" وحلقة "for" التقليدية. لماذا بالضبط هاتين الحالتين؟ من الاختبارات سيتبين أن الفوائد التي تم الحصول عليها قد لا تكون كبيرة جدا. بالإضافة إلى ذلك ، في الحالة الثانية ، حاولت أيضًا استخدام "filter" عند استدعاء "تقليل".

اختبار الأداء لـ "filter" و "map": https://jsperf.com/array-function-chains-vs-single-loop-filter-map/1 . نتائجي:



يمكن ملاحظة أن دورة واحدة أسرع ، لكن الفرق صغير. سبب هذه الفجوة الصغيرة هو عملية "الدفع" ، وهو أمر غير مطلوب عند استخدام "الخريطة". لذلك ، في هذه الحالة ، يمكنك التفكير فيما إذا كان من الضروري حقًا المتابعة إلى دورة واحدة.

الآن دعونا نتحقق من "filter" + "يقلل": https://jsperf.com/array-function-chains-vs-single-loop-filter-reduce/1 . نتائجي:



الفرق هنا أكبر بالفعل: أدى الجمع بين وظيفتين إلى واحدة إلى تسريع التنفيذ بمقدار النصف تقريبًا. ومع ذلك ، فإن الانتقال إلى دورة "for" التقليدية يعطي زيادة أكبر بكثير في السرعة.

4. استخدام منتظم للحلقات


قد تبدو هذه النصيحة مثيرة للجدل أيضًا ، لأن المطورين يحبون الدورات الوظيفية: فهم يقرأون جيدًا ويمكنهم تبسيط العمل. ومع ذلك ، فهي أقل فعالية من الدورات التقليدية. أعتقد أنك قد تلاحظ بالفعل الفرق في استخدام الحلقات ، ولكن دعونا نلقي نظرة عليه في اختبار منفصل: https://jsperf.com/for-loops-in-few-different-ways/ . كما ترون ، بالإضافة إلى الآليات المدمجة ، لقد قمت أيضًا بالتحقق من "كل منها" من مكتبة "Lodash" و "كل" من "jQuery". النتائج:



ومرة أخرى نرى أن أبسط حلقة "من أجل" أسرع بكثير من الباقي. صحيح أن هذه الحلقات جيدة فقط للصفائف - في حالة الكائنات القابلة للتكرار الأخرى ، يجب عليك استخدام "forEach" أو "for ... of" أو المكرر نفسه. ولكن يجب تطبيق "من أجل ... في" فقط إذا لم تكن هناك طرق أخرى على الإطلاق. وتذكر أيضًا أن "for ... in" تقبل جميع خصائص الكائن (وفي الصفات ، تعد الفهارس بمثابة فهارس) ، مما قد يؤدي إلى نتائج غير متوقعة. من المثير للدهشة أن الأساليب المتبعة في Lodash و jQuery لم تكن سيئة للغاية فيما يتعلق بالأداء ، لذا في بعض الحالات يمكنك استخدامها بأمان بدلاً من "forEach" المدمج (من المثير للاهتمام أن الحلقة من Lodash عملت بشكل أسرع من المدمج في).

5. استخدم الوظائف المدمجة للعمل مع DOM


في بعض الأحيان تنظر إلى رمز شخص آخر وترى أن المطور قد استورد jQuery فقط من أجل معالجة DOM - أنا متأكد من أنك شاهدت هذا أيضًا ، لأن هذه واحدة من مكتبات JavaScript الأكثر شيوعًا. من الواضح أنه لا يوجد خطأ في استخدام المكتبات للتحكم في DOM: اليوم نستخدم React و Angular ، ويفعلون نفس الشيء. ومع ذلك ، في بعض الأحيان يبدو للبعض أنه يجب استخدام jQuery حتى في العمليات البسيطة لاستخراج عنصر من DOM وإجراء تغييرات بسيطة عليه.

فيما يلي مقارنة بين الوظائف المدمجة لـ DOM وعمليات JQuery المماثلة في ثلاث حالات مختلفة: https://jsperf.com/native-dom-functions-vs-jquery/1 . نتائجي:



ومرة أخرى ، تبين أن الوظائف الأساسية - "getElementById" و "getElementsByClassName" - كانت الأسرع عند عرض DOM. في حالة المعرفات والمحددات المتقدمة ، يكون querySelector أيضًا أسرع من مسج. وفي حالة واحدة فقط ، يكون "querySelectorAll" أبطأ من jQuery (الحصول على العناصر حسب اسم الفئة). لمزيد من المعلومات حول كيفية استبدال jQuery وكيفيه ، انظر هنا: http://youmightnotneedjquery.com .

من الواضح أنك إذا كنت تستخدم المكتبة بالفعل لإدارة DOM ، فمن المستحسن بشدة الالتزام بها - ومع ذلك ، في الحالات البسيطة ، تكون الأدوات المدمجة كافية.

مواد إضافية


ستساعدك هذه النصائح الخمس في كتابة رمز JavaScript أسرع. ولكن إذا كنت مهتمًا بقراءة المزيد حول تحسين الأداء ، فإليك بعض التوصيات:

1. تحسين حزم JavaScript باستخدام Webpack: يعد هذا موضوعًا واسعًا للغاية ، ولكن إذا تم كل شيء بشكل صحيح ، فيمكن تسريع تحميل التطبيقات بشكل كبير.

2. هياكل البيانات ، الخوارزميات الأساسية وتعقدها: يعتقد الكثيرون أن هذه "مجرد نظرية" ، ولكن في الفقرة الأولى رأينا كيف تعمل هذه النظرية في الممارسة العملية.

3. الاختبارات على صفحة jsPerf : هنا يمكنك التعرف على طرق مختلفة لإنجاز المهمة نفسها في JavaScript وفي الوقت نفسه ترى مؤشرا هاما في الممارسة - الفرق في السرعة.

عن المترجم

تمت ترجمة المقال من قبل Alconost.

تقوم Alconost بترجمة الألعاب والتطبيقات والمواقع بـ 70 لغة. مترجمون من اللغة الأم ، واختبار لغوي ، ومنصة سحابة مع واجهة برمجة التطبيقات ، والتعريب المستمر ، ومديري المشاريع على مدار الساعة وطوال أيام الأسبوع ، أي تنسيق لموارد السلسلة.

نحن أيضًا نصنع مقاطع فيديو للإعلان والتدريب - للمواقع التي تبيع ، الصور ، الإعلانات ، التدريب ، المضايقون ، المستكشفون ، المقطورات لجوجل بلاي ومتجر التطبيقات.

اقرأ المزيد

Source: https://habr.com/ru/post/ar456808/


All Articles