ارزیابی اسکریپت و کارهای طولانی

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

جرمی واگنر
جرمی واگنر

وقتی صحبت از بهینه‌سازی Interaction to Next Paint (INP) می‌شود، بیشتر توصیه‌هایی که با آن مواجه می‌شوید این است که خود تعاملات را بهینه کنید. به عنوان مثال، در راهنمای بهینه سازی وظایف طولانی ، تکنیک هایی مانند تسلیم شدن با setTimeout ، isInputPending و غیره مورد بحث قرار گرفته است. این تکنیک‌ها سودمند هستند، زیرا با اجتناب از کارهای طولانی، به نخ اصلی اجازه می‌دهند تا فضای تنفسی داشته باشد، که می‌تواند فرصت‌های بیشتری را برای تعاملات و سایر فعالیت‌ها فراهم کند تا زودتر اجرا شوند، نه اینکه مجبور باشند برای یک کار طولانی منتظر بمانند.

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

ارزیابی فیلمنامه چیست؟

اگر برنامه‌ای را که جاوا اسکریپت زیادی ارسال می‌کند پروفایل کرده‌اید، ممکن است کارهای طولانی را دیده باشید که مجرم با برچسب ارزیابی اسکریپت است.

کار ارزیابی اسکریپت همانطور که در نمایه ساز عملکرد Chrome DevTools تجسم شده است. این کار باعث ایجاد یک کار طولانی در هنگام راه اندازی می شود که توانایی نخ اصلی را برای پاسخ به تعاملات کاربر مسدود می کند.
کار ارزیابی اسکریپت همانطور که در نمایه ساز عملکرد در Chrome DevTools نشان داده شده است. در این مورد، کار برای ایجاد یک کار طولانی کافی است که رشته اصلی را از انجام کارهای دیگر مسدود می کند - از جمله وظایفی که باعث تعامل کاربر می شود.

ارزیابی اسکریپت بخشی ضروری از اجرای جاوا اسکریپت در مرورگر است، زیرا جاوا اسکریپت به موقع قبل از اجرا کامپایل می شود. هنگامی که یک اسکریپت ارزیابی می شود، ابتدا برای خطاها تجزیه می شود. اگر تجزیه کننده خطاها را پیدا نکرد، اسکریپت سپس در بایت کد کامپایل می شود و سپس می تواند به اجرا ادامه دهد.

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

رابطه بین اسکریپت ها و وظایفی که آنها را ارزیابی می کند

اینکه چگونه وظایفی که مسئول ارزیابی اسکریپت هستند، به این بستگی دارد که آیا اسکریپتی که بارگیری می کنید از طریق یک عنصر <script> معمولی بارگیری می شود یا اینکه یک اسکریپت یک ماژول بارگذاری شده با type=module است. از آنجایی که مرورگرها تمایل دارند مسائل را به گونه‌ای متفاوت مدیریت کنند، نحوه انجام ارزیابی اسکریپت توسط موتورهای مرورگر اصلی در جایی که رفتارهای ارزیابی اسکریپت در آنها متفاوت است، مورد بررسی قرار خواهد گرفت.

بارگیری اسکریپت ها با عنصر <script>

تعداد وظایفی که برای ارزیابی اسکریپت ها ارسال می شود معمولاً با تعداد عناصر <script> در یک صفحه رابطه مستقیم دارد. هر عنصر <script> یک کار را برای ارزیابی اسکریپت درخواستی آغاز می کند تا بتوان آن را تجزیه، کامپایل و اجرا کرد. این مورد برای مرورگرهای مبتنی بر Chromium، Safari و Firefox است.

چرا این مهم است؟ فرض کنید از یک بسته‌کننده برای مدیریت اسکریپت‌های تولید خود استفاده می‌کنید، و آن را طوری پیکربندی کرده‌اید که همه چیزهایی را که صفحه‌تان برای اجرا نیاز دارد در یک اسکریپت واحد قرار دهد. اگر این مورد برای وب سایت شما باشد، می توانید انتظار داشته باشید که یک وظیفه برای ارزیابی آن اسکریپت ارسال شود. آیا این چیز بدی است؟ نه لزوما - مگر اینکه آن اسکریپت بزرگ باشد.

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

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

