با اسکنر پیش بارگذاری مرورگر مبارزه نکنید

دریابید که اسکنر پیش‌بارگذاری مرورگر چیست، چگونه به عملکرد آن کمک می‌کند و چگونه می‌توانید از دسترس آن دور بمانید.

یکی از جنبه‌های نادیده گرفته شده در بهینه‌سازی سرعت صفحه، دانستن کمی در مورد اجزای داخلی مرورگر است. مرورگرها بهینه‌سازی‌های خاصی را برای بهبود عملکرد به روش‌هایی انجام می‌دهند که ما به عنوان توسعه‌دهندگان نمی‌توانیم - اما فقط تا زمانی که این بهینه‌سازی‌ها به طور ناخواسته خنثی نشوند.

یکی از بهینه‌سازی‌های داخلی مرورگر که باید درک شود، اسکنر پیش‌بارگذاری مرورگر است. این پست نحوه کار اسکنر پیش‌بارگذاری و مهم‌تر از آن، نحوه جلوگیری از ایجاد مزاحمت برای آن را پوشش می‌دهد.

اسکنر پیش‌بارگذاری چیست؟

هر مرورگری یک تجزیه‌گر HTML اولیه دارد که نشانه‌گذاری خام را توکن‌سازی کرده و آن را به یک مدل شیء پردازش می‌کند. همه این‌ها به خوبی ادامه می‌یابد تا زمانی که تجزیه‌گر با یافتن یک منبع مسدودکننده ، مانند یک فایل stylesheet که با عنصر <link> بارگذاری شده است، یا اسکریپتی که با عنصر <script> بدون ویژگی async یا defer بارگذاری شده است، متوقف شود.

نمودار تجزیه‌گر HTML.
شکل ۱: نموداری از نحوه مسدود کردن تجزیه‌کننده اصلی HTML مرورگر. در این حالت، تجزیه‌کننده در یک عنصر <link> برای یک فایل CSS خارجی اجرا می‌شود که مرورگر را از تجزیه بقیه سند - یا حتی رندر کردن هر یک از آن - تا زمانی که CSS دانلود و تجزیه شود، باز می‌دارد.

در مورد فایل‌های CSS، رندر کردن (rendering) مسدود می‌شود تا از نمایش ناگهانی محتوای بدون استایل (FOUC) جلوگیری شود، که زمانی است که یک نسخه بدون استایل از یک صفحه را می‌توان قبل از اعمال استایل‌ها به طور خلاصه مشاهده کرد.

صفحه اصلی web.dev در حالت بدون استایل (چپ) و حالت استایل‌دار (راست).
شکل ۲: یک نمونه شبیه‌سازی‌شده از FOUC. در سمت چپ صفحه اصلی web.dev بدون استایل‌ها قرار دارد. در سمت راست همان صفحه با استایل‌های اعمال‌شده قرار دارد. اگر مرورگر هنگام دانلود و پردازش یک stylesheet، رندر شدن را مسدود نکند، حالت بدون استایل می‌تواند در یک چشم به هم زدن رخ دهد.

مرورگر همچنین هنگام مواجهه با عناصر <script> بدون ویژگی defer یا async تجزیه و رندر صفحه را مسدود می‌کند.

دلیل این امر این است که مرورگر نمی‌تواند با اطمینان بداند که آیا هر اسکریپت داده شده DOM را تغییر خواهد داد یا خیر، در حالی که تجزیه‌کننده اصلی HTML هنوز کار خود را انجام می‌دهد. به همین دلیل است که بارگذاری جاوا اسکریپت در انتهای سند یک روش رایج بوده است تا اثرات تجزیه و رندر مسدود شده به حاشیه برود.

اینها دلایل خوبی هستند که چرا مرورگر باید هم تجزیه و هم رندر را مسدود کند. با این حال، مسدود کردن هر یک از این مراحل مهم نامطلوب است، زیرا می‌توانند با به تأخیر انداختن کشف سایر منابع مهم، نمایش را مختل کنند. خوشبختانه، مرورگرها تمام تلاش خود را می‌کنند تا این مشکلات را از طریق یک تجزیه‌کننده HTML ثانویه به نام اسکنر پیش‌بارگذاری کاهش دهند.

نموداری از تجزیه‌کننده‌ی اولیه‌ی HTML (چپ) و اسکنر پیش‌بارگذاری (راست)، که تجزیه‌کننده‌ی ثانویه‌ی HTML است.
شکل ۳: نموداری که نحوه‌ی عملکرد اسکنر پیش‌بارگذاری را به موازات تجزیه‌کننده‌ی اولیه‌ی HTML برای بارگذاری احتمالی فایل‌ها نشان می‌دهد. در اینجا، تجزیه‌کننده‌ی اولیه‌ی HTML هنگام بارگذاری و پردازش CSS قبل از شروع پردازش نشانه‌گذاری تصویر در عنصر <body> مسدود شده است، اما اسکنر پیش‌بارگذاری می‌تواند در نشانه‌گذاری خام به دنبال منبع تصویر بگردد و قبل از اینکه تجزیه‌کننده‌ی اولیه‌ی HTML از حالت مسدود خارج شود، بارگذاری آن را آغاز کند.

