در این مطلب میخواهیم با هم ببینیم از لحظهای که وارد یک سایت میشویم چه اتفاقاتی در مرورگر ما رخ میدهد و مراحل render شدن و همچنین Critical Rendering Path را بررسی کنیم. دانستن این مراحل برای توسعه دهندههای وب میتواند خیلی مفید باشد و درک درستی به ما بدهد تا بتوانیم منابع خود را بهتر مدیریت کنیم و کارایی(Performance) صفحات وب خود را بهبود بدیم.
بنابراین ما بر روی اینکه چه اتفاقاتی برای منابع ما(CSS, JS, HTML,...) در مرورگر رخ میدهد متمرکز میشویم. اما نکاتی که لازم است در نظر داشته باشید:
حالا بیایید با یک مثال ساده شروع کنیم.
به قطعه کد بالا نگاه کنید ما تنها یک فایل HTML داریم که در آن یک پیام و یک عکس وجود دارد و هیچ فایل CSS و JS ای وجود ندارد. حالا اگر لینک بالا رو باز کنید با استفاده از developer tools میتوانید در بخش network ببینید که ابتدا فایل HTML (که در اینجا فایل basic_dom_nostyle.html است) بارگذاری میشود و سپس فایل عکسی که ضمیمه آن شده. حالا به بخش waterfall نگاه کنید ناحیهی سبز رنگ TTFB و یا Time To First Byte است که مدت زمانی رو که مرورگر ما منتظر میماند تا اولین Byte داده بهش برسد است و بخش آبی رنگ نیز Content Download یا مدت زمانی که صرف دانلود فایل شده است رو نشان میدهد.
و آن خط آبی رنگ عمودی نیز نشان دهنده مدت زمانی است که طول کشیده تا فایل HTML بارگذاری و parse شود و درخت DOM یا همان Document Object Model ساخته شود که به آن DOMContentLoaded گفته میشود گه در نوار پایین مدت زمان(446ms) آن مشخص شده است.
اما نکته ای که باید به آن توجه کنیم این است که فایل عکسما رویداد DOMContentLoaded را block نکرده و این نشان دهنده این است که ما میتوانیم render tree رو بدون نیاز به بارگذاری چنین فایل هایی مثل تصاویر و… بسازیم اما بارگذاری عکس رویداد بارگذاری(load event) رو مسدود کرده است که این رویداد با یک خط عمودی قرمز رنگ نمایش داده میشود.
حالا بریم سراغ مثال بعدی که میخواهیم شرایطی رو بررسی کنیم که فایل های CSS و JS نیز رو هم در صفحه داریم. به مثال زیر توجه کنید.
قبل از اضافه کردن CSS:
بعد از اضافه کردن CSS و JS:
همانطور که در تصاویر بالا میبینید بعد از اضافه کردن فایلهای CSS و JS در بخش waterfall دو request دیگر اضافه شد. نکته مهمای که باید به آن توجه کنیم این است اگر دقت کنید میبینید که زمان فراخوانی رویداد domContentLoaded و رویداد load به هم نزدیکتر شده است.
چه اتفاقی افتاد؟
حالا اگر فایل جاوا اسکریپت external رو بصورت inline در فایل HTML قرار بدهیم چه اتفاقی می افتد؟ باز هم مرورگر قادر به اجرای آن ها نیست و باید ابتدا فایل CSS دانلود و parse شود تا CSSOM ساخته شود. بنابراین کدهای جاوا اسکریپت inline هم، همچنان parser blocking هستند.
اما با این وجود بیایید ببینیم که inline کردن کدهای js باعث میشود که صفحه سریعتر بارگذاری شود یا خیر؟
با فایل جاوا اسکریپت خارجی:
با کدهای جاوا اسکریپت به صورت inline:
خب همانطور که میبینید در تصویر دوم ما بخاطر inline کردن فایل js یک درخواست کمتر به سرور داریم اما اگر به مدت زمان load آن ها نگاه کنید میبینید که تقریبا برابر هستند. اما چرا؟ اول اینکه ما میدانیم در هر شرایطی مرورگر نیاز دارد که ابتدا CSSOM را بسازد و سپس فایل های جاوا اسکریپت را بارگذاری کند و همچنین به این نکته توجه کنید که در مثال اول مرورگر فایلهای CSS و JS را به طور موازی دانلود کرده و در واقع در این مثال خیلی تفاوتی احساس نمیکنیم اما راههای زیادی وجود دارد که بتوانیم سرعت load صفحه را بهتر کنیم.
اول اینکه به یاد داشته باشید که جاوا اسکریپت های inline ما نیز parser blocking هستند اما برای فایل های جاوا اسکریپت external میتوانیم با اضافه کردن پارامتر "async" به صورت زیر آن را به یک فایل تبدیل کنیم که فرایند parse رو متوقف نمیکند.
فایل جاوا اسکریپت خارجی(Parser-blocking):
فایل جاوا اسکریپت خارجی(async):
خب همانطور که میبینید خیلی بهتر شد ;) اگر دقت کنید بعد از بارگذاری فایل HTML رویداد domContentLoaded فراخوانی شده و مرورگر متوجه شده که نباید بخاطر فایل جاوا اسکریپت عملیات را متوقف کند و بنابراین بارگذاری فایل CSS و JS را به صورت موازی انجام میدهد.
و همچنین میتوانیم CSS و JS رو به صورت inline قرار بدهیم.
خب همانطور که میبینید تقریبا domContentLoaded با مثال قبل برابر است. در این روش هرچند حجم فایل HTML زیاد میشود اما دیگر نیازی به این نیست که مرورگر زمانی را صرف واکشی(fetch) فایلهای CSS و JS بکند.
با توجه به مثال هایی که تا به اینجا بررسی کردیم حتما متوجه شده اید که درک درست این فرآیند ها چقدر مهم خواهند بود اما حالا بریم سراغ الگوهای عملکرد تا درک بهتری در خصوص critical rendering path و روشهای بهینه سازی آن داشته باشیم.
برمیگردیم به مثال اول که داشتیم. ساده ترین حالت این است که صفحه ما یک فایل HTML دارد و هیچ فایل CSS و JS و منابع دیگه ای ندارد. برای render شدن همچین صفحه ای ابتدا مرورگر درخواستی را برای دریافت فایل HTML ارسال میکند و سپس منتظر میماند تا فایل برسد بعد از رسیدن آن را parse میکند و DOM را میسازد و در آخر آن را بر روی صفحه render میکند.
در تصویر بالا در مدت زمان بین T0 تا T1 پردازش های شبکه و سرور انجام میشود. در بهترین حالت(اگر حجم فایل HTML کم باشد) ما فقط یک roundtrip در شبکه برای واکشی فایل خواهیم داشت. با توجه به نحوه کارکرد پروتکل انتقال TCP فایلهای بزرگتر ممکن است بیش از یک roundtrip داشته باشند. بنابراین در مثال بالا ما در بهترین حالت یک roundtrip در critical rendering path خواهیم داشت.
حالا بیایید همین صفحه رو با وجود یک فایل CSS نیز بررسی کنیم:
در این حالت همانطور که در تصویر میبینید ما ابتدا یک roundtrip در شبکه برای دریافت فایل HTML داریم و در هنگام ساخت DOM مرورگر متوجه میشود که به یک فایل CSS نیز احتیاج دارد بنابراین یک بار دیگر باید به سرور مراجعه کند و فایل CSS را نیز بگیرد(این خود یک roundtrip دیگر است) تا بتواند صفحه را نمایش دهد. بنابراین در اینجا ما حداقل دو roundtrip داریم که البته باز هم باید یادآوری کرد که به حجم فایل بستگی دارد و ما در اینجا فقط به حداقل آن اشاره میکنیم.
پس باید دقت کنید که فایلهای HTML و CSS ما critical resources هستند یعنی فایلهایی که ممکن است فرآیند render شدن صفحه را متوقف کنند تا بارگذاری شوند.
حالا اگر یک فایل JS هم در صفحه داشته باشیم این فرآیند به چه شکل خواهد بود؟
در این مثال همانطور که میبینید ما یک فایل app.js نیز اضافه کردیم که همانطور که گفتیم فایل های جاوا اسکریپت parser blocking هستند و برای اجرا(execute) شدن میبایست منتظر فایل CSS و ساخت CSSOM باشیم. اما نکته دیگری که باید به آن توجه کنید این است که ما در این مثال ۳ فایل داریم اما تنها ۲ roundtrip داریم که در تصویر مشخص است فایل های CSS و JS به صورت موازی واکشی میشوند.
در نظر داشته باشید که اگر نیازی به بلاک کردن صفحه برای فایل های JS خود ندارید همانطور که قبل از این توضیح داده شد میتوانید از پارامتر "async" برای فایل جاوا اسکریپت external خود استفاده کنید.
مزیتهای بارگذاری فایل JS به صورت غیر همزمان(asynchronous):
در نتیجه صفحهی ما در بهینه ترین حالت خواهد بود. و نکتهای که شاید نیاز باشد ذکر کنیم این است که ممکن است فایل CSS ما فقط برای یک سایز خاص و یا حالت خاص مثل حالت print نیاز باشد که میتوانیم آن را به صورت زیر با افزودن media="print" از حالتی که فرایند render را متوقف میکند خارج کنیم.
در نتیجه همانطور که در تصویر میبینید بعد از ساخته شدن DOM صفحه render میشود و سپس CSSOM ساخته میشود و فایل های JS اجرا میشوند و ما فقط یک critical resources خواهیم داشت.
این مطلب برداشت من(به صورت خلاصه و با ایجاد تغییراتی) از Analyzing Critical Rendering Path Performance بوده و امیدوارم که توانسته باشم مطلب رو به درستی انتقال بدهم و درک خوبی از مفهوم critical rendering path پیدا کرده باشید و بتوانید منابع خود رو به درستی مدیریت کنید و بهینه ترین صفحات وب رو داشته باشید. و همچنین ممنون میشم اگر نقصی در این مطلب میبینید اون رو برای من کامنت کنید تا در کامل کردن این مطلب به من کمک کرده باشید.