چندین کار شامل ارزیابی اسکریپت همانطور که در نمایه ساز عملکرد Chrome DevTools تجسم شده است. از آنجایی که چندین اسکریپت کوچکتر به جای اسکریپت های کوچکتر بارگذاری می شوند، وظایف کمتر به وظایف طولانی تبدیل می شوند و به رشته اصلی اجازه می دهد تا سریعتر به ورودی کاربر پاسخ دهد.
وظایف متعددی برای ارزیابی اسکریپت ها در نتیجه عناصر <script> متعدد موجود در HTML صفحه ایجاد می شوند. این به ارسال یک بسته اسکریپت بزرگ برای کاربران ترجیح داده می شود، که احتمال بیشتری دارد تا رشته اصلی را مسدود کند.

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

بارگیری اسکریپت ها با عنصر <script> و ویژگی type=module

اکنون می توان ماژول های ES را به صورت بومی در مرورگر با ویژگی type=module در عنصر <script> بارگیری کرد. این رویکرد برای بارگذاری اسکریپت دارای برخی از مزایای تجربه توسعه‌دهنده است، مانند عدم نیاز به تغییر کد برای استفاده در تولید - به‌ویژه زمانی که در ترکیب با نقشه‌های وارداتی استفاده می‌شود. با این حال، بارگیری اسکریپت ها به این روش، وظایفی را که از مرورگر به مرورگر متفاوت است، زمان بندی می کند.

مرورگرهای مبتنی بر کروم

در مرورگرهایی مانند کروم - یا مرورگرهای مشتق شده از آن - بارگیری ماژول‌های ES با استفاده از ویژگی type=module انواع مختلفی از وظایف را ایجاد می‌کند که معمولاً هنگام عدم استفاده از type=module مشاهده می‌کنید. به عنوان مثال، یک کار برای هر اسکریپت ماژول اجرا می شود که شامل فعالیتی است که به عنوان ماژول کامپایل برچسب گذاری شده است.

کامپایل ماژول‌ها در چندین کار که در Chrome DevTools تجسم شده است، کار می‌کند.
رفتار بارگیری ماژول در مرورگرهای مبتنی بر Chromium. هر اسکریپت ماژول یک فراخوانی ماژول Compile ایجاد می کند تا محتویات خود را قبل از ارزیابی جمع آوری کند.

هنگامی که ماژول ها کامپایل شدند، هر کدی که متعاقباً در آنها اجرا می شود، فعالیتی را آغاز می کند که به عنوان ماژول ارزیابی برچسب گذاری شده است.

ارزیابی به موقع یک ماژول همانطور که در پانل عملکرد Chrome DevTools تجسم شده است.
هنگامی که کد در یک ماژول اجرا می شود، آن ماژول به موقع ارزیابی می شود.

اثر اینجا - حداقل در کروم و مرورگرهای مرتبط - این است که مراحل کامپایل هنگام استفاده از ماژول‌های ES شکسته می‌شوند. این یک پیروزی آشکار از نظر مدیریت وظایف طولانی است. با این حال، کار ارزیابی ماژول حاصل که نتیجه می‌شود همچنان به این معنی است که شما هزینه‌های غیرقابل اجتنابی را متحمل می‌شوید. در حالی که باید تلاش کنید تا جاوا اسکریپت کمتری ارسال کنید، استفاده از ماژول های ES - صرف نظر از مرورگر - مزایای زیر را ارائه می دهد:

  • همه کدهای ماژول به طور خودکار در حالت سخت اجرا می شوند، که امکان بهینه سازی های بالقوه توسط موتورهای جاوا اسکریپت را فراهم می کند که در غیر این صورت نمی توانند در یک زمینه غیر دقیق انجام شوند.
  • اسکریپت هایی که با استفاده از type=module بارگذاری می شوند، به گونه ای رفتار می شوند که گویی به طور پیش فرض به تعویق افتاده اند. برای تغییر این رفتار می توان از ویژگی async در اسکریپت های بارگذاری شده با type=module استفاده کرد.

