كيف يبني المتصفح الصفحة
يقرأ المتصفح HTML من أول بايت ويبني شجرة الصفحة أثناء ذلك، لكنه لا يرسم شيئا على الشاشة حتى يفهم CSS، وإن بلغ سكربتا عاديا توقف عنده. وكل نصيحة عن السرعة ستسمعها في بقية هذا المسار تخرج من هاتين الجملتين.
- الدرس 2 من 10
- مبتدئ
- مجاني، دون تسجيل
من وصول HTML إلى أول ما تراه
الخطوات الخمس التي يخطوها المتصفح لكل صفحة. الثلاث الأولى يمكن إبطاؤها، والأخيرتان نتيجة.
-
1
الطلب
ما لم يصل أول بايت من HTML فليس لدى المتصفح ما يبدأ به.
-
2
التحليل وبناء DOM
يمضي تدريجيا ويتوقف عند سكربت عادي.
-
3
قراءة CSS
تُبنى الخريطة الثانية. ولا يُرسم شيء حتى تجهز.
-
4
شجرة العرض والتخطيط
تندمج الخريطتان ويُحسب موضع كل شيء على الشاشة.
-
5
الرسم
أول ما يراه القارئ. وكل تأخير في الخطوات الثلاث قبله يظهر هنا.
هذا الترتيب مبسط. فالمتصفحات الحقيقية تنفذ أجزاء من هذه الخطوات بالتوازي وتخمينا، وهذا ما يجعل أثر أي تغيير ليس دائما بحجم ما توحي به هذه الصورة.
آخر مراجعة: تُراجع الحقائق وأسماء الأدوات في هذا الدرس مقابل مصادرها في هذا التاريخ.
ماذا يحدث من لحظة وصول HTML
أول ما ينبغي إخراجه من ذهنك أن المتصفح ينتظر وصول الملف كله. إنه لا ينتظر. بل يبدأ القراءة من أول بايت ويبني بنية الصفحة وبقيتها ما زالت في الطريق.
وما يفعله له اسم: التحليل. فالمتصفح يحول البايتات إلى حروف، والحروف إلى علامات مثل هنا فُتحت فقرة، وتلك العلامات إلى عقد. وتتصل العقد فتصنع شجرة تسمى DOM، وهي الخريطة التي يحملها المتصفح في الذاكرة لبنية صفحتك. وكل ما تغيره جافاسكربت لاحقا يتغير على تلك الشجرة لا على ملفك.
وأهم خاصية في هذا العمل أنه تدريجي. فالمتصفح لا يحتاج إلى انتظار الوسم الختامي؛ وإن كان شيء جاهزا للعرض عرضه. ولهذا ترى أحيانا صفحة وصل أعلاها وأسفلها ما زال قادما. والموقع البطيء ليس بالضرورة موقعا يصل كل شيء فيه متأخرا؛ بل هو في الغالب موقع اعترض شيء ما تلك البداية التدريجية.
وهذا بالضبط ما يجعل موضوع السرعة كله مفهوما. فالسؤال الصحيح ليس كم تزن صفحتي، بل ما الذي منع المتصفح من البدء أبكر. والقسمان التاليان هما الجوابان الرئيسان لذلك السؤال.

