تسريع الواجهة. عندما تكون الكثير من طلبات الخادم جيدة

توضح هذه المقالة بعض الطرق لتسريع تحميل التطبيقات الأمامية لتنفيذ واجهة مستخدم سريعة الاستجابة.

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

تنقسم عملية التنزيل إلى ثلاث مراحل. لكل مرحلة ، نقوم بصياغة استراتيجيات عامة لزيادة الإنتاجية:

  1. العرض الأولي : كم من الوقت يستغرق المستخدم لرؤية شيء ما على الأقل
    • تقليل عرض طلبات الحظر
    • تجنب السلاسل المتسلسلة
    • إعادة استخدام اتصالات الخادم
    • عمال الخدمة للعرض الفوري
  2. : ,
    • . .
    • ,


  3. :
    • ,


حتى العرض الأولي ، لا يرى المستخدم أي شيء على الشاشة. ماذا نحتاج لهذا العرض؟ كحد أدنى ، قم بتحميل مستند HTML ، وفي معظم الحالات موارد إضافية ، مثل ملفات CSS و JavaScript. بمجرد أن تصبح متاحة ، يمكن للمتصفح أن يبدأ في عرض ما.

يتم توفير مخططات WebPageTest طوال هذه المقالة . من المحتمل أن يبدو تسلسل الاستعلام الخاص بموقعك شيئًا كهذا.



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

(ملاحظة: في لقطة الشاشة ، يعد gov.uk مثالاً على تمكين HTTP / 2 الآنحتى يتمكن مجال الموارد من إعادة استخدام اتصال موجود. انظر أدناه لاتصالات الخادم.)

تقليل عرض طلبات الحظر


تمنع أوراق الأنماط والنصوص البرمجية (افتراضيًا) عرض أي محتوى أسفلها.

هناك عدة خيارات لإصلاح هذا:

  • انقل علامات النص البرمجي إلى الجزء السفلي من النص
  • تنزيل البرامج النصية في وضع غير متزامن باستخدام async
  • إذا كان يجب تحميل JS أو CSS بالتسلسل ، فمن الأفضل تضمينها بمقتطفات صغيرة

تجنب المحادثات مع الطلبات المتسلسلة التي تمنع العرض


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

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

  • قواعد @importCSS
  • خطوط الويب المشار إليها بواسطة ملف CSS
  • جافا سكريبت أو علامات البرنامج النصي للتحميل

ألق نظرة على هذا المثال: يقوم



أحد ملفات CSS على هذا الموقع بتحميل خط Google من خلال القاعدة @import. هذا يعني أن المتصفح يجب أن يتناوب في تنفيذ الطلبات التالية:

  1. مستند HTML
  2. تطبيقات CSS
  3. CSS لخطوط جوجل
  4. ملف Google Font Woff (لا يظهر في الرسم التخطيطي)

لإصلاح ذلك ، قم أولاً بنقل طلب CSS لخطوط Google من العلامة @importإلى الرابط في مستند HTML. لذلك نقوم بتقصير السلسلة برابط واحد.

لتسريع الأمور بشكل أكبر ، قم بتضمين خطوط Google CSS مباشرة في ملف HTML أو CSS.

(ضع في اعتبارك أن استجابة CSS من خادم خطوط Google تعتمد على سطر وكيل المستخدم. إذا قدمت طلبًا باستخدام IE8 ، فسوف تشير CSS إلى ملف EOT ، وستتلقى IE11 ملف woff ، وستتلقى المتصفحات الحديثة ملف woff2. إذا وافقت على ذلك ستقتصر المتصفحات القديمة على خطوط النظام ، يمكنك ببساطة نسخ ولصق محتويات ملف CSS لنفسك).

حتى بعد بدء العرض ، من غير المحتمل أن يتمكن المستخدم من التفاعل مع الصفحة ، لأن الخط يحتاج إلى تحميل لعرض النص. هذا تأخير إضافي للشبكة أود تجنبه. معلمة المبادلة مفيدة هنا ، فهي تتيح لك استخدامها font-displayمع خطوط Google ، وتخزين الخطوط محليًا.

في بعض الأحيان لا يمكن حل سلسلة الاستعلام. في مثل هذه الحالات ، قد ترغب في التفكير في علامة التحميل المسبق أو علامة الاتصال المسبق . على سبيل المثال ، قد يتصل موقع الويب في المثال أعلاه fonts.googleapis.comقبل وصول طلب CSS الفعلي.

إعادة استخدام اتصالات الخادم لتسريع الطلبات


لإنشاء اتصال جديد بالخادم ، عادة ما يتطلب تبادل ثلاثة حزم بين المتصفح والخادم:

  1. بحث DNS
  2. قم بتأسيس اتصال TCP
  3. قم بإنشاء اتصال SSL

