کشف کنید چگونه Performance Observer API روشی قدرتمند و غیرمداخلهگر برای نظارت بر عملکرد وب در زمان اجرا، ردیابی Core Web Vitals و بهینهسازی تجربه کاربری برای مخاطبان جهانی ارائه میدهد.
گشودن قفل عملکرد وب: نگاهی عمیق به Performance Observer API
در دنیای دیجیتال پرسرعت امروز، عملکرد وب یک تجمل نیست؛ بلکه یک ضرورت است. یک وبسایت کند یا غیرپاسخگو میتواند منجر به ناامیدی کاربر، نرخ پرش بالاتر و تأثیر منفی مستقیم بر اهداف کسبوکار، چه فروش باشد، چه درآمد تبلیغات یا تعامل کاربر، شود. سالهاست که توسعهدهندگان به ابزارهایی تکیه کردهاند که عملکرد را در یک نقطه زمانی، معمولاً در هنگام بارگذاری اولیه صفحه، اندازهگیری میکنند. این رویکرد اگرچه مفید است، اما بخش مهمی از داستان را نادیده میگیرد: کل تجربه کاربر در حین تعامل با صفحه. اینجاست که نظارت بر عملکرد در زمان اجرا وارد میشود و قدرتمندترین ابزار آن Performance Observer API است.
روشهای سنتی اغلب شامل نظرسنجی (polling) برای دادههای عملکرد با توابعی مانند performance.getEntries() هستند. این کار میتواند ناکارآمد باشد، مستعد از دست دادن رویدادهای مهمی باشد که بین نظرسنجیها اتفاق میافتد و حتی میتواند به سربار عملکردی که سعی در اندازهگیری آن دارد، بیفزاید. Performance Observer API این فرآیند را با ارائه یک مکانیزم ناهمزمان (asynchronous) با سربار کم برای اشتراک در رویدادهای عملکرد به محض وقوع، متحول میکند. این راهنما شما را به یک غواصی عمیق در این API ضروری میبرد و به شما نشان میدهد که چگونه از قدرت آن برای نظارت بر Core Web Vitals، شناسایی گلوگاهها و در نهایت ساختن تجربیات وب سریعتر و لذتبخشتر برای مخاطبان جهانی استفاده کنید.
Performance Observer API چیست؟
در هسته خود، Performance Observer API یک رابط است که راهی برای مشاهده و جمعآوری رویدادهای اندازهگیری عملکرد، که به عنوان ورودیهای عملکرد (performance entries) شناخته میشوند، فراهم میکند. آن را به عنوان یک شنونده اختصاصی برای فعالیتهای مرتبط با عملکرد در مرورگر در نظر بگیرید. به جای اینکه شما فعالانه از مرورگر بپرسید، «آیا هنوز اتفاقی افتاده است؟»، مرورگر به طور پیشگیرانه به شما میگوید، «یک رویداد عملکرد جدید همین الان رخ داد! این هم جزئیات.»
این امر از طریق یک الگوی مشاهدهگر (observer pattern) به دست میآید. شما یک نمونه مشاهدهگر ایجاد میکنید، به آن میگویید به چه نوع رویدادهای عملکردی علاقهمند هستید (مثلاً، رنگهای بزرگ، ورودیهای کاربر، تغییرات چیدمان)، و یک تابع بازگشتی (callback function) ارائه میدهید. هر زمان که یک رویداد جدید از نوع مشخص شده در خط زمانی عملکرد مرورگر ثبت شود، تابع بازگشتی شما با لیستی از ورودیهای جدید فراخوانی میشود. این مدل مبتنی بر فشار (push-based) و ناهمزمان بسیار کارآمدتر و قابل اعتمادتر از مدل قدیمی مبتنی بر کشش (pull-based) یعنی فراخوانی مکرر performance.getEntries() است.
روش قدیمی در مقابل روش جدید
برای درک نوآوری Performance Observer، بیایید دو رویکرد را مقایسه کنیم:
- روش قدیمی (نظرسنجی - Polling): شما ممکن است از setTimeout یا requestAnimationFrame برای فراخوانی دورهای performance.getEntriesByName('my-metric') استفاده کنید تا ببینید آیا معیار شما ثبت شده است یا خیر. این کار مشکلساز است زیرا ممکن است خیلی دیر بررسی کنید و رویداد را از دست بدهید، یا خیلی مکرر بررسی کنید و چرخههای CPU را هدر دهید. همچنین اگر ورودیها را به طور منظم پاک نکنید، با خطر پر کردن بافر عملکرد مرورگر روبرو هستید.
- روش جدید (مشاهده - Observing): شما یک PerformanceObserver را یک بار تنظیم میکنید. این مشاهدهگر بیصدا در پسزمینه قرار میگیرد و حداقل منابع را مصرف میکند. به محض اینکه یک ورودی عملکرد مرتبط ثبت شود - چه یک میلیثانیه پس از بارگذاری صفحه باشد یا ده دقیقه پس از شروع جلسه کاربر - کد شما فوراً مطلع میشود. این تضمین میکند که شما هرگز یک رویداد را از دست ندهید و کد نظارت شما تا حد امکان کارآمد باشد.
چرا باید از Performance Observer استفاده کنید
ادغام Performance Observer API در جریان کاری توسعه شما، مزایای متعددی را ارائه میدهد که برای برنامههای وب مدرن با هدف دستیابی به مخاطبان جهانی، حیاتی هستند.
- نظارت غیرمداخلهگر: تابع بازگشتی مشاهدهگر معمولاً در دورههای بیکاری (idle periods) اجرا میشود و تضمین میکند که کد نظارت بر عملکرد شما با تجربه کاربر تداخل نداشته و رشته اصلی (main thread) را مسدود نمیکند. این API طوری طراحی شده است که سبک باشد و اثر عملکردی ناچیزی داشته باشد.
- دادههای جامع در زمان اجرا: وب پویا است. مشکلات عملکرد فقط در زمان بارگذاری رخ نمیدهند. یک کاربر ممکن است یک انیمیشن پیچیده را فعال کند، با اسکرول کردن محتوای بیشتری را بارگذاری کند، یا با یک مؤلفه سنگین مدتها پس از تثبیت صفحه اولیه تعامل داشته باشد. Performance Observer این رویدادهای زمان اجرا را ثبت میکند و به شما تصویر کاملی از کل جلسه کاربر میدهد.
- آیندهنگر و استاندارد شده: این API استاندارد توصیه شده W3C برای جمعآوری دادههای عملکرد است. معیارهای عملکرد و APIهای جدید طوری طراحی شدهاند که با آن ادغام شوند، و این آن را به یک انتخاب پایدار و آیندهنگر برای پروژههای شما تبدیل میکند.
- بنیاد نظارت بر کاربر واقعی (RUM): برای درک واقعی اینکه سایت شما برای کاربران در کشورها، دستگاهها و شرایط شبکه مختلف چگونه عمل میکند، به دادههای جلسات واقعی نیاز دارید. Performance Observer ابزاری ایدهآل برای ساختن یک راهحل RUM قوی است که به شما امکان میدهد معیارهای حیاتی را جمعآوری کرده و آنها را برای تجمیع و تحلیل به یک سرویس تحلیلی ارسال کنید.
- حذف شرایط رقابتی (Race Conditions): با نظرسنجی، ممکن است سعی کنید به یک ورودی عملکرد قبل از اینکه ثبت شود دسترسی پیدا کنید. مدل مشاهدهگر این شرایط رقابتی را به طور کامل حذف میکند، زیرا کد شما فقط پس از در دسترس بودن ورودی اجرا میشود.
شروع کار: مبانی Performance Observer
استفاده از این API ساده است. فرآیند شامل سه مرحله اصلی است: ایجاد یک مشاهدهگر، تعریف یک تابع بازگشتی، و گفتن به مشاهدهگر که چه چیزی را تماشا کند.
۱. ایجاد یک Observer با یک Callback
ابتدا، شما یک شیء PerformanceObserver را نمونهسازی میکنید و یک تابع بازگشتی به آن میدهید. این تابع هر زمان که ورودیهای جدید شناسایی شوند، اجرا خواهد شد.
const observer = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntries()) { console.log('Entry Type:', entry.entryType); console.log('Entry Name:', entry.name); console.log('Start Time:', entry.startTime); console.log('Duration:', entry.duration); } });
تابع بازگشتی یک شیء PerformanceObserverEntryList دریافت میکند. شما میتوانید متد getEntries() را روی این لیست فراخوانی کنید تا آرایهای از تمام ورودیهای عملکردی که به تازگی مشاهده شدهاند را دریافت کنید.
۲. مشاهده انواع ورودیهای خاص (Entry Types)
یک مشاهدهگر تا زمانی که به آن نگویید چه چیزی را نظارت کند، هیچ کاری انجام نمیدهد. شما این کار را با استفاده از متد .observe() انجام میدهید. این متد یک شیء با یک ویژگی entryTypes (یا در برخی موارد مدرن، فقط type برای یک نوع واحد) میگیرد که آرایهای از رشتهها است که انواع ورودیهای عملکردی که به آنها علاقهمند هستید را نشان میدهد.
// شروع مشاهده دو نوع ورودی observer.observe({ entryTypes: ['mark', 'measure'] });
برخی از رایجترین انواع ورودی عبارتند از:
- 'resource': جزئیات مربوط به درخواستهای شبکه برای داراییهایی مانند اسکریپتها، تصاویر و شیوهنامهها.
- 'paint': زمانبندی برای first-paint و first-contentful-paint.
- 'largest-contentful-paint': معیار Core Web Vital برای سرعت بارگذاری درک شده.
- 'layout-shift': معیار Core Web Vital برای ثبات بصری.
- 'first-input': اطلاعات مربوط به اولین تعامل کاربر، که برای First Input Delay Core Web Vital استفاده میشود.
- 'longtask': وظایفی را در رشته اصلی که بیش از ۵۰ میلیثانیه طول میکشند شناسایی میکند، که میتواند باعث عدم پاسخگویی شود.
- 'mark' & 'measure': نشانگرها و اندازهگیریهای سفارشی که شما در کد خود با استفاده از User Timing API تعریف میکنید.
۳. متوقف کردن Observer
هنگامی که دیگر نیازی به جمعآوری داده ندارید، بهتر است برای آزاد کردن منابع، مشاهدهگر را قطع کنید.
observer.disconnect();
کاربردهای عملی: نظارت بر Core Web Vitals
Core Web Vitals مجموعهای از عوامل خاص هستند که گوگل آنها را در تجربه کاربری کلی یک صفحه وب مهم میداند. نظارت بر آنها یکی از قدرتمندترین کاربردهای Performance Observer API است. بیایید ببینیم چگونه هر یک را اندازهگیری کنیم.
نظارت بر Largest Contentful Paint (LCP)
LCP عملکرد بارگذاری را اندازهگیری میکند. این معیار نقطهای را در خط زمانی بارگذاری صفحه مشخص میکند که محتوای اصلی احتمالاً بارگذاری شده است. نمره LCP خوب ۲.۵ ثانیه یا کمتر است.
عنصر LCP میتواند با بارگذاری صفحه تغییر کند. در ابتدا، یک عنوان ممکن است عنصر LCP باشد، اما بعداً، یک تصویر بزرگتر ممکن است بارگذاری شود و به عنصر LCP جدید تبدیل شود. به همین دلیل است که Performance Observer عالی است - شما را از هر کاندیدای بالقوه LCP به محض رندر شدن مطلع میکند.
// مشاهده LCP و ثبت مقدار نهایی let lcpValue = 0; const lcpObserver = new PerformanceObserver((entryList) => { const entries = entryList.getEntries(); // آخرین ورودی بهروزترین کاندیدای LCP است const lastEntry = entries[entries.length - 1]; lcpValue = lastEntry.startTime; console.log(`LCP updated: ${lcpValue.toFixed(2)}ms`, lastEntry.element); }); lcpObserver.observe({ type: 'largest-contentful-paint', buffered: true }); // بهتر است پس از تعامل کاربر، مشاهدهگر را قطع کنید، // زیرا تعاملات میتوانند مانع از ارسال کاندیداهای جدید LCP شوند. // window.addEventListener('beforeunload', () => lcpObserver.disconnect());
به استفاده از buffered: true توجه کنید. این یک گزینه حیاتی است که به مشاهدهگر دستور میدهد ورودیهایی را که *قبل* از فراخوانی متد observe() ثبت شدهاند، شامل شود. این کار از از دست دادن یک رویداد LCP زودهنگام جلوگیری میکند.
نظارت بر First Input Delay (FID) و Interaction to Next Paint (INP)
این معیارها تعاملپذیری را اندازهگیری میکنند. آنها تجربه کاربر را هنگام تلاش برای اولین تعامل با صفحه، کمیسازی میکنند.
First Input Delay (FID) زمان از لحظهای که کاربر برای اولین بار با یک صفحه تعامل میکند (مثلاً روی یک دکمه کلیک میکند) تا زمانی که مرورگر واقعاً قادر به شروع پردازش کنترلکنندههای رویداد (event handlers) در پاسخ به آن تعامل است را اندازهگیری میکند. FID خوب ۱۰۰ میلیثانیه یا کمتر است.
Interaction to Next Paint (INP) یک معیار جدیدتر و جامعتر است که در مارس ۲۰۲۴ جایگزین FID به عنوان یک Core Web Vital شد. در حالی که FID فقط *تأخیر* *اولین* تعامل را اندازهگیری میکند، INP *کل تأخیر* *تمام* تعاملات کاربر در طول چرخه حیات صفحه را ارزیابی کرده و بدترین آنها را گزارش میدهد. این به تصویر بهتری از پاسخگویی کلی میدهد. INP خوب ۲۰۰ میلیثانیه یا کمتر است.
شما میتوانید FID را با استفاده از نوع ورودی 'first-input' نظارت کنید:
// مشاهده FID const fidObserver = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntries()) { const fid = entry.processingStart - entry.startTime; console.log(`FID: ${fid.toFixed(2)}ms`); // پس از گزارش اولین ورودی، قطع کنید fidObserver.disconnect(); } }); fidObserver.observe({ type: 'first-input', buffered: true });
نظارت بر INP کمی پیچیدهتر است زیرا به کل مدت زمان یک رویداد نگاه میکند. شما نوع ورودی 'event' را مشاهده کرده و مدت زمان را محاسبه میکنید و طولانیترین آن را پیگیری میکنید.
// مثال ساده شده نظارت بر INP let worstInp = 0; const inpObserver = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntries()) { // INP مدت زمان رویداد است const inp = entry.duration; // ما فقط به تعاملاتی که طولانیتر از بدترین حالت فعلی هستند اهمیت میدهیم if (inp > worstInp) { worstInp = inp; console.log(`New worst INP: ${worstInp.toFixed(2)}ms`); } } }); inpObserver.observe({ type: 'event', durationThreshold: 16, buffered: true }); // durationThreshold به فیلتر کردن رویدادهای بسیار کوتاه و احتمالاً بیاهمیت کمک میکند.
نظارت بر Cumulative Layout Shift (CLS)
CLS ثبات بصری را اندازهگیری میکند. این معیار به کمیسازی تعداد دفعاتی که کاربران تغییرات چیدمان غیرمنتظره را تجربه میکنند کمک میکند - تجربهای ناامیدکننده که در آن محتوا بدون هشدار در صفحه جابجا میشود. نمره CLS خوب ۰.۱ یا کمتر است.
این نمره تجمعی از تمام نمرات تغییرات چیدمان فردی است. Performance Observer در اینجا ضروری است، زیرا هر تغییر را به محض وقوع گزارش میدهد.
// مشاهده و محاسبه نمره کل CLS let clsScore = 0; const clsObserver = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntries()) { // ما نمیخواهیم تغییراتی را که ناشی از ورودی کاربر بوده است، بشماریم if (!entry.hadRecentInput) { clsScore += entry.value; console.log(`Current CLS score: ${clsScore.toFixed(4)}`); } } }); clsObserver.observe({ type: 'layout-shift', buffered: true });
ویژگی hadRecentInput مهم است. این به شما کمک میکند تا تغییرات چیدمان مشروعی را که در پاسخ به یک اقدام کاربر رخ میدهند (مانند کلیک کردن روی دکمهای که یک منو را باز میکند) فیلتر کنید، که نباید در نمره CLS محاسبه شوند.
فراتر از Core Web Vitals: دیگر Entry Typeهای قدرتمند
در حالی که Core Web Vitals نقطه شروع خوبی هستند، Performance Observer میتواند خیلی بیشتر از اینها را نظارت کند. در اینجا چند نوع ورودی فوقالعاده مفید دیگر آورده شده است.
ردیابی وظایف طولانی (`longtask`)
Long Tasks API وظایفی را که رشته اصلی را برای ۵۰ میلیثانیه یا بیشتر اشغال میکنند، افشا میکند. اینها مشکلساز هستند زیرا در حالی که رشته اصلی مشغول است، صفحه نمیتواند به ورودی کاربر پاسخ دهد، که منجر به تجربهای کند یا منجمد میشود. شناسایی این وظایف کلید بهبود INP است.
// مشاهده وظایف طولانی const longTaskObserver = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntries()) { console.log(`Long Task Detected: ${entry.duration.toFixed(2)}ms`); // ویژگی 'attribution' گاهی اوقات میتواند به شما بگوید چه چیزی باعث وظیفه طولانی شده است console.log('Attribution:', entry.attribution); } }); longTaskObserver.observe({ type: 'longtask', buffered: true });
تحلیل زمانبندی منابع (`resource`)
درک چگونگی بارگذاری داراییهای شما برای بهینهسازی عملکرد اساسی است. نوع ورودی 'resource' دادههای زمانبندی دقیق شبکه را برای هر منبع در صفحه شما، از جمله زمان جستجوی DNS، اتصال TCP و زمان دانلود محتوا، به شما میدهد.
// مشاهده زمانبندی منابع const resourceObserver = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntries()) { // بیایید تصاویر با بارگذاری کند را پیدا کنیم if (entry.initiatorType === 'img' && entry.duration > 500) { console.warn(`Slow image detected: ${entry.name}`, `Duration: ${entry.duration.toFixed(2)}ms`); } } }); // استفاده از 'buffered: true' تقریباً همیشه برای زمانبندی منابع ضروری است // تا داراییهایی را که قبل از اجرای این اسکریپت بارگذاری شدهاند، دریافت کند. resourceObserver.observe({ type: 'resource', buffered: true });
اندازهگیری نشانگرهای عملکرد سفارشی (`mark` و `measure`)
گاهی اوقات، شما نیاز به اندازهگیری عملکرد منطق خاص برنامه خود دارید. User Timing API به شما امکان میدهد تا مهرهای زمانی (timestamps) سفارشی ایجاد کرده و مدت زمان بین آنها را اندازهگیری کنید.
- performance.mark('start-operation'): یک مهر زمانی به نام 'start-operation' ایجاد میکند.
- performance.mark('end-operation'): یک مهر زمانی دیگر ایجاد میکند.
- performance.measure('my-operation', 'start-operation', 'end-operation'): یک اندازهگیری بین دو نشانگر ایجاد میکند.
Performance Observer میتواند به این ورودیهای سفارشی 'mark' و 'measure' گوش دهد، که برای جمعآوری دادههای زمانبندی در مورد مواردی مانند زمان رندر مؤلفهها در یک فریمورک جاوااسکریپت یا مدت زمان یک فراخوانی API حیاتی و پردازش دادههای بعدی، عالی است.
// در کد برنامه شما: performance.mark('start-data-processing'); // ... پردازش دادههای پیچیده ... performance.mark('end-data-processing'); performance.measure('data-processing-duration', 'start-data-processing', 'end-data-processing'); // در اسکریپت نظارت شما: const customObserver = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntriesByName('data-processing-duration')) { console.log(`Custom Measurement '${entry.name}': ${entry.duration.toFixed(2)}ms`); } }); customObserver.observe({ entryTypes: ['measure'] });
مفاهیم پیشرفته و بهترین شیوهها
برای استفاده مؤثر از Performance Observer API در یک محیط تولید حرفهای، این بهترین شیوهها را در نظر بگیرید.
- همیشه `buffered: true` را در نظر بگیرید: برای انواع ورودی که میتوانند در اوایل بارگذاری صفحه رخ دهند (مانند 'resource', 'paint', یا 'largest-contentful-paint')، استفاده از پرچم buffered برای جلوگیری از از دست دادن آنها ضروری است.
- پشتیبانی مرورگر را بررسی کنید: در حالی که این API به طور گسترده در مرورگرهای مدرن پشتیبانی میشود، همیشه عاقلانه است که قبل از استفاده از آن، وجود آن را بررسی کنید. شما همچنین میتوانید بررسی کنید که کدام انواع ورودی توسط یک مرورگر خاص پشتیبانی میشوند.
- if ('PerformanceObserver' in window && PerformanceObserver.supportedEntryTypes.includes('longtask')) { // استفاده از PerformanceObserver برای وظایف طولانی امن است }
- ارسال دادهها به یک سرویس تحلیلی: ثبت دادهها در کنسول برای توسعه عالی است، اما برای نظارت در دنیای واقعی، شما نیاز به تجمیع این دادهها دارید. بهترین راه برای ارسال این تلهمتری از سمت کلاینت استفاده از navigator.sendBeacon() API است. این یک مکانیزم غیرمسدودکننده است که برای ارسال مقادیر کمی از داده به سرور طراحی شده است و حتی زمانی که یک صفحه در حال تخلیه شدن (unloaded) است، به طور قابل اعتمادی کار میکند.
- گروهبندی مشاهدهگرها بر اساس دغدغه: در حالی که میتوانید از یک مشاهدهگر واحد برای چندین نوع ورودی استفاده کنید، اغلب تمیزتر است که مشاهدهگرهای جداگانهای برای دغدغههای مختلف ایجاد کنید (مثلاً، یکی برای Core Web Vitals، یکی برای زمانبندی منابع، یکی برای معیارهای سفارشی). این کار خوانایی و قابلیت نگهداری کد را بهبود میبخشد.
- درک سربار عملکرد: این API طوری طراحی شده است که سربار بسیار پایینی داشته باشد. با این حال، یک تابع بازگشتی بسیار پیچیده که محاسبات سنگین انجام میدهد، به طور بالقوه میتواند بر عملکرد تأثیر بگذارد. توابع بازگشتی مشاهدهگر خود را سبک و کارآمد نگه دارید. هرگونه پردازش سنگین را به یک web worker موکول کنید یا دادههای خام را برای پردازش به بکاند خود ارسال کنید.
نتیجهگیری: ساختن یک فرهنگ مبتنی بر عملکرد
Performance Observer API چیزی بیش از یک ابزار دیگر است؛ این یک تغییر اساسی در نحوه رویکرد ما به عملکرد وب است. این ما را از اندازهگیریهای واکنشی و یکباره به سمت نظارت پیشگیرانه و مستمر سوق میدهد که تجربه واقعی و پویای کاربران ما در سراسر جهان را منعکس میکند. با ارائه راهی قابل اعتماد و کارآمد برای ثبت Core Web Vitals، وظایف طولانی، زمانبندی منابع و معیارهای سفارشی، این API به توسعهدهندگان قدرت میدهد تا گلوگاههای عملکرد را قبل از اینکه بر تعداد قابل توجهی از کاربران تأثیر بگذارند، شناسایی و حل کنند.
پذیرش Performance Observer API یک گام حیاتی به سوی ساختن یک فرهنگ مبتنی بر عملکرد در هر تیم توسعه است. وقتی میتوانید آنچه را که مهم است اندازهگیری کنید، میتوانید آنچه را که مهم است بهبود بخشید. از همین امروز شروع به ادغام این مشاهدهگرها در پروژههای خود کنید. کاربران شما - در هر کجای دنیا که باشند - از شما برای تجربه سریعتر، روانتر و لذتبخشتر سپاسگزار خواهند بود.