سافاری و فایرفاکس

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

بارگیری اسکریپت ها با import()

Dynamic import() روش دیگری برای بارگذاری اسکریپت ها است. برخلاف دستورهای import static که لازم است در بالای یک ماژول ES قرار گیرند، یک فراخوانی import() پویا می‌تواند در هر جایی از یک اسکریپت ظاهر شود تا تکه‌ای از جاوا اسکریپت را در صورت درخواست بارگیری کند. به این تکنیک تقسیم کد می گویند.

Dynamic import() در بهبود INP دو مزیت دارد:

  1. ماژول هایی که بارگذاری آنها به تعویق افتاده است، با کاهش مقدار جاوا اسکریپت بارگذاری شده در آن زمان، اختلاف موضوع اصلی در هنگام راه اندازی را کاهش می دهند. این موضوع رشته اصلی را آزاد می کند تا بتواند به تعاملات کاربر پاسخگوتر باشد.
  2. هنگامی که فراخوانی های import() پویا انجام می شود، هر فراخوانی به طور موثر کامپایل و ارزیابی هر ماژول را به وظیفه خود جدا می کند. البته، یک import() پویا که یک ماژول بسیار بزرگ را بارگذاری می‌کند، یک کار ارزیابی نسبتاً بزرگ اسکریپت را آغاز می‌کند، و در صورتی که تعامل همزمان با فراخوانی dynamic import() . بنابراین، هنوز هم بسیار مهم است که جاوا اسکریپت کمتری را که ممکن است بارگیری کنید.

فراخوانی Dynamic import() در تمام موتورهای مرورگر اصلی به طور یکسان عمل می کند: وظایف ارزیابی اسکریپت که نتیجه می شود با مقدار ماژول هایی که به صورت پویا وارد می شوند یکسان خواهد بود.

بارگیری اسکریپت ها در یک وب کارگر

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

علاوه بر کاهش کار رشته اصلی، کارگران وب خود می‌توانند اسکریپت‌های خارجی را برای استفاده در زمینه کارگر بارگذاری کنند، یا از طریق importScripts یا عبارات import ثابت در مرورگرهایی که کارگران ماژول را پشتیبانی می‌کنند. نتیجه این است که هر اسکریپت درخواست شده توسط یک وب کارگر از موضوع اصلی ارزیابی می شود.

مبادلات و ملاحظات

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

راندمان فشرده سازی

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

باندلرها ابزارهای ایده آلی برای مدیریت اندازه خروجی برای اسکریپت هایی هستند که وب سایت شما به آنها بستگی دارد:

  • در مورد بسته وب، افزونه SplitChunksPlugin آن می تواند کمک کند. برای یافتن گزینه هایی که می توانید برای کمک به مدیریت اندازه دارایی تنظیم کنید، با اسناد SplitChunksPlugin مشورت کنید.
  • برای سایر بسته‌ها مانند Rollup و esbuild ، می‌توانید اندازه فایل‌های اسکریپت را با استفاده از فراخوانی‌های import() پویا در کد خود مدیریت کنید. این بسته‌ها - و همچنین بسته وب - به‌طور خودکار دارایی‌های وارد شده به‌صورت پویا را در فایل خود جدا می‌کنند، بنابراین از اندازه‌های بزرگتر بسته‌های اولیه جلوگیری می‌کنند.

عدم اعتبار کش

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

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

ماژول های تو در تو و عملکرد بارگذاری

اگر ماژول‌های ES را در مرحله تولید ارسال می‌کنید و آنها را با ویژگی type=module بارگیری می‌کنید، باید از نحوه تأثیرگذاری تودرتوی ماژول‌ها بر زمان راه‌اندازی آگاه باشید. تودرتوی ماژول به زمانی اشاره دارد که یک ماژول ES به صورت ایستا ماژول ES دیگری را وارد می کند که به صورت ایستا ماژول ES دیگری را وارد می کند:

