کاربرانی که وسواس افزایش سرعت سایت خود را بنا به معیارهای تست سرعت گوگل و GTMetrix دارند، عموما با خطای مرسوم Render Blocking و تقاضای حذف آن را روبرو میشوند.
خود این خطا به زبان ساده دارد میگوید که در صفحه شما یک سری فایل -اعم از فونت، جاوااسکریپت یا CSS و غیره- وجود دارند که ابتدا باید دانلود و اجرا شوند تا بعد مابقی صفحه اجازه لود پیدا کند. بنابراین کاربر در این بازه زمانیِ دریافت و اجرای فایلها امکان مشاهده یا استفاده از صفحه شما را ندارد و به اصطلاح «بلاک» میشود.
برای حل این مساله البته افزونههای بهینهسازی مثل W3 Total Cache، Autoptimize و غیره به کار میآیند. ولی این افزونهها نیازمند تنظیمات بهینه هستند و تنظیمات بهینه هم نیازمند دانستن صورت مساله و منطق پشت آن.
در غیر این صورت حکایت کاربرانی میشود که تصور میکنند صرف نصب و راهاندازی این افزونهها قرار است معجزه کند. ولی وقتی نتیجه دلخواه را نمیگیرند و آب یخ رویشان ریخته میشود، یقه طراح افزونه را میگیرند.
یک دسته از فایلهایی که باعث بروز این خطا میشوند، جاوااسکریپتها هستند.
برای درک مساله، یک صفحه HTML را با ساختار کلی زیر در نظر بگیرید:
مرورگر برای نمایش محتوای این صفحه به کاربر، باید کدها را از بالا به پایین بخواند تا در نهایت یک ساختار درختی یا داربستمانند (به اصطلاح DOM) شکل بگیرد. اگر به تصویر بالا نگاه کنید میبینید که در طول فرایند ساخت این DOM، مرورگر به یک اسکریپت خارجی برخورد میکند که در قسمت پایین تگ Head قرار گرفته. پس الان ادامه فرایند ساخت DOM برای لحظاتی متوقف میشود تا مرورگر بتواند اول این اسکریپت را پیدا و اجرا کند (یعنی دو مرحله مجزا) و بعد به سراغ ادامه ساخت DOM برود (حالا در این مثال به جای اسکریپت خارجی، میتواند یک عکس، فیلم یا فایل استایل و خلاصه هر چیز دیگری قرار بگیرد، ولی در اینجا بحث ما فعلا روی همان اسکریپت متمرکز است).
به این ترتیب تایملاین یا نقشه زمانی لود این صفحه چنین حالتی پیدا میکند:
این حالت بدترین سناریوی ممکن است؛ چون تا زمانی که مروگر عملیات ساخت DOM را تمام نکرده باشد، کاربر نمیتواند محتوای صفحه وب را ببیند.
به همین خاطر توسعهدهندگان عموما اسکریپتهای خارجی را به قسمت پایین صفحه (قبل از تگ </body>) منتقل میکنند تا به این ترتیب مرورگر فرصت داشته باشد که بخش بیشتری از محتوای صفحه را به کاربر نشان بدهد. در این حالت تایملاین بارگذاری صفحه چنین حالتی پیدا میکند:
تا اینجا با این کار گرچه لود صفحه سریعتر میشود و کاربر میتواند بخش بیشتری از صفحه را ببینید، ولی به محض رسیدن مرورگر به اسکریپت همچنان باید منتظر دریافت و اجرای آن و تکرار سناریوی قبلی بشود (به این حالت لود متقارن گفته میشود).
در اینجاست که ویژگی Async (نامتقارن) به کمک شما میآید. در مثال قبل مرورگر با رسیدن به اسکریپت خارجی متوقف میشد تا بتواند آن را دریافت و اجرا کند.
اما در حالت جدید با اضافه کردن ویژگی Async، مرورگر میتواند همزمان با لودِ باقی محتوای صفحه و به شکل موازی، اسکریپت را دریافت کند؛ ولی همچنان برای اجرای اسکریپت باید لحظاتی متوقف شود (به همین خاطر این حالت لود نامتقارن خوانده میشود).
در این مثال با استفاده از ویژگی Async میتوانیم نسبت به سناریوی قبلی کمی در مدت زمان دریافت اسکریپت صرفهجویی کنیم. منتها با توجه به اینکه در ساختار HTML یک صفحه تگهای script زیادی میتوانند وجود داشته باشند، بنابراین استفاده از ویژگی Async باعث میشود که روی هم رفته بشود زمان قابلتوجهی را صرفهجویی کرد.
ولی هنوز هم این سناریو ایدهآل نیست. چون اولا تا اینجا فقط توانستهایم مدت زمان دریافت اسکریپت را کمتر کنیم (در صورتی که مساله مهمتر بر سر مدت زمان اجرای آن است) و ثانیا ممکن است که کد جاوا اسکریپت شما برای اجرا نیازمند یکی از اشیای موجود در ساختار DOM باشد:
مثلا اگر شما از روش Document.getElementById() برای فراخواندن یک دکمه و انجام یک عمل خاص استفاده کرده باشید، تا زمانی که این دکمه لود نشده باشد اسکریپت شما عمل نخواهد کرد. در این حالت اجرای اسکریپت، منوط به لود یکی از اشیای DOM است (این اتفاقا جواب سوالی است که قبلا در یک مطلب مطرح شده بود).
پس در اینجا یک ویژگی دیگر به نام Defer وارد صحنه میشود که به همان سادگی Async قابل استفاده است.
ویژگی Defer همانطور که از نامش پیداست، اجرای اسکریپت را موکول به زمانی میکند که ساختار DOM تشکیل شده و محتوای صفحه لود شده باشد.
در حالت Defer همانطور که در تصویر ملاحظه میکنید، اسکریپت خارجی همزمان با لود صفحه دریافت میشود، ولی اجرای آن به انتهای این فرایند سپرده میشود. بنابراین مشکل قبلیای که با Async داشتیم، با ویژگی Defer رفع میشود.
حالا به این سوال متداول میرسیم که بالاخره Async بهتر است یا Defer؟
به طور کلی ویژگی Defer در اکثر موارد سریعترین راه برای لود محتوای صفحه در مرورگر است. ولی با توضیحاتی که داده شد، باید روشن شده باشد که اساسا طرح مساله به این شکل چندان درست نیست. در اینجا با دو ویژگی متفاوت و در نوع خود کاربردی طرف هستیم که اگر در جای درست استفاده شوند، نتیجه مطلوب میدهند و در غیر اینصورت نتیجه معکوس. بنابراین مساله نه الزاما بر سر انتخاب بین این یا آن ویژگی، بلکه یافتن ترکیب بهینه از هر دو است. مثلا در یک صفحه واحد میتوان ضمن اعمال ویژگی Async روی همه اسکریپتها، استثنائا فقط به تعدادی از آنها ویژگی Defer داد یا برعکس.
افزونههای مختلف بهینهسازی به شما کمک میکنند که خیلی ساده و به روش آزمون و خطا پی ببرید که ویژگی Async بیشتر مناسب سایت شماست یا ویژگی Defer (چون اعمال یک ویژگی روی اسکریپتهای خارجی میتواند باعث بهبود سرعت، ولی در عین حال از کار افتادن بخشی از عملکردهای سایت شود). پس این همیشه و در همه موارد کافی نیست. قدم بعدی اینست که دادههای سایتهایی مثل GTmetrix را آنالیز کنید؛ تشخیص بدهید که کدام فایلهای ضروری را علیرغم Render Blocking نگه دارید و کدامیک را حذف کنید تا تعدادشان به حداقل برسد؛ کدام فایل جاوااسکریپت را از یک ویژگی خاص (Async یا Defer) مستثنا کنید و... در چنین مواردی حتی شاید بطلبد که قدری کدنویسی سایت خود را هم اصلاح کنید.
وندا نوژن