نقش یک اسکنر پیش‌بارگذاری، حدسی است ، به این معنی که نشانه‌گذاری خام را بررسی می‌کند تا منابعی را پیدا کند که بتواند به صورت فرصت‌طلبانه قبل از اینکه تجزیه‌کننده اولیه HTML آنها را کشف کند، آنها را دریافت کند.

چگونه بفهمیم اسکنر پیش‌بارگذاری کار می‌کند

اسکنر پیش‌بارگذاری به دلیل رندر و تجزیه مسدود شده وجود دارد. اگر این دو مشکل عملکردی هرگز وجود نداشتند، اسکنر پیش‌بارگذاری خیلی مفید نمی‌بود. کلید فهمیدن اینکه آیا یک صفحه وب از اسکنر پیش‌بارگذاری سود می‌برد یا خیر، به این پدیده‌های مسدودسازی بستگی دارد. برای انجام این کار، می‌توانید یک تأخیر مصنوعی برای درخواست‌ها ایجاد کنید تا بفهمید اسکنر پیش‌بارگذاری در کجا کار می‌کند.

به عنوان مثال، این صفحه از متن و تصاویر پایه را با یک stylesheet در نظر بگیرید. از آنجا که فایل‌های CSS هم رندر و هم تجزیه را مسدود می‌کنند، شما یک تأخیر مصنوعی دو ثانیه‌ای برای stylesheet از طریق یک سرویس پروکسی ایجاد می‌کنید. این تأخیر، مشاهده آن را در آبشار شبکه، جایی که اسکنر پیش‌بارگذاری کار می‌کند، آسان‌تر می‌کند.

نمودار آبشاری شبکه WebPageTest یک تأخیر مصنوعی ۲ ثانیه‌ای را که به styleesheet اعمال شده است، نشان می‌دهد.
شکل ۴: نمودار آبشاری شبکه WebPageTest از یک صفحه وب که در مرورگر کروم روی دستگاه تلفن همراه از طریق اتصال 3G شبیه‌سازی شده اجرا می‌شود. اگرچه stylesheet به طور مصنوعی از طریق یک پروکسی دو ثانیه قبل از شروع بارگذاری به تأخیر می‌افتد، اما تصویری که بعداً در محتوای نشانه‌گذاری قرار دارد توسط اسکنر پیش‌بارگذاری کشف می‌شود.

همانطور که در آبشار مشاهده می‌کنید، اسکنر پیش‌بارگذاری، عنصر <img> را حتی در حالی که رندر و تجزیه سند مسدود شده است، کشف می‌کند. بدون این بهینه‌سازی، مرورگر نمی‌تواند در طول دوره مسدودسازی، موارد را به صورت فرصت‌طلبانه دریافت کند و درخواست‌های منابع بیشتر به صورت متوالی و نه همزمان خواهند بود.

با کنار گذاشتن این مثال کلیشه‌ای، بیایید نگاهی به برخی الگوهای دنیای واقعی بیندازیم که در آن‌ها اسکنر پیش‌بارگذاری می‌تواند دچار مشکل شود—و برای رفع آن‌ها چه کاری می‌توان انجام داد.

اسکریپت‌های async تزریق‌شده

فرض کنید در <head> خود HTML دارید که شامل مقداری جاوا اسکریپت درون‌خطی مانند این است:

<script>
  const scriptEl = document.createElement('script');
  scriptEl.src = '/yall.min.js';

  document.head.appendChild(scriptEl);
</script>

اسکریپت‌های تزریق‌شده به‌طور پیش‌فرض async هستند، بنابراین وقتی این اسکریپت تزریق می‌شود، طوری رفتار می‌کند که انگار ویژگی async روی آن اعمال شده است. این بدان معناست که در اسرع وقت اجرا می‌شود و رندر را مسدود نمی‌کند. به نظر بهینه می‌رسد، درست است؟ با این حال، اگر فرض کنید که این <script> درون‌خطی بعد از یک عنصر <link> که یک فایل CSS خارجی را بارگذاری می‌کند، می‌آید، نتیجه‌ای غیربهینه خواهید گرفت:

این نمودار WebPageTest نشان می‌دهد که اسکن پیش‌بارگذاری هنگام تزریق اسکریپت با شکست مواجه می‌شود.
شکل ۵: نمودار آبشاری شبکه WebPageTest از یک صفحه وب که در مرورگر کروم روی دستگاه تلفن همراه از طریق اتصال 3G شبیه‌سازی شده اجرا می‌شود. این صفحه شامل یک stylesheet واحد و یک اسکریپت async تزریق شده است. اسکنر پیش‌بارگذاری نمی‌تواند اسکریپت را در طول مرحله مسدود کردن رندر کشف کند، زیرا در کلاینت تزریق شده است.