// a.js
import {b} from './b.js';

// b.js
import {c} from './c.js';

اگر ماژول‌های ES شما با هم ترکیب نشده باشند، کد قبلی منجر به یک زنجیره درخواست شبکه می‌شود: وقتی a.js از یک عنصر <script> درخواست می‌شود، درخواست شبکه دیگری برای b.js ارسال می‌شود که سپس درخواست دیگری برای c.js یکی از راه‌های جلوگیری از این امر استفاده از باندلر است - اما مطمئن شوید که باندلر خود را به گونه‌ای پیکربندی می‌کنید که اسکریپت‌ها را برای گسترش کار ارزیابی اسکریپت تجزیه کنید.

اگر نمی‌خواهید از یک بسته‌کننده استفاده کنید، راه دیگری برای دور زدن تماس‌های ماژول تودرتو، استفاده از اشاره منبع modulepreload است که برای جلوگیری از زنجیره درخواست شبکه، ماژول‌های ES را زودتر بارگیری می‌کند.

نتیجه

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

برای جمع بندی، در اینجا چند کار وجود دارد که می توانید برای تجزیه وظایف ارزیابی اسکریپت بزرگ انجام دهید:

  • هنگام بارگذاری اسکریپت‌ها با استفاده از عنصر <script> بدون ویژگی type=module ، از بارگیری اسکریپت‌هایی که بسیار بزرگ هستند خودداری کنید، زیرا این کارها وظایف ارزیابی اسکریپت با منابع فشرده را آغاز می‌کنند که رشته اصلی را مسدود می‌کند. اسکریپت‌های خود را روی عناصر <script> بیشتری پخش کنید تا این کار را تجزیه کنید.
  • استفاده از ویژگی type=module برای بارگیری ماژول های ES به صورت بومی در مرورگر، وظایف فردی را برای ارزیابی برای هر اسکریپت ماژول جداگانه آغاز می کند.
  • با استفاده از فراخوانی dynamic import() اندازه بسته‌های اولیه خود را کاهش دهید. این همچنین در باندلرها کار می کند، زیرا باندلرها با هر ماژول وارد شده به صورت پویا به عنوان یک "نقطه تقسیم" رفتار می کنند و در نتیجه یک اسکریپت جداگانه برای هر ماژول وارد شده به صورت پویا تولید می شود.
  • مطمئن شوید که مبادلاتی مانند راندمان فشرده سازی و باطل کردن حافظه پنهان را در نظر بگیرید. اسکریپت‌های بزرگ‌تر بهتر فشرده می‌شوند، اما احتمالاً کار ارزیابی اسکریپت گران‌تری را در وظایف کمتری شامل می‌شوند و منجر به باطل شدن حافظه پنهان مرورگر می‌شوند که منجر به راندمان کلی ذخیره‌سازی کمتر می‌شود.
  • اگر از ماژول‌های ES به صورت بومی و بدون بسته‌بندی استفاده می‌کنید، از راهنمایی منبع modulepreload برای بهینه‌سازی بارگذاری آن‌ها در هنگام راه‌اندازی استفاده کنید.
  • مثل همیشه، تا حد امکان جاوا اسکریپت کمتری ارسال کنید.

مطمئناً این یک اقدام متعادل کننده است - اما با شکستن اسکریپت ها و کاهش بارهای اولیه از طریق dynamic import() ، می توانید به عملکرد راه اندازی بهتری دست پیدا کنید و تعاملات کاربر را در طول آن دوره راه اندازی حیاتی انجام دهید. این باید به شما کمک کند در معیار INP امتیاز بهتری کسب کنید و در نتیجه تجربه کاربری بهتری را ارائه دهید.

تصویر قهرمان از Unsplash اثر مارکوس اسپیسکه .