DEV Community

playgames
playgames

Posted on

لماذا لا أحمّل لعبة الـ iframe فور فتح الصفحة؟

عند تطوير موقع ألعاب متصفح، يبدو الحل الأسرع واضحًا: نضع رابط اللعبة داخل عنصر iframe، ثم نعرض الصفحة للمستخدم.

تقنيًا، اللعبة موجودة وتبدأ في التحميل مباشرة. لكن هذا لا يعني أن التجربة جيدة.

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

على الهاتف واتصال الإنترنت المتوسط، تكون النتيجة واضحة:

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

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

هذه ليست حركة تجميلية. إنها تغير طريقة تحميل الصفحة بالكامل.

لماذا يمثل الـ iframe عبئًا حقيقيًا؟

عنصر iframe ليس مجرد مستطيل يحتوي على صفحة أخرى.

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

  • ملفات JavaScript؛
  • ملفات CSS؛
  • صورًا وأصواتًا؛
  • خطوطًا؛
  • إعلانات؛
  • أدوات تحليل؛
  • اتصالات بخوادم إضافية؛
  • تخزينًا محليًا أو ملفات تعريف ارتباط؛
  • ملفات اللعبة نفسها.

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

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

loading="lazy" مفيد، لكنه ليس الحل الكامل

يمكن إضافة الخاصية التالية:

<iframe
  src="https://example-game-provider.com/game"
  loading="lazy"
  title="لعبة تلبيس"
></iframe>
Enter fullscreen mode Exit fullscreen mode

تطلب loading="lazy" من المتصفح تأجيل تحميل الـ iframe عندما يكون بعيدًا عن الجزء الظاهر من الصفحة. هذا مفيد للعناصر الموجودة أسفل الصفحة، ويقلل تحميل موارد لا يراها المستخدم بعد.

لكن في صفحة لعبة، يكون الـ iframe غالبًا قريبًا من أعلى الصفحة. عندها قد يقرر المتصفح تحميله مبكرًا، لأن المستخدم سيصل إليه بسرعة.

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

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

الحل الذي استخدمته: واجهة غلاف بدل اللعبة

عند فتح الصفحة، أعرض:

  • صورة حقيقية من اللعبة؛
  • اسم اللعبة؛
  • وصفًا قصيرًا؛
  • زر “ابدئي اللعب”؛
  • معلومات أن اللعبة تعمل في المتصفح.

لا يوجد iframe نشط في هذه المرحلة.

<section class="game-shell" data-game-url="https://provider.example/game">
  <img
    src="/images/dress-up-game.webp"
    alt="لعبة تلبيس واختيار فساتين وتسريحات شعر"
    width="960"
    height="540"
  >

  <button type="button" class="play-button">
    ابدئي اللعب
  </button>
</section>
Enter fullscreen mode Exit fullscreen mode

عندما يضغط المستخدم على الزر، ينشئ JavaScript عنصر iframe ويستبدل الغلاف به:

const gameShell = document.querySelector(".game-shell");
const playButton = gameShell.querySelector(".play-button");

playButton.addEventListener("click", () => {
  const gameUrl = gameShell.dataset.gameUrl;

  if (!gameUrl) {
    return;
  }

  const iframe = document.createElement("iframe");

  iframe.src = gameUrl;
  iframe.title = "لعبة متصفح";
  iframe.allow = "fullscreen; autoplay";
  iframe.allowFullscreen = true;
  iframe.referrerPolicy = "strict-origin-when-cross-origin";

  gameShell.replaceChildren(iframe);
});
Enter fullscreen mode Exit fullscreen mode

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

لماذا أفضل هذا على زر يغيّر قيمة src فقط؟

يمكن وضع iframe فارغ ثم إضافة src عند النقر:

<iframe data-src="https://provider.example/game"></iframe>
Enter fullscreen mode Exit fullscreen mode

ثم:

iframe.src = iframe.dataset.src;
Enter fullscreen mode Exit fullscreen mode

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

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

كما يقل خطر أن يبدأ iframe في التحميل بسبب خطأ في القالب أو إضافة تلقائية للخاصية src.

لا تستخدم صورة غلاف عامة

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

الأفضل استخدام لقطة توضّح النشاط الأساسي:

  • شخصية وملابس في لعبة التلبيس؛
  • مطبخ ومكونات في لعبة الطبخ؛
  • وجه وأدوات تجميل في لعبة المكياج؛
  • أثاث وغرفة في لعبة الديكور.

الغلاف ليس إعلانًا. هو معاينة تساعد المستخدم على تحديد ما إذا كانت اللعبة تناسبه.