بیایید آنچه را که اینجا اتفاق افتاده است، تجزیه و تحلیل کنیم:

  1. در ثانیه ۰، سند اصلی درخواست می‌شود.
  2. در ۱.۴ ثانیه، اولین بایت درخواست ناوبری می‌رسد.
  3. در ثانیه ۲.۰، فایل CSS و تصویر درخواست می‌شوند.
  4. از آنجا که تجزیه‌گر هنگام بارگذاری stylesheet مسدود شده است و جاوا اسکریپت درون‌خطی که اسکریپت async را تزریق می‌کند، پس از آن stylesheet و در ثانیه ۲.۶ می‌آید، عملکردی که اسکریپت ارائه می‌دهد، به محض اینکه ممکن باشد، در دسترس نیست.

این حالت بهینه نیست زیرا درخواست برای اسکریپت فقط پس از اتمام دانلود stylesheet رخ می‌دهد. این باعث می‌شود اسکریپت در اسرع وقت اجرا نشود. در مقابل، از آنجا که عنصر <img> در نشانه‌گذاری ارائه شده توسط سرور قابل کشف است، توسط اسکنر پیش‌بارگذاری کشف می‌شود.

بنابراین، اگر از یک تگ <script> معمولی با ویژگی async به جای تزریق اسکریپت به DOM استفاده کنید، چه اتفاقی می‌افتد؟

<script src="/yall.min.js" async></script>

این نتیجه است:

یک نمودار آبشاری شبکه WebPageTest که نشان می‌دهد چگونه یک اسکریپت ناهمگام که با استفاده از عنصر اسکریپت HTML بارگذاری شده است، هنوز توسط اسکنر پیش‌بارگذاری مرورگر قابل شناسایی است، حتی اگر تجزیه‌کننده اصلی HTML مرورگر هنگام دانلود و پردازش یک stylesheet مسدود شده باشد.
شکل ۶: نمودار آبشاری شبکه WebPageTest از یک صفحه وب که روی مرورگر کروم روی دستگاه تلفن همراه از طریق اتصال 3G شبیه‌سازی شده اجرا می‌شود. این صفحه شامل یک stylesheet و یک عنصر <script> async . اسکنر پیش‌بارگذاری، اسکریپت را در طول مرحله مسدودسازی رندر کشف می‌کند و آن را همزمان با CSS بارگذاری می‌کند.

ممکن است وسوسه‌ای وجود داشته باشد که پیشنهاد شود این مشکلات را می‌توان با استفاده از rel=preload برطرف کرد. این قطعاً کار می‌کند، اما ممکن است عوارض جانبی داشته باشد. گذشته از همه اینها، چرا از rel=preload برای رفع مشکلی استفاده کنیم که می‌توان با عدم تزریق عنصر <script> به DOM از آن جلوگیری کرد؟

یک نمودار آبشاری WebPageTest که نشان می‌دهد چگونه از اشاره‌گر منبع rel=preload برای شناسایی یک اسکریپت تزریق‌شده‌ی ناهمگام استفاده می‌شود - البته به روشی که ممکن است عوارض جانبی ناخواسته‌ای داشته باشد.
شکل ۷: نمودار آبشاری شبکه WebPageTest از یک صفحه وب که در مرورگر کروم روی دستگاه تلفن همراه از طریق اتصال 3G شبیه‌سازی شده اجرا می‌شود. این صفحه شامل یک stylesheet واحد و یک اسکریپت async تزریق شده است، اما اسکریپت async از قبل بارگذاری شده است تا از کشف زودتر آن اطمینان حاصل شود.

پیش‌بارگذاری مشکل را در اینجا «رفع» می‌کند، اما مشکل جدیدی ایجاد می‌کند: اسکریپت async در دو دموی اول - با وجود بارگذاری در <head> - با اولویت «پایین» بارگذاری می‌شود، در حالی که stylesheet با اولویت «بالاترین» بارگذاری می‌شود. در آخرین دمو که اسکریپت async از قبل بارگذاری شده است، stylesheet همچنان با اولویت «بالاترین» بارگذاری می‌شود، اما اولویت اسکریپت به «بالا» ارتقا یافته است.

وقتی اولویت یک منبع افزایش می‌یابد، مرورگر پهنای باند بیشتری را به آن اختصاص می‌دهد. این بدان معناست که - حتی اگر stylesheet بالاترین اولویت را داشته باشد - اولویت افزایش یافته اسکریپت ممکن است باعث ایجاد تداخل در پهنای باند شود. این می‌تواند عاملی در اتصالات کند یا در مواردی باشد که منابع بسیار بزرگ هستند.

