ইন্ট্যার‍্যাকশন এবং সফট-নেভিগেশন পেইন্ট টাইমিং পরিমাপ করুন

স্ট্যান্ডার্ড পেইন্ট এবং রেসপন্সিভনেস মেট্রিক প্রাথমিক পৃষ্ঠা লোড হওয়ার পরে পরিমাপের ক্ষেত্রে একটি ফাঁক রেখে যায়:

  • LargestContentfulPaint (largest-contentful-paint) শুধুমাত্র প্রাথমিক ("হার্ড") ডকুমেন্ট লোড পরিমাপ করে এবং ব্যবহারকারী পৃষ্ঠার সাথে প্রথম ইন্টার‍্যাক্ট করার সাথে সাথেই এন্ট্রি নির্গমন বন্ধ করে দেয়।
  • PerformanceEventTiming এবং event / Interaction to Next Paint (INP) ঠিক পরবর্তী ফ্রেম (যেমন, বোতামের অ্যাক্টিভ স্টেট বা লোডিং স্পিনার) পর্যন্ত ইনপুট রেসপন্সিভনেস পরিমাপ করে, কিন্তু পরবর্তী অ্যাসিঙ্ক্রোনাস পেইন্ট ট্র্যাক করে না—যেমন, fetch() সমাধান হওয়ার পরে নতুন কন্টেন্ট রেন্ডার করা বা ক্লায়েন্ট-সাইড রুট ট্রানজিশন সম্পূর্ণ হওয়া।
  • ফ্রেমওয়ার্ক কোডের বাইরে সিঙ্গেল পেজ অ্যাপে (SPA) রুট পরিবর্তন লক্ষ্য করা কঠিন হতে পারে। শুধু URL আপডেট মনিটর করলে, URL-এ হওয়া পরিবর্তন সম্পর্কে অতিরিক্ত রিপোর্ট করা হতে পারে, যা রুট পরিবর্তনের সাথে লিঙ্ক করা থাকে না (যেমন, অ্যাঙ্কর লিঙ্কে পরিবর্তন, একটি রুট পরিবর্তনের জন্য একাধিক "রিডাইরেক্ট")।

InteractionContentfulPaint (interaction-contentful-paint) ও PerformanceSoftNavigation (soft-navigation) পারফর্ম্যান্স এন্ট্রি টাইপ এই ফাঁক পূরণ করে। এগুলি অ্যাসিঙ্ক্রোনাস টাস্ক (fetch, await, setTimeout) জুড়ে ইন্টার‍্যাকশন কজালিটি প্রচার করে যাতে নতুন কন্টেন্টফুল পেইন্ট এবং সিঙ্গেল-পেজ অ্যাপ্লিকেশন (SPA) রুট ট্রানজিশন সরাসরি ব্যবহারকারীর ইন্টার‍্যাকশনে ফিরে আসে যা তাদের ট্রিগার করে।

কীভাবে প্রয়োগ করতে হয়

১. ইন্টার‍্যাকশনের মাধ্যমে ট্রিগার হওয়া কন্টেন্টফুল পেইন্ট লক্ষ্য করুন

ব্যবহারকারীর যেকোনও ইন্টার‍্যাকশনের জন্য দায়ী, ক্রমশ বড় হতে থাকা কন্টেন্টফুল পেইন্ট পর্যবেক্ষণ করতে, type: 'interaction-contentful-paint' ও buffered: true-এর সাথে PerformanceObserver ব্যবহার করুন।