لهذا أكتب أيضًا نصًا بديلًا واضحًا للصورة، بدل كلمات عامة مثل “أفضل لعبة رائعة”.

احجز مساحة اللعبة قبل بدء التحميل

عند استبدال الغلاف بالـ iframe، يجب ألا يتغير ارتفاع الصفحة فجأة.

استخدمت نسبة أبعاد ثابتة للحاوية:

.game-shell {
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: 1rem;
  background: #f1f1f1;
}

.game-shell img,
.game-shell iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

.game-shell img {
  object-fit: cover;
}
Enter fullscreen mode Exit fullscreen mode

وجود aspect-ratio يعني أن المتصفح يعرف مساحة اللعبة قبل وصول الصورة أو إنشاء الـ iframe.

هذا يمنع المحتوى الموجود أسفل اللعبة من القفز عند بدء التشغيل.

لا تؤجل صورة الغلاف الرئيسية

قد يبدو من المنطقي وضع loading="lazy" على كل الصور، لكن صورة الغلاف الموجودة في أعلى صفحة اللعبة غالبًا هي أكبر عنصر مرئي للمستخدم.

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

لذلك أتعامل مع الصور بهذه الطريقة:

  • صورة اللعبة الرئيسية: تحميل عادي؛
  • أول صف من الألعاب المشابهة: تحميل عادي أو حسب موقعه الفعلي؛
  • البطاقات الموجودة أسفل الصفحة: loading="lazy".
<img
  src="/images/main-game.webp"
  alt="لعبة طبخ وتزيين كعكة"
  width="960"
  height="540"
  fetchpriority="high"
>
Enter fullscreen mode Exit fullscreen mode

ولا أضع fetchpriority="high" على عشر صور. الأولوية المرتفعة يجب أن تبقى للعنصر الأساسي فقط.

ماذا عن الصفحة التي تعرض عشرات الألعاب؟

في صفحة التصنيف، المشكلة ليست iframe بل الصور.

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

هناك أستخدم:

<img
  src="/images/game-card.webp"
  alt="لعبة مكياج وتسريح شعر"
  width="320"
  height="180"
  loading="lazy"
  decoding="async"
>
Enter fullscreen mode Exit fullscreen mode

لكنني لا أضيف loading="lazy" تلقائيًا إلى جميع البطاقات. الصور الظاهرة فور فتح الصفحة يجب أن تصل بسرعة.

أفضل تقسيم عملي هو:

  • الصور داخل أول شاشة: تحميل عادي؛
  • الصور القريبة منها: يمكن أن يقرر المتصفح تحميلها؛
  • بقية القائمة: تحميل متكاسل.

المبالغة في التحميل المتكاسل قد تؤخر الصور التي ينتظرها المستخدم فعلًا.

استخدم صورًا متعددة المقاسات

إرسال صورة بعرض 1200 بكسل إلى بطاقة لا يتجاوز عرضها 240 بكسل يهدر البيانات.

استخدمت srcset حتى يختار المتصفح الحجم المناسب:

<img
  src="/images/game-480.webp"
  srcset="
    /images/game-320.webp 320w,
    /images/game-480.webp 480w,
    /images/game-640.webp 640w
  "
  sizes="
    (max-width: 600px) 50vw,
    (max-width: 1100px) 33vw,
    240px
  "
  alt="لعبة اختيار أزياء أميرة"
  width="320"
  height="180"
  loading="lazy"
>
Enter fullscreen mode Exit fullscreen mode

هذه الخطوة أهم من تبديل امتداد الصورة فقط. صورة WebP كبيرة جدًا قد تبقى أثقل من JPEG بالحجم الصحيح.

لا تجعل الصفحة الرئيسية تحمل الألعاب نفسها

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

الفكرة تبدو سريعة، لكنها سيئة عمليًا.

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

حتى لو استخدمت loading="lazy"، تصبح بنية الصفحة معقدة ويصعب التحكم في الموارد.

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

زر التشغيل يجب أن يكون مختلفًا عن الإعلان

في مواقع الألعاب، يشعر المستخدم بالشك عندما يرى عدة أزرار كبيرة تحمل كلمات مثل:

  • تحميل؛
  • ابدأ الآن؛
  • تشغيل؛
  • تثبيت؛
  • متابعة.

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

استخدمت زرًا واحدًا داخل الغلاف، في مكان ثابت، مع نص واضح:

ابدئي اللعب

لا أستخدم كلمة “تحميل” لأن اللعبة لا تحتاج إلى ملف.

ولا أستخدم “التالي” لأنها لا توضّح ما سيحدث.