پاسخ در اینجا ساده است: اگر در هنگام راه‌اندازی به یک اسکریپت نیاز است، با تزریق آن به DOM، اسکنر پیش‌بارگذاری را شکست ندهید. در صورت نیاز، جایگذاری عنصر <script> و همچنین ویژگی‌هایی مانند defer و async را آزمایش کنید.

بارگذاری تنبل با جاوا اسکریپت

بارگذاری تنبل یک روش عالی برای حفظ داده‌ها است، روشی که اغلب برای تصاویر اعمال می‌شود. با این حال، گاهی اوقات بارگذاری تنبل به اشتباه برای تصاویری که "بالای صفحه" هستند، به اصطلاح، اعمال می‌شود.

این امر مشکلات بالقوه‌ای را در رابطه با قابلیت کشف منابع، در جایی که اسکنر پیش‌بارگذاری مربوط می‌شود، ایجاد می‌کند و می‌تواند بی‌جهت مدت زمان لازم برای کشف ارجاع به یک تصویر، دانلود آن، رمزگشایی آن و ارائه آن را به تأخیر بیندازد. بیایید به عنوان مثال، این نشانه‌گذاری تصویر را در نظر بگیریم:

<img data-src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">

استفاده از پیشوند data- یک الگوی رایج در lazy loader های مبتنی بر جاوا اسکریپت است. وقتی تصویر به داخل viewport اسکرول می‌شود، lazy loader پیشوند data- را حذف می‌کند، به این معنی که در مثال قبلی، data-src به src تبدیل می‌شود. این به‌روزرسانی مرورگر را وادار به دریافت منبع می‌کند.

این الگو تا زمانی که روی تصاویری که در هنگام راه‌اندازی در پنجره نمایش هستند اعمال نشود، مشکلی ایجاد نمی‌کند. از آنجا که اسکنر پیش‌بارگذاری، ویژگی data-src را به همان روشی که ویژگی src (یا srcset ) را می‌خواند، نمی‌خواند، مرجع تصویر زودتر کشف نمی‌شود. بدتر از آن، بارگذاری تصویر تا زمانی که بارگذاری تنبل جاوا اسکریپت را دانلود، کامپایل و اجرا کند، به تأخیر می‌افتد.

نمودار آبشاری شبکه WebPageTest که نشان می‌دهد چگونه یک تصویر با بارگذاری تنبل که در هنگام راه‌اندازی در viewport قرار دارد، لزوماً به تأخیر می‌افتد زیرا اسکنر پیش‌بارگذاری مرورگر نمی‌تواند منبع تصویر را پیدا کند و فقط زمانی بارگیری می‌شود که جاوا اسکریپت مورد نیاز برای بارگذاری تنبل بارگذاری شود. تصویر خیلی دیرتر از آنچه باید کشف می‌شود.
شکل ۸: نمودار آبشاری شبکه WebPageTest از یک صفحه وب که در مرورگر کروم روی دستگاه تلفن همراه از طریق اتصال 3G شبیه‌سازی شده اجرا می‌شود. منبع تصویر، با وجود اینکه در هنگام راه‌اندازی در پنجره نمایش قابل مشاهده است، به طور غیرضروری بارگذاری تنبلی دارد. این امر اسکنر پیش‌بارگذاری را از کار می‌اندازد و باعث تأخیر غیرضروری می‌شود.

بسته به اندازه تصویر - که ممکن است به اندازه نمایشگر بستگی داشته باشد - ممکن است یک عنصر کاندید برای Largest Contentful Paint (LCP) باشد. هنگامی که اسکنر پیش‌بارگذاری نمی‌تواند به صورت حدسی منبع تصویر را از قبل دریافت کند - احتمالاً در نقطه‌ای که رندر شدن stylesheet(s) صفحه مسدود می‌شود - LCP دچار مشکل می‌شود.

راه حل تغییر نشانه گذاری تصویر است:

<img src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">

این الگوی بهینه برای تصاویری است که در هنگام راه‌اندازی در پنجره نمایش قرار دارند، زیرا اسکنر پیش‌بارگذاری، منبع تصویر را سریع‌تر کشف و دریافت می‌کند.

نمودار آبشاری شبکه WebPageTest که سناریوی بارگذاری یک تصویر را در پنجره نمایش در هنگام راه‌اندازی نشان می‌دهد. تصویر به صورت تنبل بارگذاری نشده است، به این معنی که برای بارگذاری به اسکریپت وابسته نیست، به این معنی که اسکنر پیش‌بارگذاری می‌تواند آن را زودتر کشف کند.
شکل ۹: نمودار آبشاری شبکه WebPageTest از یک صفحه وب که در مرورگر کروم روی دستگاه تلفن همراه از طریق اتصال 3G شبیه‌سازی شده اجرا می‌شود. اسکنر پیش‌بارگذاری، منبع تصویر را قبل از شروع بارگذاری CSS و جاوا اسکریپت کشف می‌کند، که به مرورگر امکان می‌دهد بارگذاری آن را سریع‌تر انجام دهد.

