أفضل ممارسات التحميل الكسول

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

التركيز على التفاصيل

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

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

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

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

let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
  // lazy-loading image code goes here
}, {
  rootMargin: "0px 0px 256px 0px"
});

إذا كانت قيمة rootMargin تبدو مشابهة للقيم التي كان تحدّدها للسمة margin في CSS، يكون السبب أنّها كذلك. في هذه الحالة، يتم توسيع الهامش السفلي للعنصر الذي تم رصده (إطار عرض المتصفّح تلقائيًا، ولكن يمكن تغييره إلى عنصر معيّن باستخدام السمة root) بمقدار 256 بكسل. وهذا يعني أن دالة الاستدعاء سيتم تنفيذها عندما يكون عنصر صورة ضمن 256 بكسل من إطار العرض وسيبدأ تحميل الصورة قبل أن يراها المستخدم بالفعل.

لتحقيق هذا التأثير نفسه في المتصفحات التي لا تتيح مراقبة التقاطع، استخدِم رمز معالجة أحداث التمرير وعدِّل عملية فحص getBoundingClientRect لتضمين مخزن مؤقت.

متغيّرات التصميم والعناصر النائبة

يمكن أن تؤدي وسائط التحميل الكسول إلى تغيُّر التنسيق في حال عدم استخدام العناصر النائبة. قد تكون هذه التغييرات مربكة للمستخدمين وتؤدي إلى تنفيذ عمليات تخطيط DOM باهظة الثمن، والتي تستهلك موارد النظام وتساهم في إيقاف مؤقت. على الأقل، يمكنك استخدام عنصر نائب بلون واحد مصمت ويشغل نفس أبعاد الصورة المستهدَفة، أو أساليب مثل LQIP أو SQIP التي تشير إلى محتوى أحد عناصر الوسائط قبل تحميله.

بالنسبة إلى علامات <img>، يجب أن توجِّه السمة src في البداية إلى عنصر نائب إلى أن يتم تعديل هذه السمة باستخدام عنوان URL النهائي للصورة. استخدِم السمة poster في العنصر <video> للإشارة إلى صورة عنصر نائب. بالإضافة إلى ذلك، يمكنك استخدام السمتَين width وheight على كل من علامتَي <img> و<video>. ويضمن ذلك ألّا يؤدي الانتقال من العناصر النائبة إلى الصور النهائية إلى تغيير الحجم المعروض للعنصر أثناء تحميل الوسائط.

تأخير في فك ترميز الصور

إنّ تحميل الصور الكبيرة في JavaScript وإفلاتها في نموذج العناصر في المستند (DOM) قد يؤدي إلى ربط سلسلة التعليمات الرئيسية، ما يتسبّب في عدم استجابة واجهة المستخدم لفترة زمنية قصيرة أثناء حدوث فك الترميز. إنّ فك ترميز الصور بشكل غير متزامن باستخدام الطريقة decode قبل إدراجها في نموذج العناصر في المستند (DOM) يمكن أن يؤدي إلى تقليل هذا النوع من البيانات غير المرغوب فيها، ولكن يجب الانتباه لأنّ هذه الميزة غير متاحة في كل مكان بعد، وتزيد من مستوى التعقيد في منطق التحميل الكسول. إذا كنت تريد استخدامه، يجب البحث عنه. يمكنك الاطّلاع أدناه على كيفية استخدام Image.decode() مع إجراء احتياطي:

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

if ("decode" in newImage) {
  // Fancy decoding logic
  newImage.decode().then(function() {
    imageContainer.appendChild(newImage);
  });
} else {
  // Regular image load
  imageContainer.appendChild(newImage);
}

راجع رابط CodePen هذا لرؤية رمز مشابه لهذا المثال عمليًا. إذا كانت معظم صورك صغيرة إلى حدٍ ما، قد لا يفيدك ذلك كثيرًا، ولكنه يمكن أن يساعد بالتأكيد في التقليل من البيانات غير المرغوب فيها عند التحميل الكسول للصور الكبيرة وإدراجها في نموذج العناصر في المستند (DOM).

عند عدم تحميل العناصر

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

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

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

newImage.onerror = function(){
  // Decide what to do on error
};
newImage.onload = function(){
  // Load the image
};

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

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

مدى توفّر JavaScript

ولا يجب افتراض أن لغة JavaScript متاحة دائمًا. إذا كنت تنوي تحميل صور باستخدام طريقة "التحميل الكسول"، يمكنك تقديم ترميز <noscript> الذي سيعرض الصور في حال عدم توفّر JavaScript. يتضمن أبسط مثال احتياطي ممكن استخدام عناصر <noscript> لعرض الصور في حال إيقاف JavaScript:

أنا صورة!

في حال إيقاف JavaScript، ستظهر للمستخدمين كل من صورة العنصر النائب والصورة المضمّنة في عناصر <noscript>. لحلّ هذه المشكلة، ضَع فئة من no-js على العلامة <html> على النحو التالي:

<html class="no-js">

بعد ذلك، ضَع سطرًا واحدًا من النص البرمجي المضمّن في <head> قبل طلب أي أوراق أنماط عبر علامات <link> التي تزيل الفئة no-js من العنصر <html> في حال تفعيل JavaScript:

<script>document.documentElement.classList.remove("no-js");</script>

أخيرًا، يمكنك استخدام بعض CSS لإخفاء العناصر بفئة "الاستجابة الكسولة" عند عدم توفّر JavaScript:

.no-js .lazy {
  display: none;
}

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