آشنایی با «برنامه وب»
برنامههای وب با عملکرد بالا برای تجربه کاربری عالی حیاتی هستند. با پیچیدهتر شدن برنامههای وب، درک تأثیر عملکرد برای ایجاد تجربهای جذاب حیاتی است. در چند سال گذشته، تعدادی API مختلف در مرورگر ظاهر شدهاند تا به تجزیهوتحلیل عملکرد شبکه، زمان بارگیری، و غیره کمک کنند، اما این APIها لزوماً جزئیات دقیق با انعطافپذیری کافی برای یافتن آنچه برنامه شما را کند میکند ارائه نمیدهند. User Timing API را وارد کنید که سازوکاری ارائه میدهد که میتوانید از آن برای ابزارسازی برنامه وب خود استفاده کنید تا مشخص شود برنامهتان زمان خود را کجا صرف میکند. در این مقاله، به API و نمونههایی از نحوه استفاده از آن میپردازیم.
چیزی را که نمیتوانید اندازهگیری کنید نمیتوانید بهینهسازی کنید
اولین قدم برای سرعت بخشیدن به برنامه وب کند این است که بدانید زمان کجا صرف میشود. اندازهگیری تأثیر زمانی بخشهای کد جاوا اسکریپت بهترین روش برای شناسایی نقاط داغ است که اولین گام در یافتن نحوه بهبود عملکرد است. خوشبختانه User Timing API روشی را ارائه میدهد که میتوانید فراخوانیهای میانای برنامهسازی کاربردی را در بخشهای مختلف جاوا اسکریپت خود درج کنید و سپس دادههای زمانبندی دقیق را استخراج کنید که میتواند برای کمک به بهینهسازی شما استفاده شود.
زمان با وضوح بالا و now()
بخش اساسی اندازهگیری دقیق زمان، دقت است. در گذشته، زمانبندی براساس اندازهگیری میلیثانیه بود که خوب است، اما ساختن سایت ۶۰ فریم در ثانیه بدون پرش به این معنی است که هر فریم باید در ۱۶ میلیثانیه ترسیم شود. بنابراین وقتی فقط دقت میلیثانیهای دارید، دقت لازم برای تحلیل خوب را ندارید. زمان با وضوح بالا را وارد کنید، نوع زمانبندی جدیدی که در مرورگرهای مدرن تعبیه شده است. زمان با وضوح بالا مُهرهای زمان ممیز شناور را به ما میدهد که میتواند تا وضوح میکروثانیه دقیق باشد - هزار برابر بهتر از قبل.
برای دریافت زمان فعلی در برنامه وب، روش now() را فراخوانی کنید که افزونهای از میانای عملکرد را تشکیل میدهد. کد زیر نحوه انجام این کار را نشان میدهد:
var myTime = window.performance.now();
میانای دیگری بهنام PerformanceTiming وجود دارد که تعدادی زمان مختلف مربوط به نحوه بار شدن برنامه وب شما را ارائه میدهد. روش now() زمان سپریشده از وقتی را که navigationStartزمان در PerformanceTiming اتفاق افتاده است برمیگرداند.
نوع DOMHighResTimeStamp
وقتی در گذشته میخواستید برنامههای وب را زمانبندی کنید، از چیزی مثل Date.now() استفاده میکردید که DOMTimeStamp را برمیگرداند. DOMTimeStamp یک عدد صحیح از میلیثانیهها را بهعنوان مقدار خود برمیگرداند. برای ارائه دقت بالاتر موردنیاز برای زمان «وضوح بالا»، نوع جدیدی بهنام DOMHighResTimeStamp معرفی شد. این نوع یک مقدار نقطه شناور است که زمان را نیز برحسب میلیثانیه برمیگرداند. اما چون ممیز شناور است، مقدار میتواند نشاندهنده میلیثانیه کسری باشد و بنابراین میتواند دقت یک هزارم میلیثانیه را ارائه دهد.
میانای «زمانبندی کاربر»
اکنون که مُهرهای زمان با وضوح بالا داریم، بیایید از واسط زمانبندی کاربر برای استخراج اطلاعات زمانبندی استفاده کنیم.
رابط «زمانبندی کاربر» عملکردهایی ارائه میدهد که به ما امکان میدهد روشها را در مکانهای مختلف برنامهمان فراخوانی کنیم که میتواند یک مسیر راهنمای سبک هانسل و گرتل ارائه دهد تا به ما امکان دهد ردیابی کنیم زمان کجا صرف میشود.
درحال استفاده از mark()
روش mark() ابزار اصلی در جعبه ابزار تحلیل زمانبندی ما است. کاری که mark() میکند این است که مُهر زمان را برای ما ذخیره میکند. ویژگی بسیار مفید mark() این است که میتوانیم مُهر زمان را نامگذاری کنیم و میانای برنامهسازی کاربردی نام و مُهر زمان را بهعنوان یک واحد بهخاطر خواهد سپرد.
فراخوانی mark() در جاهای مختلف برنامهتان به شما امکان میدهد تا متوجه شوید چقدر زمان برده است تا به آن «علامت» در برنامه وب خود برسید.
این مشخصات تعدادی نام پیشنهادی را برای علامتگذاریها ذکر میکند که ممکن است جالب باشند و بهاندازه کافی گویا هستند، مانند mark_fully_loaded، mark_fully_visible، mark_above_the_fold، و غیره.
برای مثال، میتوانیم بااستفاده از کد زیر، علامتی برای زمانی که برنامه کاملاً بارگیری میشود تنظیم کنیم:
window.performance.mark('mark_fully_loaded');
با تنظیم نشانهای نامگذاریشده در سراسر برنامه وبمان، میتوانیم انبوهی از دادههای زمانبندی را جمعآوری کنیم و آنها را در اوقات فراغت خود تجزیهوتحلیل کنیم تا بفهمیم برنامه چه کاری را در چه زمانی انجام میدهد.
درحال محاسبه اندازهگیریها با measure()
وقتی تعدادی علامت زمان تنظیم کردید، میخواهید زمان سپریشده بین آنها را بدانید. برای انجام این کار از روش measure() استفاده میکنید.
روش measure() زمان سپریشده بین علامتها را محاسبه میکند و همچنین میتواند زمان بین علامت شما و هرکدام از نامهای رویداد شناختهشده در میانای PerformanceTiming را اندازهگیری کند.
برای مثال، میتوانید زمان تکمیل DOM تا بار شدن کامل وضعیت برنامهتان را بااستفاده از کدی مانند کد زیر محاسبه کنید:
window.performance.measure('measure_load_from_dom', 'domComplete', 'mark_fully_loaded');
وقتی measure() را فراخوانی میکنید، نتیجه را مستقل از علامتهایی که تنظیم کردهاید ذخیره میکند تا بتوانید بعداً آنها را بازیابی کنید. با ذخیره کردن زمانهای دور از دسترس درحین اجرای برنامه، برنامه پاسخگو باقی میماند و میتوانید پساز اینکه برنامه برخیاز کارها را بهپایان رساند، همه دادهها را تخلیه کنید تا بعداً تجزیهوتحلیل شوند.
درحال صرفنظر کردن از نمرهها با clearMarks()
گاهی اوقات مفید است که بتوانید از شر دستهای از علامتهایی که تنظیم کردهاید خلاص شوید. برای مثال، ممکن است بخواهید اجرای دستهای در برنامه وب خود انجام دهید و بنابراین میخواهید هر اجرا را از ابتدا شروع کنید.
با گفتن clearMarks()، بهراحتی میتوانید از شر هر علامتی که تنظیم کردهاید خلاص شوید.
بنابراین کد نمونه زیر همه علامتهای موجود شما را پاک میکند تا بتوانید درصورت تمایل، زمانبندی اجرا را دوباره تنظیم کنید.
window.performance.clearMarks();
البته، در برخی سناریوها ممکن است نخواهید همه علامتهایتان را پاک کنید. بنابراین اگر میخواهید از شر علامتهای خاصی خلاص شوید، میتوانید فقط نام علامتی را که میخواهید بردارید ارسال کنید. برای مثال، کد زیر:
window.performance.clearMarks('mark_fully_loaded');
علامتی را که در مثال اول تنظیم کردیم حذف میکند، درحالیکه سایر علامتهایی را که تنظیم کردهایم بدون تغییر باقی میگذارد.
شاید بخواهید از شر هرگونه اندازهگیریای که انجام دادهاید نیز خلاص شوید، و روشی متناظر برای انجام این کار وجود دارد که clearMeasures() نامیده میشود. این دکمه دقیقاً مانند clearMarks() عمل میکند، اما بهجای آن روی اندازهگیریهایی که انجام دادهاید کار میکند. برای مثال، کد:
window.performance.clearMeasures('measure_load_from_dom');
اندازهگیریای را که در مثال measure() بالا انجام دادیم برمیدارد. اگر میخواهید همه معیارها را بردارید، این کار درست مثل clearMarks() انجام میشود - یعنی فقط clearMeasures() را بدون آرگومان فراخوانی میکنید.
دریافت دادههای زمانبندی
تنظیم علامتها و اندازهگیری فاصلهها خوب و مفید است، اما در نقطهای میخواهید به آن دادههای زمانبندی دسترسی پیدا کنید تا تحلیلی انجام دهید. این کار هم واقعاً ساده است، فقط باید از میانای PerformanceTimeline استفاده کنید.
برای مثال، روش getEntriesByType() به ما امکان میدهد همه زمانهای علامتگذاری یا همه زمانهای اندازهگیری را بهصورت فهرست دریافت کنیم تا بتوانیم آن را تکرار کنیم و دادهها را هضم کنیم. نکته خوب این است که فهرست بهترتیب زمانی برگردانده میشود، بنابراین میتوانید علامتها را بهترتیبی که در برنامه وب شما زده شدهاند ببینید.
کد زیر:
var items = window.performance.getEntriesByType('mark');
فهرستی از همه علامتهایی را که در برنامه وب ما زده شده است به ما برمیگرداند، درحالیکه کد:
var items = window.performance.getEntriesByType('measure');
فهرستی از همه اندازهگیریهایی که انجام دادهایم به ما برمیگرداند.
همچنین میتوانید بااستفاده از نام خاصی که به ورودیها دادهاید، فهرست آنها را برگردانید. بنابراین برای مثال، کد:
var items = window.performance.getEntriesByName('mark_fully_loaded');
فهرستی با یک مورد حاوی مُهر زمان «mark_fully_loaded» در ویژگی startTime به ما برمیگرداند.
زمانبندی درخواست XHR (نمونه)
اکنون که تصویر مناسبی از User Timing API داریم، میتوانیم از آن برای تجزیهوتحلیل مدتزمان همه XMLHttpRequests در برنامه وب خود استفاده کنیم.
ابتدا همه درخواستهای send() را اصلاح میکنیم تا فراخوانی تابعی را صادر کنند که نشانها را تنظیم میکند، و همزمان فراخوانیهای برگشتی موفقیت را با فراخوانی تابعی تغییر میدهیم که نشان دیگری را تنظیم میکند و سپس معیاری از مدتزمان درخواست تولید میکند.
بنابراین معمولاً XMLHttpRequest ما چیزی شبیه به این خواهد بود:
var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
do_something(e.responseText);
}
myReq.send();
برای مثال، یک شمارنده جهانی اضافه میکنیم تا تعداد درخواستها را ردیابی کنیم و همچنین از آن برای ذخیره کردن یک معیار برای هر درخواست انجامشده استفاده کنیم. کد انجام این کار به این شکل است:
var reqCnt = 0;
var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
window.performance.mark('mark_end_xhr');
reqCnt++;
window.performance.measure('measure_xhr_' + reqCnt, 'mark_start_xhr', 'mark_end_xhr');
do_something(e.responseText);
}
window.performance.mark('mark_start_xhr');
myReq.send();
کد بالا برای هر XMLHttpRequest که ارسال میکنیم، یک معیار با مقدار نام یکتا تولید میکند. فرض میکنیم درخواستها بهصورت متوالی اجرا میشوند - کد درخواستهای موازی باید کمی پیچیدهتر باشد تا درخواستهایی را که خارجاز ترتیب برمیگردند مدیریت کند، این را بهعنوان تمرینی برای خواننده میگذاریم.
وقتی برنامه وب درخواستهای زیادی انجام داد، میتوانیم همه آنها را بااستفاده از کد زیر به کنسول منتقل کنیم:
var items = window.performance.getEntriesByType('measure');
for (var i = 0; i < items.length; ++i) {
var req = items[i];
console.log('XHR ' + req.name + ' took ' + req.duration + 'ms');
}
نتیجهگیری
User Timing API ابزارهای عالی زیادی را دراختیارتان قرار میدهد تا در هر جنبهای از برنامه وب خود اعمال کنید. Narrowing down the hot-spots in your application can be easily achieved by sprinkling API calls throughout your web application and post-processing the timing data generated to create a clear picture of where time is being spent. اما اگر مرورگرتان از این میانای برنامهسازی کاربردی پشتیبانی نکند چه؟ مشکلی نیست، میتوانید شبیهساز مرورگر عالیای را در اینجا پیدا کنید که میانای برنامهسازی کاربردی را بهخوبی شبیهسازی میکند و با webpagetest.org نیز بهخوبی کار میکند. بنابراین منتظر چه هستید؟ اکنون User Timing API را در برنامههایتان امتحان کنید، متوجه خواهید شد که چگونه سرعت آنها را افزایش دهید و کاربران از شما بهخاطر بهبود تجربه کاربریشان تشکر خواهند کرد.