نتیجه در این مثال ساده، بهبود ۱۰۰ میلی‌ثانیه‌ای در LCP در یک اتصال کند است. این ممکن است بهبود بزرگی به نظر نرسد، اما وقتی در نظر بگیرید که راه‌حل، یک اصلاح سریع نشانه‌گذاری است و اینکه اکثر صفحات وب پیچیده‌تر از این مجموعه مثال‌ها هستند، این بهبود بزرگ به نظر می‌رسد. این بدان معناست که نامزدهای LCP ممکن است مجبور باشند برای پهنای باند با منابع بسیار دیگری رقابت کنند، بنابراین بهینه‌سازی‌هایی از این دست اهمیت فزاینده‌ای پیدا می‌کنند.

تصاویر پس زمینه CSS

به یاد داشته باشید که اسکنر پیش‌بارگذاری مرورگر، نشانه‌گذاری را اسکن می‌کند. این اسکنر انواع دیگر منابع، مانند CSS را که ممکن است شامل واکشی تصاویری باشد که توسط ویژگی background-image به آنها ارجاع داده می‌شود، اسکن نمی‌کند.

مرورگرها مانند HTML، CSS را در مدل شیء خود، که به عنوان CSSOM شناخته می‌شود، پردازش می‌کنند. اگر منابع خارجی هنگام ساخت CSSOM کشف شوند، آن منابع در زمان کشف درخواست می‌شوند و نه توسط اسکنر پیش‌بارگذاری.

فرض کنید کاندید LCP صفحه شما عنصری با ویژگی background-image در CSS باشد. هنگام بارگذاری منابع، اتفاقات زیر رخ می‌دهد:

نمودار آبشاری شبکه WebPageTest که صفحه‌ای را با یک کاندید LCP که از CSS با استفاده از ویژگی background-image بارگذاری شده است، نشان می‌دهد. از آنجا که تصویر کاندید LCP از نوع منبعی است که اسکنر پیش‌بارگذاری مرورگر نمی‌تواند آن را بررسی کند، بارگذاری منبع تا زمان دانلود و پردازش CSS به تأخیر می‌افتد و زمان رنگ‌آمیزی کاندید LCP را به تأخیر می‌اندازد.
شکل 10: نمودار آبشاری شبکه WebPageTest از یک صفحه وب که روی کروم روی دستگاه تلفن همراه از طریق اتصال 3G شبیه‌سازی شده اجرا می‌شود. کاندید LCP صفحه، عنصری با ویژگی background-image CSS است (ردیف 3). تصویری که درخواست می‌کند تا زمانی که تجزیه‌کننده CSS آن را پیدا نکند، شروع به دریافت نمی‌کند.

در این حالت، اسکنر پیش‌بارگذاری آنقدرها هم که درگیر می‌شود، شکست نمی‌خورد. با این حال، اگر یک کاندید LCP در صفحه از یک ویژگی CSS با ویژگی background-image باشد، شما می‌خواهید آن تصویر را پیش‌بارگذاری کنید:

<!-- Make sure this is in the <head> below any
     stylesheets, so as not to block them from loading -->
<link rel="preload" as="image" href="lcp-image.jpg">

این نکته‌ی rel=preload کوچک است، اما به مرورگر کمک می‌کند تا تصویر را زودتر از حالت عادی پیدا کند:

نمودار آبشاری شبکه WebPageTest که نشان می‌دهد یک تصویر پس‌زمینه CSS (که کاندید LCP است) به دلیل استفاده از اشاره‌گر rel=preload خیلی زودتر بارگذاری می‌شود. زمان LCP تقریباً ۲۵۰ میلی‌ثانیه بهبود یافته است.
شکل ۱۱: نمودار آبشاری شبکه WebPageTest از یک صفحه وب که روی کروم روی دستگاه تلفن همراه از طریق اتصال 3G شبیه‌سازی شده اجرا می‌شود. کاندید LCP صفحه، عنصری با ویژگی background-image در CSS است (ردیف ۳). اشاره rel=preload به مرورگر کمک می‌کند تا تصویر را حدود ۲۵۰ میلی‌ثانیه زودتر از زمانی که اشاره‌ای وجود ندارد، کشف کند.

با استفاده از راهنمایی rel=preload ، تصویر کاندید LCP زودتر کشف می‌شود و زمان LCP کاهش می‌یابد. اگرچه این راهنمایی به رفع این مشکل کمک می‌کند، اما گزینه بهتر می‌تواند ارزیابی این باشد که آیا تصویر کاندید LCP شما باید از CSS بارگذاری شود یا خیر. با استفاده از تگ <img> ، کنترل بیشتری بر بارگذاری تصویری که برای صفحه نمایش مناسب است، خواهید داشت و در عین حال به اسکنر پیش‌بارگذاری اجازه می‌دهید آن را کشف کند.