প্রতিটি InteractionContentfulPaint এন্ট্রি এগুলি প্রদান করে:

  • interactionId: শুরু করা ব্যবহারকারীর ইন্টার‌্যাকশনের অনন্য শনাক্তকারী (PerformanceEventTiming.interactionId এবং PerformanceSoftNavigation.interactionId-এর সাথে ম্যাচ করে)।
  • startTime: প্রাথমিক হার্ড নেভিগেশন টাইম অরিজিনের সাপেক্ষে, ব্যবহারকারীর ইন্টার‍্যাকশন শুরু হওয়ার টাইমস্ট্যাম্প।
  • paintTime এবং presentationTime: রেন্ডারিং ফেজ কখন শেষ হয়েছে এবং স্ক্রিনে কখন পিক্সেল আঁকা হয়েছে।
  • duration: ইন্টার‍্যাকশন শুরু হওয়া থেকে পেইন্ট প্রেজেন্টেশন পর্যন্ত অতিবাহিত সময় (presentationTime - startTime)।
  • largestContentfulPaint: একটি স্ন্যাপশট LargestContentfulPaint অবজেক্ট যাতে এখনও পর্যন্ত সেই ইন্টার‍্যাকশনের জন্য সবচেয়ে বড় পেইন্ট করা এলিমেন্ট সম্পর্কে বিবরণ (element, size, url, id, renderTime, loadTime) থাকে।
/**
 * Observes contentful paints caused by user interactions and invokes a callback
 * whenever an interaction produces a larger contentful paint.
 *
 * @param {(entry: PerformanceEntry) => void} onInteractionPaint
 */
export function observeInteractionPaints(onInteractionPaint) {
  // Feature-detect support before calling observe() so unsupported browsers
  // do not throw or log console warnings.
  if (
    typeof PerformanceObserver === 'undefined' ||
    !PerformanceObserver.supportedEntryTypes?.includes('interaction-contentful-paint')
  ) {
    return null;
  }

  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // entry.duration is already calculated as (entry.presentationTime - entry.startTime),
      // giving the total time from the user interaction to this contentful paint.
      onInteractionPaint(entry);
    }
  });

  // MANDATORY: Pass buffered: true to capture interaction paints that occurred
  // before this observer was registered.
  observer.observe({ type: 'interaction-contentful-paint', buffered: true });
  return observer;
}

২. SPA সফট নেভিগেশনের জন্য Largest Contentful Paint (LCP) পরিমাপ করা

কোনও বিশ্বস্ত ব্যবহারকারীর ইন্টার‍্যাকশনের ফলে দৃশ্যমান URL পরিবর্তন এবং কন্টেন্টফুল পেইন্ট, দুটিই হলে ব্রাউজার একটি সফট নেভিগেশন নির্গত করে। প্রতিটি সফট নেভিগেশনের জন্য LCP গণনা করতে:

  1. buffered: true-এর সাথে 'soft-navigation' এবং 'interaction-contentful-paint' এন্ট্রি দুটিই লক্ষ্য করুন।
  2. বাধ্যতামূলক: 'soft-navigation' এন্ট্রি পাওয়া গেলে, softNavEntry.getLargestInteractionContentfulPaint() কল করে এর প্রাথমিক LCP ক্যান্ডিডেট সিড করুন। URL আপডেট করার আগে অ্যাপ্লিকেশন কন্টেন্ট পেইন্ট করলে (যেমন, ভিউ রেন্ডার করা এবং তারপর history.pushState() কল করা), সেইসব প্রাথমিক interaction-contentful-paint এন্ট্রি soft-navigation এন্ট্রির আগে নির্গত হয় এবং এখনও আগের navigationId বহন করে।
  3. বাধ্যতামূলক: entry.interactionId === softNavEntry.interactionId-এর সাথে ম্যাচ করে অ্যাক্টিভ সফট নেভিগেশনে পরবর্তী 'interaction-contentful-paint' এন্ট্রি কোরিলেট করুন, entry.navigationId-এর সাথে ম্যাচ করবেন না। interactionId ব্যবহার করলে, URL আপডেট হওয়ার আগে হওয়া পেইন্ট অন্তর্ভুক্ত করা এবং নতুন রুটে পরে হওয়া, সম্পর্কহীন ইন্টার‍্যাকশন থেকে পেইন্ট বাদ দেওয়ার বিষয়টি নিশ্চিত করা যায়।
/**
 * Tracks FCP and LCP for each client-side soft navigation and reports finalized
 * route metrics when the user navigates to a new route or hides the page.
 *
 * @param {(report: {
 *   url: string,
 *   navigationId: number,
 *   interactionId: number,
 *   fcpDuration: number,
 *   lcpDuration: number | null,
 *   lcpElement: Element | null,
 *   lcpSize: number | null
 * }) => void} onSoftNavReport
 */