لماذا يحجز CSS العرض ولا تحجزه الصورة
امتلاك شجرة الصفحة لا يكفي للرسم. فالمتصفح يعرف أن هنا عنوانا، لكنه لا يعرف حجمه ولا لونه ولا هل يُفترض أن يُرى أصلا. ويعرف ذلك من CSS فيبني منه خريطة ثانية تسمى CSSOM. وحين تجهز الخريطتان معا يستطيع المتصفح بناء شجرة العرض والرسم.
فلماذا ينتظر؟ لأن البديل أسوأ. فلو رسم النص قبل جهوز CSS لرأيت لحظة من نص بلا هيئة أسود على أبيض ثم قفز كل شيء إلى مكانه. وتعد المتصفحات ذلك تجربة أسوأ من انتظار قليل، ولهذا يحجز CSS العرض افتراضيا. وهذا ليس عيبا بل قرار.
والآن الفارق الذي يوقع كثيرين: الصورة لا تفعل هذا. فإن لم تصل صورة بعد، لا يتغير مظهر النص، فلا سبب لدى المتصفح لانتظارها؛ بل يرسم الصفحة وتستقر الصورة لاحقا. لكنك إن لم تحجز لتلك الصورة مكانا، دفعت بقية المحتوى حين تصل، فتحصل على القفزة التي تزعج القارئ أكثر من كل شيء.
ومثال عملي يمكنك التحقق منه الآن: الصفحة الأولى لهذا الموقع لا تحمل وسم link rel=stylesheet أصلا. فكل CSS القالب يُرسل داخل رد HTML نفسه، أي لا حاجة إلى ذهاب وإياب على الشبكة لأول رسم. وهذا ليس الطريق الصحيح الوحيد وله كلفته، لكنه يبين أن تحسين ملف CSS ليس الخيار الوحيد على الطاولة.
أين تتوقف جافاسكربت في هذا المسار
حين يبلغ المحلل وسم script عاديا، يتوقف عن قراءة HTML، ويجلب الملف، وينفذه، ثم يواصل. وما دام هذا يحدث لا تُضاف أي عقدة جديدة إلى الشجرة، أي أن بقية الصفحة، مهما كانت جاهزة، تنتظر.
والسبب منطقي. فالسكربت مسموح له أن يكتب في الصفحة تلك اللحظة أو يغير الشجرة، والمتصفح لا يعرف سلفا هل يفعل هذا أم لا. فيفعل أحوط شيء وينتظر. وثمة نقطة تُقال أقل: السكربت الجالس في head قد ينتظر CSS أيضا إلى جانب حجزه للمحلل، لأنه يستطيع أن يسأل كم حجم عنصر ما الآن، والمتصفح لا يجيب حتى يجهز CSSOM.
وكلمتان تغيران هذا الوضع. defer تقول للمتصفح اجلب الملف الآن وبالتوازي مع قراءة HTML، لكن أجّل تنفيذه إلى ما بعد انتهاء التحليل، واحفظ ترتيب السكربتات. وasync تقول اجلبه ونفذه حيثما وصل، بلا ضمان في الترتيب. ولأي سكربت يخص صفحتك تقريبا، defer هو الجواب الصحيح؛ أما async فمعقول لأشياء مستقلة كأداة تحليلات لا شأن لها ببقية شفرتك.
وهنا يضللك وزن الملف. فصورة بميغابايتين في أسفل الصفحة لا تحجز شيئا. وسكربت بأربعة كيلوبايت جالس في head بلا defer، يأتي من طرف ثالث لا تتحكم فيه، يؤخر أول رسم للصفحة كلها بمقدار ذهاب وإياب كامل إلى ذلك الخادم. الحجم مهم، لكن الموضع أهم.
سكربت واحد، موضعان مختلفان في المسار
الملف نفسه والحجم نفسه. والفرق الوحيد كلمة واحدة، وتلك الكلمة تحدد متى يرى القارئ شيئا.
سكربت عادي في head
- يتوقف المحلل هناك وتتوقف شجرة الصفحة عن النمو
- إن جاء الملف من خادم آخر، أُنفق ذهاب وإياب كامل في الانتظار
- قد ينتظر أيضا جهوز CSS لا أن يحجز المحلل وحده
- يرى القارئ صفحة بيضاء حتى ينتهي ذلك كله
السكربت نفسه مع defer
- يبدأ التنزيل فورا وبالتوازي مع قراءة HTML
- لا يتوقف المحلل وتكتمل شجرة الصفحة
- يجري التنفيذ بعد التحليل ويُحفظ ترتيب السكربتات
- يرى القارئ الصفحة وذلك الملف ما زال في الطريق
ثمة استثناء حقيقي واحد: السكربت الذي يجب أن يعمل قبل أول رسم، كتحديد السمة الفاتحة أو الداكنة، يُترك حاجزا عن قصد كي لا تومض الصفحة بيضاء ثم تصير داكنة.
إذن «CSS أولا وجافاسكربت بعدها» ليست خرافة
هذه نصيحة قديمة، وفي أكثر المواضع التي تُكرر فيها يُذكر سببها خطأ. فالسبب الحقيقي ليس أن جافاسكربت ثقيلة. بل أن العرض ينتظر CSS، فوجب أن يصل CSS باكرا؛ وأن المحلل ينتظر السكربت، فوجب ألا يقف السكربت في منتصف الطريق. إنها جملة واحدة عن ترتيب انتظارين، لا عن وزن الملفات.
وعلى الصفحة الأولى لهذا الموقع يمكن عدّ هذا الترتيب. في head صفر سكربت. وعلى الصفحة سبعة وسوم سكربت، وسبعتها تجلس في الثلاثة بالمئة الأخيرة من المستند، أي بعد كل المحتوى الذي يُفترض أن يراه القارئ. وCSS ليس ملفا منفصلا أصلا بل يأتي داخل الرد نفسه. والنتيجة أن المتصفح لا ينتظر لأول رسم شيئا من الشبكة.
وأصدق جزء في هذا الدرس: الترتيب أعلاه هو الشكل القديم للحل. فوضع السكربتات في آخر الجسم يعمل، لأن المحلل حين يبلغها يكون قد بنى كل شيء. لكن الشكل الأفضل اليوم هو عادة وضع السكربت نفسه في head مع defer، فيبدأ التنزيل أبكر ويبقى التنفيذ في الآخر. ولم نجرِ هذا التغيير على هذا الموقع بعد؛ فالمكسب صغير، وعلى صفحة سكربتاتها متأخرة إلى هذا الحد لم يستحق مخاطرة التغيير.
وحد يستحق المعرفة: بضعة سكربتات يجب فعلا أن تعمل قبل أول رسم، كالمقطع الذي يحدد السمة الفاتحة أو الداكنة كي لا تومض الصفحة بيضاء ثم تصير داكنة. ذلك يُبقى حاجزا عن قصد، وهو القرار الصحيح. فقاعدة أخّر كل شيء لها استثناء واحد وهو هذا.
ما الذي يحجز أول رسم، وما هو ثقيل فحسب
هذه لا تحجز العرض
- الصور، مهما كبرت
- السكربتات الموسومة بـ defer أو async
- CSS المرسل داخل رد HTML نفسه
هذه تحجز العرض
- كل ملف CSS موصول في head
- كل سكربت في head بلا defer ولا async
- سكربت يأتي من نطاق طرف ثالث ويجلس في head
العمود الأيمن يعني أن هذه لا تحجز أول رسم، لا أنها بلا كلفة. فصورة كبيرة بلا مكان محجوز تزيح المحتوى حين تصل، وتلك مشكلة أخرى.
كيف ترى هذا على موقعك أنت
لا تحتاج إلى أي أداة لهذا. افتح مصدر الصفحة وانظر داخل head وحده. وعُدّ شيئين: كم وسم link rel=stylesheet هناك، وكم وسم script بـ src ليس فيه defer ولا async. ومجموع هذين الرقمين هو قائمة ما يحجز أول رسم لصفحتك. وإن لم يكن الرقم الثاني صفرا، فذلك عادة أول الأعمال وأرخصها.
وإن فضّلت فعل ذلك من سطر الأوامر، فهذا الأمر يطبع تلك الوسوم لترى بنفسك أيها يحمل defer أو async:
curl -s https://example.com/ | tr '\n' ' ' | sed 's/<\/head>.*//' | grep -oE '<link[^>]*stylesheet[^>]*>|<script[^>]*src=[^>]*>'والنسخة الأبسط من هذا الأمر، تلك التي تقتطع مجال head سطرا سطرا بـ sed، تعطي جوابا خاطئا على الصفحات المضغوطة، وقد أنفقنا نحن وقتا عليها. فـ HTML المضغوط سطر واحد طويل عادة، فيبقى ذلك المجال مفتوحا إلى آخر المستند ويحسب سكربتات ذيل الجسم أيضا. والأمر أعلاه يطرح أولا كل ما بعد </head>، ولهذا يعمل في الحالتين.
وتحذير في قراءة النتيجة: للمتصفحات الحديثة ماسح منفصل ينظر إلى الأمام بينما المحلل الرئيس متوقف على سكربت، فيبدأ جلب الملفات التالية أبكر. أي أن المحلل يتوقف لا تعني أن الشبكة تجلس عاطلة. وهذا أحد أسباب أن حذف سكربت حاجز يكون أثره أحيانا أقل من المتوقع، وسبب أن القياس قبل وبعد لا يُستعاض عنه بالاستدلال.
وتلك هي الخطوة التالية: حتى الآن تعرف ما الذي يمكن أن يحجز العرض، لكنك لا تعرف أيها فعل ذلك على موقعك ولا بكم. وذلك السؤال يُجاب بالقياس، ودرس قياس سرعة الموقع في هذا المسار يتولاه. وإن أردت رقما حقيقيا من صفحتك الآن، فأداة الاختبار على صفحة تسريع الموقع تعمل بمحرك Lighthouse.
المسار السريع مع الذكاء الاصطناعي
حين تكون الصفحة بطيئة، فالحركة المعتادة أن تفتح المخطط الشلالي في أدوات المطور وتبحث عن أطول عمود. والمشكلة أن أطول عمود ليس الجاني عادة؛ فالجاني هو ما انتظره الباقون، وهو غالبا عمود قصير قرب البداية. والنموذج اللغوي جيد في هذا النوع من القراءة بالضبط: يرى عشرات صفوف التوقيت معا ويجد نمط هذا انتظر ذاك أسرع من عينك. والعمل كله عشر دقائق، وشرطه الأول ألا تضع الملف الخام أمام النموذج.
- في المتصفح، افتح أدوات المطور، وانتقل إلى تبويب الشبكة، وعطّل الكاش، وحمّل الصفحة مرة كاملة. واحفظ المخرج ملف HAR.
- لا تلصق ملف HAR كما هو في أي مكان. فذلك الملف يحمل كل رؤوس الطلبات، أي أن كوكيز دخولك ورموز جلستك داخله أيضا. واستخرج خمسة أعمدة فقط: العنوان، ونوع الملف، ولحظة البدء، والمدة، وهل كان في head. وأول خمسين صفا تكفي.
- سلّم الجدول مع الوصفة أدناه واطلب صراحة التفريق بين شيئين: ملف استغرق هو نفسه وقتا، وملف بدأ متأخرا لأنه كان ينتظر غيره. وكل القيمة في هذا الفصل. ويلزم لهذا نموذج قوي لأنه حكم؛ والاختيار الحالي في كل فئة محفوظ في مرجع الذكاء الاصطناعي لدينا.
- لا تصدق الجواب بل اختبره. غيّر شيئا واحدا فقط، هو الذي سماه النموذج، ثم قِس ثانية. فإن لم يتحرك الرقم فالفرضية كانت خاطئة، وهذه نتيجة أيضا. وتغييران معا يعني ألا تعرف أبدا أيهما نفع.
وصفة جاهزة للنسخ
دورك أخصائي أداء ويب. احكم من الجدول أدناه وحده ولا تضف عن هذا الموقع شيئا من ذاكرتك.
أُخذ الجدول أدناه من تحميل كامل واحد لصفحة {عنوان الصفحة}. لكل مورد: العنوان، والنوع، ولحظة البدء بالمللي ثانية من بداية التحميل، والمدة بالمللي ثانية، وهل كان في head.
{جدول الموارد}
اكتب خمسة أشياء:
١. افصل الموارد التي كان يمكنها حجز أول رسم: CSS فقط، والسكربتات بلا defer أو async التي كانت في head. ونحِّ الباقي وقل لماذا نحّيته.
٢. لكل منها قل أي حالة هي: استغرق هو نفسه وقتا، أم بدأ متأخرا لأنه كان ينتظر موردا آخر. وفي الحالة الثانية سمِّ ذلك المورد الآخر.
٣. سمِّ موردا واحدا هو الأرجح أنه أضاف أكبر تأخير إلى أول رسم، وقل في جملة واحدة أي أعمدة الجدول أوصلتك إلى ذلك.
٤. اقترح تغييرا محددا واحدا يمكن إجراؤه وحده ثم قياسه.
٥. اكتب ما ليس في هذا الجدول ولو كان فيه لأمكن أن يغير جوابك.
قواعد: لا تخترع رقما ليس في الجدول ولا تفترض موردا غير موجود فيه. وحجم الملف وحده ليس سببا للحجز؛ فإن أردت الاستناد إلى الحجم فقل لماذا يهم في هذه الحالة. وإن لم يكفِ الجدول للاستنتاج فاكتب ذلك وقل أي عمود تحتاج بدله. وجواب «لم يكن في head أي مورد حاجز» جواب مقبول.
قبل أن تثق بالناتج: ثمة حد أصيل لا يزيله أي نموذج: المخطط الشلالي يقول ماذا حدث ومتى، لا لماذا. والترتيب الزمني يشبه السببية وليس إياها، والنموذج يرى ذلك الترتيب نفسه. فمخرج هذا العمل فرضية لا تشخيص؛ والشيء الوحيد الذي يحوله إلى تشخيص هو تغيير ذلك البند وحده والقياس ثانية. ونقطة وردت في هذا الدرس أيضا: المتصفحات تبدأ جلب الملفات تخمينا قبل المحلل، فحذف سكربت حاجز يكون أثره أحيانا أقل مما يوحي المخطط. فإن لم يتحرك الرقم بعد التغيير فليس ذلك فشلك؛ بل هو القياس يؤدي عمله.
الذكاء الاصطناعي في هذا العمل
في هذا الموضوع للنموذج اللغوي سلوكان مختلفان تماما، والفرق بينهما هو ما يحدد هل يضيع وقتك أم لا. فإن وضعت أمامه جدول التوقيت الحقيقي لصفحتك كان ممتازا: يرى عشرات الصفوف معا ويجد علاقة الانتظار بينها أسرع منك. وإن لم تضع أمامه شيئا واكتفيت بسؤال كيف أسرّع موقعي، حصلت على القائمة العامة المعتادة، المكتوبة لأي موقع والدقيقة لأي موقع منها.
أدوات تساعد فعلا
- Claude مناسب لعمل المسار السريع نفسه: يأخذ الجدول، وبدل أن يعدد كل شيء يسمي موردا واحدا ويقول من أي عمود وصل إليه. وإيران ليست في قائمة أنثروبيك للدول المدعومة، فلا طريق رسمي للتسجيل أو الدفع.
- Gemini إن كان جدولك كبيرا وفضّلت ألا تقلل الصفوف، فهو يتعامل جيدا مع المدخلات الطويلة. وإيران ليست ضمن المناطق التي تتوفر فيها هذه الخدمة.
- OpenAI الخيار الأشيع، ويكفي لقراءة جدول من خمسين صفا. ولا ندعي شيئا عن الوصول أو الدفع من إيران، والرابط إلى صفحة الصانع لا إلى صفحة منتج: فلا تُفتح أي صفحة استهلاكية لأوبن إيه آي من هذا الخادم، ولا نكتب عما لم نستطع رؤيته بأنفسنا.
اين ينقلب ضدك
خطران، وأولهما خاص بهذا الموضوع. فالنموذج يميل بشدة إلى تسمية أكبر ملف جانيا، لأن الثقل والبطء في النص الذي دُرّب عليه شبه مترادفين. لكن كما رأيت في هذا الدرس، الحجز يتعلق بالموضع ونوع المورد لا بالحجم: فصورة كبيرة في أسفل الصفحة لا تحجز شيئا، وسكربت صغير في head يحجز كل شيء. فإن سمّى الجواب أكبر صف في الجدول فحسب، فقد أجاب على الأرجح من النمط لا من بياناتك. والخطر الثاني أمني وأصعب تداركا: ملف HAR يحمل كل رؤوس الطلبات، أي كوكيز دخولك ورموز جلستك. ولصق ذلك الملف خاما في أي أداة على الإنترنت هو عمليا إرسال مفتاح حسابك إلى خدمة خارجية، وإرشادات أنثروبيك الأمنية نفسها تقول إن كل ما يدخل نافذة السياق ينبغي التعامل معه كمدخل غير موثوق. وموقفنا: قلّص الجدول يدويا إلى خمسة أعمدة، وقبل تصديق أي مورد يسميه النموذج غيّره مرة بنفسك وقِس.
المصادر: MDN: critical rendering path MDN: the script element, defer and async Anthropic: mitigate jailbreaks and prompt injections Anthropic: supported countries Google: where Gemini Apps are available
حدود هذه النصيحة
هذه الصورة ذات الخطوات الخمس هي المسار الكلاسيكي وتكفي للفهم، لكنها تتخلف عن الواقع في موضعين. الأول أن المتصفحات الحديثة لا تنفذ هذه الخطوات واحدة تلو أخرى بدقة: فماسح منفصل يسبق المحلل ويبدأ جلب الملفات اللاحقة تخمينا، وأجزاء من التخطيط والرسم تجري بالتوازي، ولهذا لا يكون أثر أي تغيير دائما بالحجم الذي يوحي به هذا المخطط. والثاني وهو الأهم: إن كان موقعك تطبيق صفحة واحدة وجافاسكربت هي التي تبني المحتوى في المتصفح، فهذه الصورة تشرح بداية القصة فقط. فهناك قد يقع أول رسم سريعا والصفحة ما زالت فارغة، ويذهب الوقت الحقيقي إلى موضع لا يتناوله هذا الدرس.
من عملنا نحن
أرقام هذا الدرس مأخوذة من الصفحة الأولى لهذا الموقع في ٧ سبتمبر ٢٠٢٦، وكل منها يمكن التحقق منه بعرض مصدر الصفحة: صفر وسم link rel=stylesheet، وصفر سكربت في head، وسبعة وسوم سكربت تجلس سبعتها في الثلاثة بالمئة الأخيرة من المستند. وكل CSS القالب نحو ٩١ كيلوبايت ويُرسل داخل رد HTML نفسه. والآن ما تعلمناه فعلا أثناء كتابة هذا الدرس، وهو أثمن من تلك الأرقام: النسخة الأولى من الأمر الوارد في القسم الخامس كانت تقتطع مجال head سطرا سطرا بـ sed، وأعطت جوابا خاطئا على هذه الصفحة بعينها. فـ HTML هذا الموقع مضغوط وهو سطر واحد طويل تقريبا، فلم يُغلق ذلك المجال قط وامتد إلى آخر المستند؛ والنتيجة أنه عدّ تلك السكربتات السبع في ذيل الجسم سكربتات داخل head. ولو نشرنا ذلك الأمر دون اختباره، لأعطى جوابا خاطئا لكل من في موقعه كاش، ولكان بالضبط الخطأ الذي يحذر منه هذا الدرس: استدلال صحيح على بيانات لم تتحقق منها بنفسك.
اسئلة متابعة حقيقية
لماذا تظهر صفحتي لحظة بلا تنسيق؟
لأن جزءا من CSS لديك يُحمّل بطريقة لا تحجز العرض، بجافاسكربت مثلا أو بحيل تجعل ملف التنسيق غير حاجز. فلا ينتظر المتصفح، بل يرسم النص، ثم يصل التنسيق فيزيح كل شيء. والحل أن تصل التنسيقات اللازمة للشاشة الأولى بالطريقة العادية الحاجزة، ويأتي الباقي بعدها.
إن وضعت defer على كل السكربتات، هل يتعطل شيء؟
قد يتعطل، وأشيع الحالات مقطع مضمَّن في الصفحة يعتمد على مكتبة صارت تعمل الآن متأخرة. فالشفرة المضمَّنة لا تتبع قاعدة defer وتعمل في موضعها، فيختل الترتيب وتحصل على خطأ شيء ما غير معرّف. تقدّم واحدا واحدا، وبعد كل تغيير افتح الصفحة فعلا وانظر إلى وحدة الأخطاء.
أفيجدر بي إذن تضمين CSS داخل الصفحة كما تفعلون؟
ليس بالضرورة، وهذه مقايضة لا تحسين خالص. فالتضمين يحذف ذهابا وإيابا واحدا على الشبكة، لكن البايتات نفسها تُرسل ثانية في كل رد ولا تُخزَّن بعد ذلك بين الصفحات. وهو يستحق عندنا لأن HTML هذا الموقع مخزَّن على الحافة ولأن الزائر لا يفتح عادة عدة صفحات متتالية؛ أما إن كان موقعك ديناميكيا ويمر المستخدم بعشر صفحات تباعا، فالملف المنفصل المخزَّن خيار أفضل على الأرجح.