منابع زیادی را در متن قرار دهید

درون‌خطی کردن روشی است که یک منبع را درون HTML قرار می‌دهد. می‌توانید stylesheetها را در عناصر <style> ، اسکریپت‌ها را در عناصر <script> و تقریباً هر منبع دیگری را که از کدگذاری base64 استفاده می‌کند، درون‌خطی کنید.

منابع را می‌توان به صورت درون‌خطی (inline) بارگذاری کرد، زیرا درخواست جداگانه‌ای برای منبع ارسال نمی‌شود. این کار مستقیماً در سند انجام می‌شود و فوراً بارگیری می‌شود. با این حال، معایب قابل توجهی نیز وجود دارد:

  • اگر HTML خود را ذخیره نمی‌کنید - و اگر پاسخ HTML پویا باشد، نمی‌توانید این کار را انجام دهید - منابع درون‌خطی هرگز ذخیره نمی‌شوند. این امر بر عملکرد تأثیر می‌گذارد زیرا منابع درون‌خطی قابل استفاده مجدد نیستند.
  • حتی اگر بتوانید HTML را ذخیره کنید، منابع درون‌خطی بین اسناد به اشتراک گذاشته نمی‌شوند. این امر باعث کاهش کارایی ذخیره سازی در مقایسه با فایل‌های خارجی می‌شود که می‌توانند در کل یک مبدا ذخیره و دوباره استفاده شوند.
  • اگر بیش از حد از inline استفاده کنید، اسکنر preload را از کشف منابع در سند باز می‌دارید، زیرا دانلود محتوای اضافی inline شده زمان بیشتری طول می‌کشد.

به عنوان مثال، این صفحه را در نظر بگیرید. در شرایط خاص، کاندید LCP تصویر بالای صفحه است و CSS در یک فایل جداگانه که توسط یک عنصر <link> بارگذاری می‌شود، قرار دارد. این صفحه همچنین از چهار فونت وب استفاده می‌کند که به عنوان فایل‌های جداگانه از منبع CSS درخواست شده‌اند.

نمودار آبشاری شبکه WebPageTest از صفحه با یک فایل CSS خارجی که چهار فونت در آن ارجاع داده شده است. تصویر کاندید LCP در زمان مناسب توسط اسکنر پیش بارگذاری کشف می‌شود.
شکل ۱۲: نمودار آبشاری شبکه WebPageTest از یک صفحه وب که در مرورگر کروم روی دستگاه تلفن همراه از طریق اتصال 3G شبیه‌سازی شده اجرا می‌شود. کاندید LCP صفحه، تصویری است که از یک عنصر <img> بارگذاری می‌شود، اما توسط اسکنر پیش‌بارگذاری کشف می‌شود زیرا CSS و فونت‌های مورد نیاز برای صفحه در منابع جداگانه بارگذاری می‌شوند، که باعث تأخیر در انجام کار اسکنر پیش‌بارگذاری نمی‌شود.

حالا اگر CSS و تمام فونت‌ها به صورت منابع base64 درون‌خطی شوند، چه اتفاقی می‌افتد؟

نمودار آبشاری شبکه WebPageTest از صفحه با یک فایل CSS خارجی که چهار فونت در آن ارجاع داده شده است. اسکنر پیش بارگذاری با تأخیر قابل توجهی تصویر LCP را کشف می‌کند.
شکل ۱۳: نمودار آبشاری شبکه WebPageTest از یک صفحه وب که روی مرورگر کروم روی دستگاه تلفن همراه از طریق اتصال 3G شبیه‌سازی شده اجرا می‌شود. کاندید LCP صفحه، تصویری است که از یک عنصر <img> بارگذاری شده است، اما درون‌خطی CSS و چهار منبع فونت آن در ` ` اسکنر پیش‌بارگذاری را از کشف تصویر تا زمان دانلود کامل آن منابع به تأخیر می‌اندازد.

تأثیر درون‌خطی کردن در این مثال، پیامدهای منفی برای LCP و به طور کلی برای عملکرد دارد. نسخه‌ای از صفحه که هیچ چیزی را درون‌خطی نمی‌کند، تصویر LCP را در حدود ۳.۵ ثانیه رسم می‌کند. صفحه‌ای که همه چیز را درون‌خطی می‌کند، تصویر LCP را تا کمی بیش از ۷ ثانیه رسم نمی‌کند.