export function trackSoftNavigationLCP(onSoftNavReport) {
  const supported =
    typeof PerformanceObserver !== 'undefined' &&
    PerformanceObserver.supportedEntryTypes?.includes('soft-navigation') &&
    PerformanceObserver.supportedEntryTypes?.includes('interaction-contentful-paint');

  if (!supported) {
    return null;
  }

  let activeSoftNav = null;
  let activeLcpEntry = null;

  function finalizeCurrentSoftNav() {
    if (!activeSoftNav) return;

    onSoftNavReport({
      // activeSoftNav.name holds the destination URL of the soft navigation.
      url: activeSoftNav.name,
      navigationId: activeSoftNav.navigationId,
      interactionId: activeSoftNav.interactionId,
      // activeSoftNav.duration is (activeSoftNav.presentationTime - activeSoftNav.startTime),
      // which represents First Contentful Paint (FCP) relative to interaction start.
      fcpDuration: activeSoftNav.duration,
      // activeLcpEntry.duration is (activeLcpEntry.presentationTime - activeSoftNav.startTime),
      // representing the soft navigation's LCP duration relative to interaction start.
      lcpDuration: activeLcpEntry ? activeLcpEntry.duration : null,
      lcpElement: activeLcpEntry?.largestContentfulPaint?.element ?? null,
      lcpSize: activeLcpEntry?.largestContentfulPaint?.size ?? null,
    });

    activeSoftNav = null;
    activeLcpEntry = null;
  }

  const softNavObserver = new PerformanceObserver((list) => {
    for (const softNavEntry of list.getEntries()) {
      // Finalize the previous route's metrics before resetting state for the new route.
      finalizeCurrentSoftNav();

      activeSoftNav = softNavEntry;
      // MANDATORY: Seed the LCP candidate with getLargestInteractionContentfulPaint()
      // to capture any attributed contentful paints that occurred before the URL updated.
      activeLcpEntry = softNavEntry.getLargestInteractionContentfulPaint();
    }
  });

  const icpObserver = new PerformanceObserver((list) => {
    for (const icpEntry of list.getEntries()) {
      // MANDATORY: Match by interactionId (not navigationId) so only paints caused
      // by the soft navigation's initiating interaction update the route's LCP.
      if (activeSoftNav && icpEntry.interactionId === activeSoftNav.interactionId) {
        activeLcpEntry = icpEntry;
      }
    }
  });

  softNavObserver.observe({ type: 'soft-navigation', buffered: true });
  icpObserver.observe({ type: 'interaction-contentful-paint', buffered: true });

  // Finalize the active route's LCP when the page is backgrounded or unloaded.
  document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'hidden') {
      finalizeCurrentSoftNav();
    }
  });

  return { softNavObserver, icpObserver, finalizeCurrentSoftNav };
}

৩. web-vitals-এর মাধ্যমে সফ্ট নেভিগেশন জুড়ে Core Web Vitals পরিমাপ করুন

আপনার লক্ষ্য যদি হার্ড ও সফ্ট নেভিগেশন জুড়ে স্ট্যান্ডার্ড Core Web Vitals (LCP, CLS ও INP) অ্যানালিটিক্স এন্ডপয়েন্টে রিপোর্ট করা হয়, তাহলে প্রতিটি মেট্রিক টাইমলাইন ম্যানুয়ালি স্লাইস করার পরিবর্তে { reportSoftNavs: true } সহ web-vitals লাইব্রেরি (v6.0.0+) ব্যবহার করুন।

import { onLCP, onFCP, onCLS, onINP } from 'web-vitals';

function sendToAnalytics(metric) {
  // Example-only endpoint; replace with your real telemetry URL.
  navigator.sendBeacon(
    '/analytics',
    JSON.stringify({
      name: metric.name,
      value: metric.value,
      navigationType: metric.navigationType,
      navigationURL: metric.navigationURL,
    }),
  );
}

