تضمين المورد في أُطر عمل JavaScript

تحسين مقياس "سرعة عرض أكبر محتوى مرئي" في منظومة JavaScript المتكاملة

في إطار مشروع Aurora، عملت Google مع اطّلاعات الويب الرائجة لضمان أدائها الجيد وفقًا لمؤشرات أداء الويب الأساسية. تمّت إتاحة تضمين الخطوط في Angular وNext.js ، كما هو موضّح في الجزء الأول من هذه المقالة. التحسين الثاني الذي سنتناوله هو تضمين ملفات CSS المهمة، وهو مفعَّل الآن تلقائيًا في Angular CLI وجارٍ تنفيذه في Nuxt.js.

تضمين الخطوط

بعد تحليل مئات التطبيقات، اكتشف فريق Aurora أنّ المطوّرين غالبًا ما يضيفون خطوطًا في تطبيقاتهم من خلال الإشارة إليها في عنصر <head> من index.html. في ما يلي مثال على الشكل الذي سيظهر به هذا عند تضمين رموز Material Icons:

<!doctype html>
<html lang="en">
<head>
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  ...
</html>

على الرغم من أنّ هذا النمط صالح ويعمل بشكل كامل، إلا أنّه يمنع عرض التطبيق ويقدّم طلبًا إضافيًا. لفهم ما يحدث بشكل أفضل، اطّلِع على رمز مصدر جدول الأنماط المُشار إليه في ملف HTML أعلاه:

/* fallback */
@font-face {
  font-family: 'Material Icons';
  font-style: normal;
  font-weight: 400;
  src: url(https://fonts.gstatic.com/font.woff2) format('woff2');
}

.material-icons {
  /*...*/
}

لاحظ كيف يشير تعريف font-face إلى ملف خارجي مستضاف على fonts.gstatic.com. عند تحميل التطبيق، على المتصفّح أولاً تنزيل جدول الأنماط الأصلي المُشار إليه في العنوان.

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

بعد ذلك، ينزِّل المتصفّح ملف woff2، ثم يتمكّن أخيرًا من مواصلة عرض التطبيق.

صورة تعرض الطلبَين المقدَّمَين، أحدهما لملف stylesheet للخط والثاني لملف الخط
بعد ذلك، يتم تقديم طلب لتحميل الخط.

من الفرص المتاحة لتحسين الأداء هي تنزيل جدول الأنماط الأولي في وقت التصميم وإدراجه في index.html. ويؤدي ذلك إلى تخطّي رحلة كاملة بالاتجاهين إلى شبكة CDN أثناء التشغيل، ما يقلل من وقت الحظر.

عند إنشاء التطبيق، يتم إرسال طلب إلى شبكة توصيل المحتوى، ما يؤدي إلى جلب جدول الأنماط وإدراجه في ملف HTML، مع إضافة <link rel=preconnect> إلى النطاق. عند تطبيق هذه التقنية، سنحصل على النتيجة التالية:

<!doctype html>
<html lang="en">
<head>
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin >
  <style type="text/css">
  @font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/font.woff2) format('woff2');}.material-icons{/*...*/}</style>
  ...
</html>

تتوفّر ميزة تضمين الخطوط الآن في Next.js وAngular.

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

يتم تفعيل هذا التحسين تلقائيًا من الإصدار 10.2 من Next.js والإصدار 11 من Angular. يتيح كلاهما استخدام خطوط Google وAdobe المضمّنة. من المتوقّع أن تطرح Angular الإصدار الأخير في الإصدار 12.2.

يمكنك العثور على عملية تنفيذ إدراج الخطوط في Next.js على GitHub، والاطّلاع على الفيديو الذي يوضّح عملية التحسين هذه في سياق Angular.

تضمين ملفات CSS المهمة

يتضمّن التحسين الآخر تحسين مقياسَي سرعة عرض المحتوى على الصفحة (FCP) وسرعة عرض أكبر محتوى مرئي (LCP) من خلال تضمين ملفات CSS المهمة. يتضمّن ملف CSS المهم لصفحة كل الأنماط المستخدَمة في عرضها الأوّلي. لمزيد من المعلومات حول الموضوع، اطّلِع على مقالة تأخير تحميل ملفات CSS غير المُهمّة.

لاحظنا أنّ العديد من التطبيقات تحمّل الأنماط بشكل متزامن، ما يؤدي إلى حظر عرض التطبيق. يمكنك حلّ المشكلة بسرعة من خلال تحميل الأنماط بشكل غير متزامن. بدلاً من تحميل النصوص البرمجية باستخدام media="all"، اضبط قيمة السمة media على print، وبعد اكتمال التحميل، استبدِل قيمة السمة بـ all:

<link rel="stylesheet" href="..." media="print" onload="this.media='all'">

ومع ذلك، يمكن أن تؤدي هذه الممارسة إلى وميض المحتوى غير المُنمَّط.

يبدو أنّ الصفحة تومض أثناء تحميل الأنماط.

يعرض الفيديو أعلاه عرض صفحة يتم تحميل أنماطها بشكل غير متزامن. يحدث الوميض لأنّ المتصفّح يبدأ أولاً بتنزيل الأنماط، ثم يعرض محتوى HTML الذي يليه. بعد تنزيل المتصفّح للأنماط، يُشغّل الحدث onload لعنصر الرابط، ويُعدّل سمة media إلى all، ويطبّق الأنماط على DOM.

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

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

لنلقِ نظرة على المثال التالي:

الإجراءات غير المُوصى بها
<head>
   <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
</head>
<body>
  <section>
    <button class="primary"></button>
  </section>
</body>
/* styles.css */
section button.primary {
  /* ... */
}
.list {
  /* ... */
}

مثال قبل تضمين المحتوى

في المثال أعلاه، ستقرأ أدوات التنقّل محتوى styles.css وتُحلّله، وبعد ذلك تتمهّل في مطابقة عنصرَي الاختيار مع رمز HTML وترصد أنّنا نستخدم section button.primary. أخيرًا، ستضمّن Critters الأنماط المقابلة في <head> من الصفحة، ما يؤدي إلى:

الإجراءات الموصى بها
<head>
  <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
  <style>
  section button.primary {
    /* ... */
  }
  </style>
</head>
<body>
  <section>
    <button class="primary"></button>
  </section>
</body>

مثال بعد تضمين المحتوى

بعد تضمين ملفات CSS المهمة في ملف HTML، ستلاحظ أنّ الصفحة لم تعُد ترفرف:

يتم تحميل الصفحة بعد تضمين CSS.

تتوفّر ميزة تضمين CSS المهمّة الآن في Angular ويتم تفعيلها تلقائيًا في الإصدار 12. إذا كنت تستخدم الإصدار 11، يمكنك تفعيلها من خلال ضبط السمة inlineCritical على true في angular.json. لبدء استخدام هذه الميزة في Next.js، أضِف experimental: { optimizeCss: true } إلى next.config.js.

الاستنتاجات

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

مزيد من المعلومات عن التحسينات يمكنك العثور على قائمة شاملة بأعمال التحسين التي نُجريها على "مؤشرات أداء الويب الأساسية" في المقالة تقديم Aurora.