در اینجا چیزی بیش از اسکنر پیش‌بارگذاری نقش دارد. درون‌خطی کردن فونت‌ها استراتژی خوبی نیست زیرا base64 فرمتی ناکارآمد برای منابع باینری است. عامل دیگر این است که منابع فونت خارجی دانلود نمی‌شوند مگر اینکه توسط CSSOM ضروری تشخیص داده شوند. وقتی این فونت‌ها به صورت base64 درون‌خطی می‌شوند، چه برای صفحه فعلی مورد نیاز باشند و چه نباشند، دانلود می‌شوند.

آیا پیش‌بارگذاری می‌تواند اوضاع را بهبود بخشد؟ بله. شما می‌توانید تصویر LCP را پیش‌بارگذاری کنید و زمان LCP را کاهش دهید، اما حجیم کردن HTML بالقوه غیرقابل‌کش شدن شما با منابع درون‌خطی‌شده، پیامدهای منفی دیگری نیز برای عملکرد دارد. First Contentful Paint (FCP) نیز تحت تأثیر این الگو قرار می‌گیرد. در نسخه‌ای از صفحه که هیچ چیز درون‌خطی نشده است، FCP تقریباً ۲.۷ ثانیه است. در نسخه‌ای که همه چیز درون‌خطی شده است، FCP تقریباً ۵.۸ ثانیه است.

در مورد inline کردن مطالب در HTML، به خصوص منابع کدگذاری شده با base64، بسیار مراقب باشید. به طور کلی این کار توصیه نمی‌شود، مگر برای منابع بسیار کوچک. تا حد امکان کمتر inline کنید، زیرا inline کردن بیش از حد، بازی با آتش است.

رندر کردن نشانه‌گذاری با جاوا اسکریپت سمت کلاینت

شکی در این مورد نیست: جاوا اسکریپت قطعاً بر سرعت صفحه تأثیر می‌گذارد . توسعه‌دهندگان نه تنها برای ایجاد تعامل به آن وابسته هستند، بلکه تمایلی نیز وجود داشته است که برای ارائه خود محتوا نیز به آن متکی باشند. این امر از برخی جهات منجر به تجربه بهتر توسعه‌دهندگان می‌شود؛ اما مزایای آن برای توسعه‌دهندگان همیشه به مزایایی برای کاربران تبدیل نمی‌شود.

یکی از الگوهایی که می‌تواند اسکنر پیش‌بارگذاری را شکست دهد، رندر کردن نشانه‌گذاری با جاوا اسکریپت سمت کلاینت است:

یک آبشار شبکه WebPageTest که یک صفحه اصلی با تصاویر و متن را نشان می‌دهد که به طور کامل در کلاینت با جاوا اسکریپت رندر شده‌اند. از آنجا که نشانه‌گذاری در جاوا اسکریپت قرار دارد، اسکنر پیش‌بارگذاری نمی‌تواند هیچ یک از منابع را شناسایی کند. به دلیل شبکه اضافی و زمان پردازشی که چارچوب‌های جاوا اسکریپت به آن نیاز دارند، تمام منابع به طور اضافی به تأخیر می‌افتند.
شکل ۱۴: نمودار آبشاری شبکه WebPageTest از یک صفحه وب رندر شده توسط کلاینت که روی کروم روی دستگاه تلفن همراه از طریق اتصال 3G شبیه‌سازی شده اجرا می‌شود. از آنجا که محتوا در جاوا اسکریپت موجود است و برای رندر به یک چارچوب متکی است، منبع تصویر در نشانه‌گذاری رندر شده توسط کلاینت از اسکنر پیش‌بارگذاری پنهان است. تجربه معادل رندر شده توسط سرور در شکل ۹ نشان داده شده است.

وقتی کدهای نشانه‌گذاری در مرورگر به طور کامل توسط جاوا اسکریپت رندر می‌شوند، هر منبعی در آن نشانه‌گذاری عملاً برای اسکنر پیش‌بارگذاری نامرئی است. این امر کشف منابع مهم را به تأخیر می‌اندازد که قطعاً بر LCP تأثیر می‌گذارد. در مورد این مثال‌ها، درخواست تصویر LCP در مقایسه با تجربه معادل رندر شده توسط سرور که نیازی به نمایش جاوا اسکریپت ندارد، به طور قابل توجهی تأخیر دارد.

این موضوع کمی از تمرکز این مقاله خارج می‌شود، اما اثرات رندر کردن نشانه‌گذاری روی کلاینت بسیار فراتر از شکست دادن اسکنر پیش‌بارگذاری است. اولاً، معرفی جاوا اسکریپت برای تقویت تجربه‌ای که نیازی به آن ندارد، زمان پردازش غیرضروری را ایجاد می‌کند که می‌تواند بر تعامل با رنگ بعدی (INP) تأثیر بگذارد. رندر کردن مقادیر بسیار زیاد نشانه‌گذاری روی کلاینت، در مقایسه با همان مقدار نشانه‌گذاری که توسط سرور ارسال می‌شود، احتمال بیشتری دارد که وظایف طولانی ایجاد کند. دلیل این امر - گذشته از پردازش اضافی که جاوا اسکریپت درگیر آن است - این است که مرورگرها نشانه‌گذاری را از سرور پخش می‌کنند و رندر را به گونه‌ای تقسیم می‌کنند که تمایل به محدود کردن وظایف طولانی دارد. از سوی دیگر، نشانه‌گذاری رندر شده توسط کلاینت به عنوان یک وظیفه واحد و یکپارچه مدیریت می‌شود که ممکن است بر INP صفحه تأثیر بگذارد.

