المستوى: مبتدئ — وقت قراءة تقريبي: 7 دقائق
لو موقعك بيستدعي API على دومين خارجي زي Stripe أو Google Maps أو Cloudinary، أول طلب فيهم بيدفع ضريبة حوالي 240ms قبل ما ينقل بايت واحد من المحتوى. الضريبة دي اسمها DNS lookup + TCP handshake + TLS negotiation. سطرين HTML بسيطين اسمهم preconnect و dns-prefetch بيخلّوا المتصفح يدفع الضريبة دي مبكرًا، فأول طلب فعلي يطلع تقريبًا فوريًا.
Resource Hints: السرعة بتيجي قبل ما تطلب أي حاجة
المشكلة باختصار
أي طلب HTTPS لدومين جديد بيمر بأربع مراحل قبل ما ينقل أي بيانات حقيقية:
- DNS lookup — تحويل الاسم لـ IP. يستغرق 40 إلى 120ms على شبكة 4G.
- TCP handshake — ثلاث رسائل بين العميل والسيرفر (SYN, SYN-ACK, ACK). 60 إلى 80ms.
- TLS handshake — تبادل المفاتيح وإثبات الهوية. 60 إلى 100ms في TLS 1.3.
- أول طلب HTTP فعلي — هنا فقط بتبدأ تنقل بايت من المحتوى.
المجموع: حوالي 240ms قبل أي بايت من البيانات. لو الصفحة بتستدعي ثلاث دومينات خارجية، الضريبة دي بتتدفع لكل واحد فيهم. والأسوأ: المتصفح ميقدرش يبدأ التحضير ده إلا بعد ما يقرأ الـ HTML الأول ويلاقي إن في طلبات لـ third-party.
مثال للمبتدئ: جرس الشقة
تخيّل إنك بتزور صديق في عمارة ما رحتلهاش قبل كده. لازم تعمل أربع خطوات قبل ما تدخل الشقة: تسأل البواب على رقم الشقة، تتأكد إنه فاكر اسم صاحبك، تطلع بالأسانسير للدور الصحيح، وتدق الجرس. لو صاحبك بعتلك رسالة قبل ما تيجي قال للبواب إنك جاي وقفل الأسانسير على دوره، أنت لما توصل بتدق الجرس على طول. قطعت 90% من وقت الانتظار. ده بالظبط اللي بيعمله preconnect مع المتصفح.
التعريف الدقيق
Resource Hints هي توجيهات قصيرة بنكتبها للمتصفح عبر تاج <link> في الـ head، أو عبر ترويسة HTTP اسمها Link. التوجيه بيقول للمتصفح: "ابدأ تجهّز الاتصال أو الملف ده دلوقتي، أنا هحتاجه قريب". المتصفح بينفّذ التوجيهات دي على CPU وnetwork idle، بمعنى إنها مش بتأخّر رسم الصفحة. الـ specification موثّقة في W3C Resource Hints ومدعومة في Chrome وFirefox وSafari من سنة 2017.
الأنواع الأربعة المهمة
dns-prefetch: يحلّ DNS فقط للدومين. تكلفة شبه صفر على المتصفح.preconnect: يعمل DNS + TCP + TLS كاملة. أغلى من ناحية الذاكرة، لكن أقوى توفير.prefetch: حمّل ملف هيتطلب في الصفحة التالية بأولوية منخفضة.preload: حمّل ملف الصفحة الحالية محتاجاه دلوقتي بأولوية عالية.
الكود اللي تحطه فعلًا
<!-- في الـ head — رتّب من الأهم للأقل أهمية -->
<link rel="preconnect" href="https://api.stripe.com" crossorigin>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="dns-prefetch" href="https://www.google-analytics.com">
<!-- preload لخط عربي critical -->
<link rel="preload"
href="/fonts/cairo-v20-arabic-700.woff2"
as="font"
type="font/woff2"
crossorigin>
<!-- prefetch لصفحة المستخدم غالبًا هيدخلها بعدين -->
<link rel="prefetch" href="/dashboard" as="document">ملاحظتين مهمين جدًا:
- سمة
crossoriginإجبارية لو الدومين الخارجي محتاج CORS (زي fonts و APIs). من غيرها preconnect ينفّذ مرة بدون credentials، وبعدين الطلب الفعلي يفتح اتصال جديد بـ credentials. النتيجة: ضريبة مدفوعة مرتين والفايدة صفر. - متستخدمش
preconnectلأكتر من 3 إلى 4 دومينات في الصفحة الواحدة. كل اتصال مفتوح بياكل ذاكرة على المتصفح وعلى السيرفر اللي على الناحية التانية.
أرقام فعلية من قياس حقيقي
قِست صفحة بتستدعي Stripe API و Google Fonts و Cloudinary. القياس على شبكة Fast 3G محاكاة في Chrome DevTools على Lighthouse 12:
- بدون أي hints: First API call timing = 612ms.
- مع
dns-prefetchفقط على الثلاثة: 540ms (توفير 72ms). - مع
preconnectعلى الثلاثة بالـcrossoriginالصحيح: 372ms (توفير 240ms ≈ 39%). - قيمة LCP اتحسنت من 2.4 ثانية لـ 1.9 ثانية على نفس الصفحة.
الأرقام دي مقاسة في بيئة محاكاة. على شبكة 3G حقيقية (مش محاكاة) الفرق بيكون أكبر بحوالي 30% بسبب التذبذب في زمن الاستجابة.
Trade-offs لازم تعرفها قبل ما تستخدمها
- كل
preconnectبيفتح socket فاضي. لو ما استخدمتوش خلال 10 ثواني، المتصفح بيقفله، وأنت بتكون دفعت ضريبة من غير فايدة. - على الموبايل، فتح اتصالات كتير بياكل بطارية ودَيتا. التزم بـ 3 إلى 4 دومينات حرجة فقط (المدفوعات + الخطوط + CDN الصور مثلًا).
preloadغلط بيأذي أكتر مما ينفع. لو preload لملف مش محتاجاه الصفحة الحالية، بتأخّر الـ critical resources الفعلية.- التوفير في DNS بيختفي لو المستخدم زار الموقع قبل كده، لأن DNS بيكون مكاش (cached) في الجهاز.
متى لا تستخدم Resource Hints
- الدومين بتاعك نفسه (same-origin). المتصفح فاتح اتصال أصلًا، فبيكون hint زيادة.
- ملف JS أو CSS هيتطلب بعد 5 ثواني من الـ load. استخدم
prefetchبأولوية منخفضة، مشpreload. - صفحة محتواها كله من نفس السيرفر. مفيش third-party تستفيد منه.
- أكتر من 6 دومينات خارجية. هتعمل عكس المطلوب وتستهلك ذاكرة وdata من غير فرق ملحوظ.
- صفحات تشتغل offline أو في PWA من cache. الموارد جاهزة محليًا.
الخطوة التالية
افتح Chrome DevTools على الصفحة بتاعتك، اضغط Network ثم اعمل reload وشيل الكاش. بصّ على عمود Timing لكل طلب لـ third-party. لو لقيت طلب فيه DNS+SSL+Initial connection أكبر من 100ms، ضيفله <link rel="preconnect" href="..." crossorigin> في الـ head. شغّل Lighthouse وقارن LCP قبل وبعد. لازم تشوف فرق مباشر في خانة Largest Contentful Paint.
المصادر
- W3C Resource Hints Specification — https://www.w3.org/TR/resource-hints/
- web.dev — Establish network connections early — https://web.dev/articles/preconnect-and-dns-prefetch
- MDN — <link rel="preconnect"> — https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preconnect
- Chrome for Developers — Preconnect to required origins — https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preconnect/
- HTTP Archive — Almanac chapter on Resource Hints — https://almanac.httparchive.org/en/2022/performance