بعد إنشاء الاتصال ، يلزم تبادل حزمة واحد على الأقل لإرسال طلب وتلقي استجابة.

يوضح الرسم البياني أدناه أننا بدأنا اتصالًا مع أربعة خوادم مختلفة: hostgator.com، optimizely.comو googletagmanager.com، و googelapis.com.

ومع ذلك ، قد تعيد طلبات الخادم اللاحقة استخدام اتصال موجود . يتم التنزيل بشكل أسرع base.cssإما index1.cssلأنه موجود على نفس الخادم hostgator.comالذي تم بالفعل إنشاء الاتصال معه.



تقليل حجم الملف واستخدام CDN


يمكنك التحكم في عاملين يؤثران على وقت تنفيذ الاستعلام: حجم ملفات الموارد وموقع الخوادم.

أرسل أقل قدر ممكن من البيانات إلى المستخدم وتأكد من ضغطها (على سبيل المثال ، باستخدام brotli أو gzip).

تمتلك شبكات توصيل المحتوى (CDN) خوادم في جميع أنحاء العالم. بدلاً من الاتصال بخادم مركزي ، يمكن للمستخدم الاتصال بخادم CDN الأقرب. وبالتالي ، سيكون تبادل الحزم أسرع بكثير. هذا مناسب بشكل خاص للموارد الثابتة مثل CSS وجافا سكريبت والصور ، حيث يسهل توزيعها عبر CDN.

القضاء على الكمون الشبكة مع عمال الخدمة


يسمح لك عمال الخدمة باعتراض الطلبات قبل إرسالها إلى الشبكة. هذا يعني أن الجواب يأتي على الفور تقريبا !



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

يقوم عامل الخدمة أدناه بتخزين HTML و CSS اللازمين للتخزين المؤقت للصفحة. عندما يتم تحميل التطبيق مرة أخرى ، يحاول إصدار الموارد المخزنة مؤقتًا نفسها - ولا يصل إلى الشبكة إلا إذا كانت غير متوفرة.

self.addEventListener("install", async e => {
 caches.open("v1").then(function (cache) {
   return cache.addAll(["/app", "/app.css"]);
 });
});

self.addEventListener("fetch", event => {
 event.respondWith(
   caches.match(event.request).then(cachedResponse => {
     return cachedResponse || fetch(event.request);
   })
 );
});

في هذا الدليل ، تم شرحه بالتفصيل حول استخدام عمال الخدمة `` للتحميل المسبق وموارد التخزين المؤقت.

تنزيل التطبيق


لذا ، يرى المستخدم شيئًا على الشاشة. ما الخطوات الإضافية اللازمة لاستخدام التطبيق؟

  1. تحميل كود التطبيق (JS و CSS)
  2. قم بتنزيل البيانات المطلوبة للصفحة
  3. تنزيل بيانات وصور إضافية



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

قم بتنزيل الكود الضروري فقط وقم بزيادة عدد الزيارات في ذاكرة التخزين المؤقت


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

كقاعدة ، يتم تقسيم الرمز إلى الأجزاء التالية:

  • رمز لصفحة معينة (خاص بالصفحة)
  • كود التطبيق المشترك
  • وحدات الجهات الخارجية التي نادرًا ما تتغير (رائعة للتخزين المؤقت!)

يمكن لـ Webpack القيام بهذا التحسين تلقائيًا ، وكسر الرمز ، وتقليل الوزن الإجمالي للحمل. يتم تقسيم الكود إلى أجزاء باستخدام كائن optimization.splitChunks . افصل وقت التشغيل (وقت التشغيل) إلى ملف منفصل: بهذه الطريقة يمكنك الاستفادة من التخزين المؤقت على المدى الطويل. كتب إيفان أكولوف دليلاً مفصلاً حول تقسيم الحزمة إلى ملفات منفصلة وتخزينها مؤقتًا في Webpack .

لا يمكن تخصيص رمز لصفحة معينة تلقائيًا. يجب عليك تحديد الأجزاء التي يمكن تنزيلها بشكل منفصل يدويًا. غالبًا ما يكون هذا مسارًا محددًا أو مجموعة صفحات. استخدم عمليات الاستيراد الديناميكية لتحميل هذا الرمز بتكاسل.

سيؤدي تقسيم الحزمة الإجمالية إلى أجزاء إلى زيادة عدد الطلبات لتنزيل تطبيقك. لكن هذه ليست مشكلة كبيرة إذا تم تنفيذ الطلبات بالتوازي ، خاصة إذا تم تحميل الموقع باستخدام بروتوكول HTTP / 2. يمكنك رؤية هذا للاستعلامات الثلاثة الأولى في الرسم التخطيطي التالي:



ومع ذلك ، يظهر استعلامان متتاليان أيضًا في الرسم التخطيطي. هذه الأجزاء مطلوبة فقط لهذه الصفحة المحددة ويتم تحميلها ديناميكيًا من خلال import().

يمكنك محاولة إصلاح المشكلة عن طريق إدراج علامة التحميل المسبق للعلامة .



ولكننا نرى أن إجمالي وقت تحميل الصفحة زاد.

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

تحميل البيانات لصفحة


من المحتمل أن يعرض تطبيقك بعض البيانات. إليك بعض النصائح التي يمكنك استخدامها لتنزيل هذه البيانات في وقت مبكر دون تأخيرات غير ضرورية في العرض.

لا تنتظر التنزيل الكامل للحزمة قبل البدء في تنزيل البيانات


فيما يلي حالة خاصة لسلسلة من الطلبات المتسلسلة: تقوم بتنزيل حزمة التطبيق بالكامل ، ثم يطلب هذا الرمز البيانات اللازمة للصفحة.

هناك طريقتان لتجنب هذا:

  1. تضمين البيانات في مستند HTML
  2. قم بتشغيل طلب بيانات باستخدام البرنامج النصي المضمن داخل المستند

يضمن تضمين البيانات في HTML أن التطبيق لا ينتظر تحميله. كما أنه يقلل من تعقيد النظام ، حيث لا تحتاج إلى معالجة حالة التمهيد.

ومع ذلك ، هذه ليست فكرة جيدة إذا كانت مثل هذه التقنية تؤخر العرض الأولي.

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

window.userDataPromise = fetch("/me")

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

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

لا تحظر العرض أثناء انتظار البيانات غير ذات الصلة


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

على سبيل المثال ، قد تقوم أداة تحليلات أولاً بتنزيل قائمة بجميع الرسوم البيانية قبل تحميل البيانات. هذا يسمح للمستخدم بالبحث الفوري عن الرسم التخطيطي الذي يهمه ، ويساعد أيضًا على توزيع طلبات الواجهة الخلفية على خوادم مختلفة.



تجنب استعلامات البيانات المتتالية


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

بدلاً من الاستعلام عن المستخدم الذي قام بتسجيل الدخول أولاً ثم طلب قائمة بمجموعاته ، قم بإرجاع قائمة المجموعات على الفور مع معلومات المستخدم في الاستعلام الأول. يمكنك استخدام GraphQL لهذا ، لكن نقطة النهاية user?includeTeams=trueتعمل أيضًا بشكل جيد.

تقديم جانب الخادم


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

نظرًا لأن الخادم يرسل العميل HTML ثابت فقط ، فإن التطبيق غير تفاعلي في هذه المرحلة. تحتاج إلى تنزيل التطبيق نفسه ، وبدء منطق العرض ، ثم توصيل مستمعي الحدث الضروريين بـ DOM.

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

فيتوضح هذه المقالة التي كتبها Mikhail Yanashek كيفية الجمع بين عمال الخدمة والعرض من جانب الخادم.

الصفحة التالية


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

التحميل المسبق للموارد


إذا قمت بالتحميل المسبق للرمز المطلوب للصفحة التالية ، فسيختفي التأخير عندما يبدأ المستخدم التنقل. استخدم علامات الجلب المسبق أو webpackPrefetchللاستيراد الديناميكي:

import(
    /* webpackPrefetch: true, webpackChunkName: "todo-list" */ "./TodoList"
)

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

فكر بشكل استراتيجي في أجزاء التطبيق التي سيحتاجها المستخدم من قبل.

إعادة استخدام البيانات التي تم تنزيلها بالفعل


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

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

استنتاج


تسرد هذه المقالة عددًا من العوامل التي يمكن أن تبطئ صفحتك في مراحل مختلفة من عملية التحميل. ستساعدك أدوات مثل Chrome DevTools و WebPageTest و Lighthouse في معرفة أي من هذه العوامل تؤثر على تطبيقك.

من الناحية العملية ، نادرًا ما يتم التحسين على الفور في جميع الاتجاهات. نحتاج إلى معرفة التأثير الأكبر على المستخدمين والتركيز عليه.

أثناء كتابة المقال ، أدركت شيئًا مهمًا واحدًا: كان لدي اعتقاد راسخ بأن العديد من طلبات الخادم الفردية كانت سيئة للأداء. كان هذا هو الحال في الماضي ، عندما تطلب كل طلب اتصالًا منفصلاً ، وسمحت المتصفحات ببعض الاتصالات فقط لكل نطاق. ولكن مع HTTP / 2 والمتصفحات الحديثة ، لم يعد هذا هو الحال.

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

All Articles