راه حل این سناریو به پاسخ این سوال بستگی دارد: آیا دلیلی وجود دارد که نشانه‌گذاری صفحه شما نمی‌تواند توسط سرور ارائه شود، نه اینکه در کلاینت رندر شود؟ اگر پاسخ به این سوال "خیر" است، رندر سمت سرور (SSR) یا نشانه‌گذاری تولید شده به صورت استاتیک باید در صورت امکان در نظر گرفته شود، زیرا به اسکنر پیش بارگذاری کمک می‌کند تا منابع مهم را قبل از زمان کشف و به صورت فرصت‌طلبانه دریافت کند.

اگر صفحه شما برای اتصال برخی از بخش‌های نشانه‌گذاری صفحه به جاوا اسکریپت نیاز دارد ، می‌توانید این کار را با SSR، یا با جاوا اسکریپت معمولی، یا با هیدراتاسیون انجام دهید تا از هر دو مزیت بهره‌مند شوید.

به اسکنر پیش بارگذاری کمک کنید تا به شما کمک کند

اسکنر پیش‌بارگذاری، یک بهینه‌سازی بسیار مؤثر مرورگر است که به بارگذاری سریع‌تر صفحات در هنگام راه‌اندازی کمک می‌کند. با اجتناب از الگوهایی که توانایی آن را در کشف منابع مهم قبل از زمان مقرر مختل می‌کنند، شما نه تنها توسعه را برای خودتان ساده‌تر می‌کنید، بلکه تجربیات کاربری بهتری ایجاد می‌کنید که نتایج بهتری را در بسیاری از معیارها، از جمله برخی از موارد حیاتی وب ، ارائه می‌دهد.

برای جمع‌بندی، نکات زیر را که باید از این پست برداشت کنید، در اینجا آورده‌ایم:

  • اسکنر پیش‌بارگذاری مرورگر، یک تجزیه‌گر HTML ثانویه است که در صورت مسدود بودن، قبل از تجزیه‌گر اصلی، آن را اسکن می‌کند تا منابعی را که می‌تواند زودتر دریافت کند، به صورت فرصت‌طلبانه کشف کند.
  • منابعی که در نشانه‌گذاری ارائه شده توسط سرور در درخواست ناوبری اولیه وجود ندارند، توسط اسکنر پیش‌بارگذاری قابل کشف نیستند. راه‌هایی که می‌توان اسکنر پیش‌بارگذاری را شکست داد ممکن است شامل موارد زیر باشد (اما محدود به آنها نیست):
    • تزریق منابع به DOM با جاوا اسکریپت، چه اسکریپت‌ها، تصاویر، استایل‌شیت‌ها یا هر چیز دیگری که بهتر است در بارگذاری اولیه نشانه‌گذاری از سرور باشد.
    • بارگذاری تنبل تصاویر یا آی‌فریم‌های بالای صفحه با استفاده از یک راه‌حل جاوا اسکریپت.
    • رندر کردن نشانه‌گذاری روی کلاینت که ممکن است شامل ارجاعاتی به زیرمنابع سند با استفاده از جاوا اسکریپت باشد.
  • اسکنر پیش‌بارگذاری فقط HTML را اسکن می‌کند. محتوای سایر منابع - به‌ویژه CSS - را که ممکن است شامل ارجاعاتی به دارایی‌های مهم، از جمله نامزدهای LCP، باشند، بررسی نمی‌کند.

اگر به هر دلیلی نمی‌توانید از الگویی که بر توانایی اسکنر پیش‌بارگذاری در افزایش سرعت عملکرد بارگذاری تأثیر منفی می‌گذارد، اجتناب کنید، نکته‌ی rel=preload resource را در نظر بگیرید. اگر از rel=preload استفاده می‌کنید ، آن را در ابزارهای آزمایشگاهی آزمایش کنید تا مطمئن شوید که اثر مطلوب را به شما می‌دهد. در نهایت، منابع زیادی را پیش‌بارگذاری نکنید، زیرا وقتی همه چیز را اولویت‌بندی می‌کنید، هیچ چیز اولویت‌بندی نخواهد شد.

منابع

تصویر قهرمان از Unsplash ، اثر محمد رحمانی .