// Pass { reportSoftNavs: true } to report metrics per soft navigation in addition
// to the initial hard navigation.
onLCP(sendToAnalytics, { reportSoftNavs: true });
onFCP(sendToAnalytics, { reportSoftNavs: true });
onCLS(sendToAnalytics, { reportSoftNavs: true });
onINP(sendToAnalytics, { reportSoftNavs: true });

পেশাদার পদ্ধতি

  • observer.observe() কল করার আগে PerformanceObserver.supportedEntryTypes?.includes(...) ব্যবহার করে 'interaction-contentful-paint' এবং 'soft-navigation' ফিচার-ডিটেক্ট করুন।
  • 'interaction-contentful-paint' এবং 'soft-navigation' পর্যবেক্ষণ করার সময় { buffered: true } পাস করুন যাতে অ্যানালিটিক্স কোড অ্যাসিঙ্ক্রোনাসভাবে শুরু হলে প্রাথমিক এন্ট্রিগুলি মিস না হয়।
  • নতুন soft-navigation এন্ট্রি ম্যানেজ করার সময় softNavEntry.getLargestInteractionContentfulPaint() কল করুন যাতে URL আপডেট করার আগে হওয়া পেইন্ট LCP-তে অন্তর্ভুক্ত করা হয়।
  • DO match interaction-contentful-paint entries to a soft navigation using interactionId, not navigationId.
  • entry.presentationTime সরাসরি রিপোর্ট করার পরিবর্তে, ইন্টার‍্যাকশন পেইন্ট লেটেন্সি পরিমাপ করতে entry.duration (বা entry.presentationTime - entry.startTime) ব্যবহার করুন। সব PerformanceEntry টাইমস্ট্যাম্প প্রাথমিক হার্ড পৃষ্ঠা লোড হওয়ার সময় থেকে পরিমাপ করা হয়, সফ্ট নেভিগেশন শুরু হওয়ার সময় থেকে নয়।
  • দীর্ঘ সময় ধরে চলা সিঙ্গল-পেজ অ্যাপ্লিকেশন মনিটর করার জন্য performance.getEntriesByType('soft-navigation')-এর উপর নির্ভর করবেন না, কারণ ব্রাউজারের বাফার প্রথম ৫০টি সফট নেভিগেশন এন্ট্রির মধ্যে সীমিত থাকে।

ফলব্যাক স্ট্র্যাটেজি

আপনার বেসলাইন টার্গেটে যদি এমন ব্রাউজার অন্তর্ভুক্ত থাকে যা InteractionContentfulPaint বা PerformanceSoftNavigation-কে সাপোর্ট করে না, তাহলে এই API-গুলিকে রিয়েল ইউজার মনিটরিং (RUM)-এর জন্য প্রোগ্রেসিভ এনহ্যান্সমেন্ট হিসেবে বিবেচনা করুন:

  1. ফিচার শনাক্তকরণ ও গ্রেসফুল ডিগ্রেডেশন: PerformanceObserver.supportedEntryTypes?.includes('interaction-contentful-paint') ও PerformanceObserver.supportedEntryTypes?.includes('soft-navigation')-এর মাধ্যমে গার্ড অবজারভার রেজিস্ট্রেশন। যেসব ব্রাউজারে এই সুবিধা নেই, সেগুলিতে অবজারভার রেজিস্টার করার ধাপটি এড়িয়ে যান যাতে আপনার অ্যাপ্লিকেশন কোনও সমস্যা ছাড়াই রান করতে পারে।
  2. স্ট্যান্ডার্ড হার্ড-নেভিগেশন অবজারভার অ্যাক্টিভ রাখুন: সব ব্রাউজার জুড়ে স্ট্যান্ডার্ড 'largest-contentful-paint', 'event' এবং 'layout-shift' এন্ট্রি পর্যবেক্ষণ করা চালিয়ে যান। যেহেতু LargestContentfulPaint সফট-নেভিগেশন সাপোর্ট নির্বিশেষে প্রথম ব্যবহারকারীর ইন্টার‍্যাকশনে চূড়ান্ত করা হয়, তাই ব্রাউজার সাপোর্টে সফট-নেভিগেশন পরিমাপ করা প্রাথমিক হার্ড-লোড LCP পরিমাপকে পরিবর্তন বা ব্রেক করে না।