واجهة أمامية متطورة. الهيكل المناسب للمواقع السريعة

مرحبا هبر!



لقد تجاهلنا منذ فترة طويلة موضوع المتصفحات و CSS وإمكانية الوصول وقررنا العودة إليه بترجمة مواد النظرة العامة اليوم (الأصل - فبراير 2020). أنا مهتم بشكل خاص برأيك حول تقنية عرض الخادم المذكورة هنا ، بالإضافة إلى مدى إلحاح الحاجة إلى كتاب كامل على HTTP / 2 - ومع ذلك ، فلنتحدث عن كل شيء بالترتيب.



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



دعنا نلقي نظرة على الهيكل العام للواجهة الأمامية. كيف يمكنك التأكد من تحميل الأصول المهمة أولاً وزيادة احتمال أن ينتهي الأمر بهذه الأصول بالفعل في ذاكرة التخزين المؤقت؟



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



نظرة عامة



دعنا نقسم عملية تنزيل التطبيق إلى ثلاث خطوات منفصلة:



  1. العرض الأساسي - كم من الوقت سيستغرق قبل أن يرى المستخدم شيئًا ما؟
  2. تنزيل التطبيق - كم من الوقت سيستغرق قبل أن يرى المستخدم التطبيق؟
  3. – ?




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



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







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



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



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



هناك عدة طرق لإصلاح هذا:



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


تجنب عرض حظر سلاسل الاستعلام



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



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



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


ضع في اعتبارك هذا المثال: يحتوي







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



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


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



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



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



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



في بعض الأحيان لا يمكن التخلص تمامًا من سلسلة الطلبات. في مثل هذه الحالات ، حاول استخدام العلامة preloador preconnect. على سبيل المثال ، قد يتم الاتصال بالموقع الموضح أعلاه fonts.googleapis.comقبل إجراء طلب CSS الفعلي.



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



عادةً ما يتطلب إنشاء اتصال خادم جديد 3 مرات ذهابًا وإيابًا بين المتصفح والخادم:



  1. بحث DNS
  2. إنشاء اتصال TCP
  3. إنشاء اتصال SSL


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



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



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







تقليل حجم الملف واستخدام شبكات توصيل المحتوى (CDN)



يتأثر طول الطلب وحجم الملف بعاملين آخرين تتحكم فيهما: حجم المورد وموقع الخوادم.



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



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



تجاوز الشبكة مع عمال الخدمة



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







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



عامل الخدمة الموضح أدناه يخزن 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 تقسيم الكود الذي تم تحليله تلقائيًا لتقليل الوزن الإجمالي للتنزيلات ، ويتم ذلك باستخدام optimisation.splitChunks . تأكد من تمكين مقطع وقت التشغيل بحيث تظل تجزئة الأجزاء ثابتة ويمكن استخدام التخزين المؤقت طويل المدى بشكل مفيد. كتب إيفان أكولوف دليلاً شاملاً حول مشاركة رمز Webpack وتخزينه مؤقتًا.



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



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







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



يمكن إصلاح ذلك عن طريق إدخال علامة preload linkإذا كنت تعلم أنك ستحتاج بالتأكيد إلى هذه الأجزاء.







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



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



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



ربما تم تصميم التطبيق الخاص بك لعرض نوع من البيانات. فيما يلي بعض النصائح حول كيفية تحميل البيانات مسبقًا وتجنب التأخير في العرض.



لا تنتظر الحزم ، ابدأ في تحميل البيانات على الفور.



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



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



  1. تضمين بيانات الصفحة في مستند HTML
  2. ابدأ في طلب البيانات عبر برنامج نصي مضمّن داخل المستند


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



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



في هذه الحالة ، أو عند تقديم مستند HTML مُخزَّن مؤقتًا باستخدام عامل خدمة ، يمكنك تضمين برنامج نصي مضمن في HTML الذي سيُحمِّل هذه البيانات. يمكن تقديمه كوعد عالمي ، مثل هذا:



window.userDataPromise = fetch("/me")


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



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



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



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



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







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



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



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



عرض جانب الخادم



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



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



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



اقرأ هذا المقال بقلم ميشال جاناشيك ؛ يصف جيدًا كيفية الجمع بين عمال الخدمة والعرض من جانب الخادم.



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



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



الجلب المسبق للموارد



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



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


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



حدد البيانات التي يحتاجها المستخدمون بشدة بشكل استراتيجي.



أعد استخدام البيانات التي تم تحميلها بالفعل ، قم



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



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



خاتمة



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



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



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



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



All Articles