صرفت على تطبيق سرعة ولسه متجرك على Shopify تقيل على الموبايل؟ في متاجر كتير المشكلة مش في الاستضافة ولا التطبيقات، المشكلة في سطر واحد بيقول للمتصفح «أجّل أهم صورة في الصفحة». المقال ده عام لأي متجر Shopify في مصر أو الخليج أو غيرهم، والإصلاح كله في ملفات القالب.
بصراحة: المقال مبني على توثيق Shopify الرسمي وعلى مقال مطوّر يراجع متاجر بشكل دوري، وليس على اختبار على متجر حي عندنا. الكود مأخوذ من التوثيق ومن مقال المصدر، فاختبره على نسخة من القالب قبل النشر.
لماذا تتأخر صورة البانر؟
صورة البانر الرئيسية (Hero) هي غالباً العنصر الذي يُقاس به أكبر عرض للمحتوى (Largest Contentful Paint) على الموبايل. لو عليها خاصية loading="lazy"، يؤجل المتصفح طلبها حتى يحسب تخطيط الصفحة، فتبدأ أهم صورة في التنزيل متأخرة عن صور أقل أهمية.
توثيق Shopify واضح في هذه النقطة: لا تضع التحميل الكسول (Lazy loading) ولا مكتبة JavaScript للتحميل الكسول على صورة الـ LCP أو أي صورة مهمة في الشاشة الأولى. المكتبات القديمة مثل lazysizes وlozad أسوأ، لأنها تضع رابط الصورة في data-src بدل src، فلا يراه ماسح التحميل المسبق في المتصفح أصلاً.
الافتراضي في image_tag يعمل لصالحك
الفلتر image_tag في لغة قوالب Shopify (Shopify Liquid) يتصرف بذكاء إن تركته وحده. بحسب التوثيق، يضع loading="eager" للأقسام من 1 إلى 3، وloading="lazy" للأقسام من 4 فما بعد، ويختار eager أيضاً عندما لا يُعرف ترتيب القسم.
{{ section.settings.image | image_url: width: 2400 | image_tag }}
المشكلة تبدأ حين «يحسّن» أحدهم كل الصور يدوياً، فيكتب loading بنفسه:
{{ section.settings.image | image_url: width: 2400 | image_tag: loading: 'lazy' }}
تمرير loading يلغي السلوك الافتراضي. الآن صار البانر كسولاً أينما وضع التاجر هذا القسم، حتى لو كان أول قسم في الصفحة. القاعدة الأولى إذن: لا تمرر loading إلا إن كنت مضطراً.
افحص متجرك في دقيقتين
- شغّل الصفحة الرئيسية في PageSpeed Insights على وضع الموبايل.
- افتح ملاحظة «LCP request discovery». إن ذكرت أن التحميل الكسول مطبق على صورة الـ LCP، أو أن
fetchpriority=highيجب إضافته، فقد وجدت المشكلة. - أكّد من الكود: افتح «عرض المصدر» في المتصفح، وابحث عن اسم ملف صورة البانر، وانظر إن كان وسم
<img>يحملloading="lazy"أوdata-src.
افحص على المتجر الحي، لا داخل محرر القالب. السبب في القسم التالي.
Shopify
إصلاح تأخر صورة البانر
من الفحص إلى التأكد على المتجر الحي
افحص في PageSpeed
وضع الموبايل، ثم ملاحظة LCP request discovery.
ابحث في القالب
عن loading: 'lazy' و data-src في الأقسام العلوية.
اترك image_tag يقرر
احذف loading اليدوي، أو استخدم شرط section.index > 3.
أولوية للبانر الأول
fetchpriority: 'high' للقسم الأول فقط.
اختبر على المتجر الحي
المحرر لا يعرض fetchpriority لأن الترتيب فارغ فيه.
الإصلاح: قرّر حسب ترتيب القسم
كل قسم في Shopify له المتغير section.index، وهو ترتيبه بدءاً من 1 داخل موقعه (القالب، أو مجموعة أقسام مثل الهيدر). العداد يبدأ من 1 من جديد في كل موقع، والأقسام المعطّلة لا تأخذ رقماً، لذا section.index == 1 تعني «أول قسم في موقعه» وليس بالضرورة أول قسم في الصفحة.
ثيم Dawn يطبق هذا في sections/image-banner.liquid: لا يمرر loading إطلاقاً، ويضيف أولوية الجلب (Fetch Priority) للقسم الأول فقط. هذه نسخة مبسطة (حذفنا حساب sizes وwidths من الإعدادات):
{%- liquid
assign fetch_priority = 'auto'
if section.index == 1
assign fetch_priority = 'high'
endif
-%}
{{
section.settings.image
| image_url: width: 3840
| image_tag:
sizes: '100vw',
fetchpriority: fetch_priority
}}
fetchpriority ليس خياراً مسمّى في الفلتر، لكن image_tag يمرر أي خاصية HTML تعطيها له كما هي إلى وسم <img>.
انتبه: section.index يكون فارغاً أحياناً
يذكر التوثيق ثلاث حالات تكون فيها قيمة section.index فارغة (nil):
- الأقسام الثابتة التي تُستدعى بـ
{% section 'name' %}، مثل قسم داخلtheme.liquid. - أثناء العرض داخل محرر المتجر (Online Store Editor).
- عند إرجاع القسم عبر واجهة عرض الأقسام (Section Rendering API).
هنا نختلف مع مقال المصدر. كاتبه يقترح شرطاً يجعل الحالة الفارغة تسقط إلى lazy ويعتبره الاتجاه الآمن، بينما ينص توثيق Shopify على العكس: افتراضي image_tag نفسه يختار eager عند الفراغ، والصيغة الموصى بها هي اختبار section.index > 3 للتحميل الكسول وترك كل ما عداه، بما فيه الفراغ، يذهب إلى eager. السبب أن nil <= 3 تُقيَّم false في Liquid، فشرط مثل if section.index <= 3 يرسل البانر الثابت إلى فرع الكسل.
لو قسمك مضطر لتحديد loading بنفسه، مثل سلايدر مخصص يجب أن تُحمَّل شريحته الأولى فقط مبكراً، فاكتب الشرط بهذه الطريقة:
{%- liquid
assign img_loading = 'lazy'
assign img_priority = 'auto'
unless section.index > 3
if forloop.first
assign img_loading = 'eager'
endif
endunless
if section.index == 1 and forloop.first
assign img_priority = 'high'
endif
-%}
{{ block.settings.image | image_url: width: 2400 | image_tag: loading: img_loading, fetchpriority: img_priority, sizes: '100vw' }}
الشريحة الأولى في الأقسام الثلاثة الأولى، أو في قسم ترتيبه غير معروف، تُحمَّل فوراً. الشرائح التالية تنتظر. وfetchpriority: 'high' يذهب لأول شريحة في القسم الأول فقط، فإن كان البانر عندك قسماً ثابتاً في theme.liquid فأضف له fetchpriority: 'high' يدوياً لأن شرط == 1 لن يتحقق أبداً.
ملاحظة: داخل محرر المتجر لن ترى fetchpriority="high" على الإطلاق لأن الترتيب فارغ هناك. لهذا يجب أن يكون الاختبار على المتجر الحي.
وماذا عن preload: true؟
يقبل image_tag الخيار preload: true، فيرسل Shopify تلميح تحميل مسبق في ترويسة Link بطلب HTTP، دون أن يغير وسم <img> نفسه. ينصح كاتب المصدر باستخدامه باعتدال، ورأيه أن fetchpriority: 'high' على صورة موجودة في HTML يكفي عادةً.
استخدم preload فقط إن كانت صورة الـ LCP خلفية CSS أو مدفونة عميقاً في الكود فيكتشفها المتصفح متأخراً. تحميل صورتين أو ثلاث مسبقاً يجعلها تتنافس على سرعة الاتصال، وهذا على شبكات الموبايل في المنطقة يضر أكثر مما ينفع.
لا تنسَ خاصية sizes
بدون sizes يفترض المتصفح أن الصورة بعرض الشاشة كاملاً. هذا صحيح لبانر يملأ العرض، لكنه خطأ لصورة بنصف العرض بجانب نص، فينزّل الموبايل ملفاً أكبر بكثير مما يحتاجه المكان. ثيم Dawn يستخدم (min-width: 750px) 50vw, 100vw للبانر المقسوم، ويؤكد توثيق Shopify على تحديد sizes صراحة بدل حسابها بـ JavaScript لأن ذلك يؤخر التنزيل.
في ووردبريس: إن كان عندك متجر WooCommerce أيضاً، فالنواة منذ الإصدار 6.3 تضيف fetchpriority="high" تلقائياً للصورة المرجّح أن تكون LCP وتتجنب التحميل الكسول لأولى صور الصفحة. نفس الخطأ يتكرر هناك حين تفرض إضافة تحسين التحميل الكسول على كل الصور.
القرار: من يصلح ماذا؟
- صاحب متجر بثيم رسمي غير معدّل (Dawn وأمثاله): غالباً لا تحتاج شيئاً. افحص فقط، وإن ظهرت المشكلة فهي من تطبيق أضاف تحميلاً كسولاً، فعطّله أولاً.
- متجر بثيم مخصص أو مشترى من طرف ثالث: ابحث في ملفات القالب عن
loading: 'lazy'وloading="lazy"وdata-src. أي قسم يمكن أن يظهر أعلى الصفحة يحتاج شرطاً، لا قيمة ثابتة. - فريلانسر يبني متاجر: اجعل هذا الفحص جزءاً من تسليم كل مشروع. هو تعديل يستغرق دقائق ويوفر على العميل اشتراكاً شهرياً في تطبيق سرعة لا يحتاجه.
اقرأ أيضًا
الأسئلة الشائعة
س: كيف أعرف أن صورة البانر هي سبب بطء LCP في متجري؟
ج: من PageSpeed Insights على الموبايل، افتح ملاحظة LCP request discovery. إن ذكرت التحميل الكسول أو fetchpriority فالصورة هي السبب.
س: هل أحتاج تطبيق سرعة لإصلاح هذه المشكلة في Shopify؟
ج: لا في أغلب الحالات. الإصلاح تعديل في ملفات Liquid للقسم، وأحياناً يكون تطبيق السرعة نفسه هو مصدر التحميل الكسول.
س: لماذا لا يظهر fetchpriority داخل محرر القالب؟
ج: لأن section.index يكون فارغاً داخل المحرر، فلا يتحقق شرط القسم الأول. اختبر دائماً على المتجر الحي.
س: هل أضع preload على كل صور الصفحة الأولى؟
ج: لا. صورة واحدة على الأكثر، وفقط إن كانت خلفية CSS أو يكتشفها المتصفح متأخراً، لأن عدة صور مسبقة تتنافس على الاتصال.
الخلاصة
أهم صورة في متجرك لازم تكون أول صورة تتحمّل، مش آخرها. شيل loading اليدوي من الأقسام اللي ممكن تظهر فوق، خلّي image_tag يقرر، وحط fetchpriority: 'high' للبانر الأول. افتح PageSpeed النهارده وشوف متجرك واقف فين.
مراجع إضافية: Never lazy-load the LCP image، Load above-the-fold and below-the-fold sections differently with section.index
مرجع المقال (بالإنجليزية): Your Shopify Hero Image Is Probably Lazy-Loaded. Here’s the Liquid Fix.
المقال ده مش ترجمة حرفية: متكيّف للقارئ العربي ومضاف عليه سياق ووردبريس وElementor وJetEngine.