أضف حالة تحميل بعد النقر

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

لذلك أعرض مؤشرًا داخل الحاوية حتى يطلق iframe حدث load:

playButton.addEventListener("click", () => {
  const gameUrl = gameShell.dataset.gameUrl;

  gameShell.innerHTML = `
    <div class="game-loading" role="status">
      جارٍ تحميل اللعبة…
    </div>
  `;

  const iframe = document.createElement("iframe");

  iframe.src = gameUrl;
  iframe.title = "لعبة متصفح";
  iframe.allowFullscreen = true;
  iframe.hidden = true;

  iframe.addEventListener("load", () => {
    const loading = gameShell.querySelector(".game-loading");

    if (loading) {
      loading.remove();
    }

    iframe.hidden = false;
  });

  gameShell.appendChild(iframe);
});
Enter fullscreen mode Exit fullscreen mode

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

ضع مهلة وخيار إعادة المحاولة

بعض الألعاب لا تُحمّل بسبب انقطاع الشبكة، حظر النطاق، مشكلة في المصدر أو توقف اللعبة نفسها.

يمكن إضافة مهلة بسيطة:

const timeoutId = window.setTimeout(() => {
  if (!iframe.hidden) {
    return;
  }

  gameShell.innerHTML = `
    <div class="game-error">
      <p>تعذر تحميل اللعبة.</p>
      <button type="button" data-retry>حاولي مرة أخرى</button>
    </div>
  `;
}, 15000);

iframe.addEventListener("load", () => {
  window.clearTimeout(timeoutId);
  iframe.hidden = false;
});
Enter fullscreen mode Exit fullscreen mode

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

لا تثق بأي رابط لعبة يدخل قاعدة البيانات

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

أتحقق من النطاق المسموح:

from urllib.parse import urlparse

ALLOWED_GAME_HOSTS = {
    "games.example-provider.com",
    "cdn.example-provider.com",
}

def is_allowed_game_url(url: str) -> bool:
    parsed = urlparse(url)

    return (
        parsed.scheme == "https"
        and parsed.hostname in ALLOWED_GAME_HOSTS
    )
Enter fullscreen mode Exit fullscreen mode

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

لا أستخدم مقارنة تنتهي باسم النطاق فقط، لأن نطاقًا مثل:

games.example-provider.com.attacker.example

ليس تابعًا للمصدر المسموح.

فكر في صلاحيات iframe

لا أعطي اللعبة صلاحيات أكثر مما تحتاج.

إذا كانت اللعبة تحتاج إلى ملء الشاشة، أضيف ذلك صراحة:

allow="fullscreen; autoplay"
Enter fullscreen mode Exit fullscreen mode

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

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

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

ما علاقة ذلك بالبحث عن ألعاب البنات؟

من يبحث عن العاب بنات لا يهتم باسم تقنية التحميل التي استخدمتها.

لكنه يهتم بنتيجتها مباشرة:

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

الأداء هنا ليس رقمًا في تقرير Lighthouse فقط. الأداء هو تقليل الوقت بين اختيار اللعبة والبدء الفعلي.

لا تجعل نتيجة Lighthouse هي الهدف الوحيد

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

لذلك أختبر مرحلتين منفصلتين:

قبل الضغط على التشغيل

أقيس:

  • سرعة ظهور العنوان والصورة؛
  • استقرار التخطيط؛
  • حجم الصفحة الأولي؛
  • سرعة التفاعل مع زر اللعب.

بعد الضغط على التشغيل

أراقب:

  • الوقت حتى ظهور محتوى اللعبة؛
  • هل يتجمد التمرير؟
  • هل يتغير ارتفاع الحاوية؟
  • هل تعمل اللعبة على الهاتف؟
  • هل تظهر رسائل خطأ؟
  • هل يفتح المصدر نوافذ جديدة؟

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

متى أحمّل اللعبة تلقائيًا؟

لا أرى أن التحميل بعد النقر قاعدة إلزامية لكل موقع.

قد يكون التحميل التلقائي مناسبًا عندما:

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

لكن في دليل يحتوي على عدد كبير من الألعاب ومصادر متعددة، أفضّل التحميل بعد التفاعل.

إنه قرار يمنحني تحكمًا أكبر ويمنع استهلاك الموارد لمن يغادر الصفحة قبل اللعب.

الخلاصة

أكبر خطأ في صفحات ألعاب المتصفح هو افتراض أن وجود iframe يعني أن اللعبة جاهزة.

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

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

الهدف ليس منع تحميل اللعبة، بل تحميلها في اللحظة التي تصبح فيها مفيدة.

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

Top comments (0)