১. ভূমিকা
এটি একটি ইন্টারেক্টিভ কোডল্যাব, যেখানে web-vitals লাইব্রেরি ব্যবহার করে ইন্টারঅ্যাকশন টু নেক্সট পেইন্ট (INP) পরিমাপ করার পদ্ধতি শেখানো হয়েছে।
পূর্বশর্ত
- এইচটিএমএল এবং জাভাস্ক্রিপ্ট ডেভেলপমেন্টের জ্ঞান।
- সুপারিশ করা হচ্ছে: web.dev INP মেট্রিক ডকুমেন্টেশনটি পড়ুন।
আপনি যা শিখবেন
- আপনার পেজে কীভাবে
web-vitalsলাইব্রেরি যুক্ত করবেন এবং এর অ্যাট্রিবিউশন ডেটা ব্যবহার করবেন। - INP-এর উন্নতি কোথায় এবং কীভাবে শুরু করতে হবে তা নির্ণয় করতে অ্যাট্রিবিউশন ডেটা ব্যবহার করুন।
আপনার যা যা প্রয়োজন হবে
- এমন একটি কম্পিউটার যা গিটহাব থেকে কোড ক্লোন করতে এবং এনপিএম কমান্ড চালাতে সক্ষম।
- একটি টেক্সট এডিটর।
- সমস্ত ইন্টারঅ্যাকশন পরিমাপ সঠিকভাবে কাজ করার জন্য ক্রোমের একটি সাম্প্রতিক সংস্করণ প্রয়োজন।
২. প্রস্তুত হন
কোডটি নিন এবং চালান
কোডটি web-vitals-codelabs রিপোজিটরিতে পাওয়া যায়।
- আপনার টার্মিনালে রিপোটি ক্লোন করুন:
git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git. - ক্লোন করা ডিরেক্টরিতে প্রবেশ করুন:
cd web-vitals-codelabs/measuring-inp. - নির্ভরতা ইনস্টল করুন:
npm ci. - ওয়েব সার্ভার চালু করুন:
npm run start. - আপনার ব্রাউজারে http://localhost:8080/ ভিজিট করুন।
পৃষ্ঠাটি ব্যবহার করে দেখুন
এই কোডল্যাবটি INP-এর সম্ভাব্য সমস্যাগুলো অন্বেষণ করতে গ্যাস্ট্রোপডিকন (শামুকের শারীরস্থান বিষয়ক একটি জনপ্রিয় তথ্যসূত্র সাইট) ব্যবহার করে।

কোন ইন্টারঅ্যাকশনগুলো ধীরগতির, তা বোঝার জন্য পেজটির সাথে ইন্টারঅ্যাক্ট করার চেষ্টা করুন।
৩. ক্রোম ডেভটুলস-এ নিজেকে প্রস্তুত করা
More Tools > Developer Tools মেনু থেকে, পেজটিতে রাইট-ক্লিক করে Inspect নির্বাচন করে , অথবা কিবোর্ড শর্টকাট ব্যবহার করে DevTools খুলুন ।
এই কোডল্যাবে আমরা পারফরম্যান্স প্যানেল এবং কনসোল উভয়ই ব্যবহার করব। আপনি ডেভটুলস-এর উপরের ট্যাবগুলো থেকে যেকোনো সময় এগুলোর মধ্যে পরিবর্তন করতে পারবেন।
- INP সমস্যাগুলো বেশিরভাগ ক্ষেত্রে মোবাইল ডিভাইসে ঘটে, তাই মোবাইল ডিসপ্লে এমুলেশন ব্যবহার করুন ।
- আপনি যদি ডেস্কটপ বা ল্যাপটপে পরীক্ষা করেন, তাহলে পারফরম্যান্স সম্ভবত একটি আসল মোবাইল ডিভাইসের চেয়ে উল্লেখযোগ্যভাবে ভালো হবে। পারফরম্যান্সের আরও বাস্তবসম্মত চিত্র পেতে, পারফরম্যান্স প্যানেলের উপরের ডানদিকে থাকা গিয়ার আইকনে ক্লিক করুন, তারপর ‘CPU 4x slowdown’ নির্বাচন করুন।

৪. ওয়েব-ভাইটালস ইনস্টল করা
web-vitals হলো একটি জাভাস্ক্রিপ্ট লাইব্রেরি, যা আপনার ব্যবহারকারীদের অভিজ্ঞতাপ্রাপ্ত ওয়েব ভাইটালস মেট্রিকস পরিমাপ করে। আপনি এই লাইব্রেরিটি ব্যবহার করে সেই মানগুলো সংগ্রহ করতে পারেন এবং পরবর্তীতে বিশ্লেষণের জন্য সেগুলোকে একটি অ্যানালিটিক্স এন্ডপয়েন্টে পাঠাতে পারেন। এর মাধ্যমে আমরা কখন এবং কোথায় ধীরগতির ইন্টারঅ্যাকশন ঘটে, তা বের করতে পারি।
একটি পেজে লাইব্রেরিটি যুক্ত করার কয়েকটি ভিন্ন উপায় রয়েছে। আপনি আপনার নিজের সাইটে লাইব্রেরিটি কীভাবে ইনস্টল করবেন তা নির্ভর করবে আপনি কীভাবে ডিপেন্ডেন্সি পরিচালনা করেন, বিল্ড প্রসেস এবং অন্যান্য বিষয়ের উপর। আপনার সমস্ত বিকল্প জানতে লাইব্রেরির ডকুমেন্টেশন অবশ্যই দেখে নেবেন।
এই কোডল্যাবটি npm থেকে ইনস্টল হবে এবং কোনো নির্দিষ্ট বিল্ড প্রক্রিয়ায় না গিয়ে সরাসরি স্ক্রিপ্টটি লোড করবে।
web-vitals দুটি সংস্করণ রয়েছে যা আপনি ব্যবহার করতে পারেন:
- পেজ লোড হওয়ার সময় কোর ওয়েব ভাইটালস-এর মেট্রিক মানগুলো ট্র্যাক করতে চাইলে 'স্ট্যান্ডার্ড' বিল্ডটি ব্যবহার করা উচিত।
- 'অ্যাট্রিবিউশন' বিল্ড প্রতিটি মেট্রিকের সাথে অতিরিক্ত ডিবাগ তথ্য যোগ করে, যার মাধ্যমে নির্ণয় করা হয় যে একটি মেট্রিকের মান কেন এমন হয়।
এই কোডল্যাবে INP পরিমাপ করার জন্য, আমরা অ্যাট্রিবিউশন বিল্ডটি চাই।
npm install -D web-vitals -vitals কমান্ডটি চালিয়ে প্রোজেক্টের devDependencies এ web-vitals যোগ করুন।
পৃষ্ঠায় web-vitals যোগ করুন:
স্ক্রিপ্টটির অ্যাট্রিবিউশন সংস্করণটি index.html এর শেষে যোগ করুন এবং ফলাফলগুলো কনসোলে লগ করুন:
<script type="module">
import {onINP} from './node_modules/web-vitals/dist/web-vitals.attribution.js';
onINP(console.log);
</script>
চেষ্টা করে দেখুন
কনসোল খোলা রেখে আবার পেজটির সাথে ইন্টারঅ্যাক্ট করার চেষ্টা করুন। আপনি পেজটিতে ক্লিক করলেও কিছুই লগ হচ্ছে না!
একটি পেজের সম্পূর্ণ জীবনচক্র জুড়ে INP পরিমাপ করা হয়, এবং তাই ডিফল্টরূপে, ব্যবহারকারী পেজটি ছেড়ে না যাওয়া বা বন্ধ না করা পর্যন্ত web-vitals INP রিপোর্ট করে না। অ্যানালিটিক্সের মতো কাজের জন্য বীকনিংয়ের ক্ষেত্রে এটিই আদর্শ আচরণ, কিন্তু ইন্টারেক্টিভভাবে ডিবাগ করার জন্য এটি ততটা আদর্শ নয়।
web-vitals আরও বিশদ রিপোর্টিংয়ের জন্য reportAllChanges অপশনটি প্রদান করে। এটি সক্রিয় করা হলে, প্রতিটি ইন্টারঅ্যাকশনের রিপোর্ট করা হয় না, কিন্তু যখনই কোনো ইন্টারঅ্যাকশন তার পূর্ববর্তীটির চেয়ে ধীরগতির হয়, তখন সেটির রিপোর্ট করা হয়।
স্ক্রিপ্টে অপশনটি যোগ করে আবার পেজটি ব্যবহার করে দেখুন:
<script type="module">
import {onINP} from './node_modules/web-vitals/dist/web-vitals.attribution.js';
onINP(console.log, {reportAllChanges: true});
</script>
পৃষ্ঠাটি রিফ্রেশ করুন এবং এখন থেকে ইন্টারঅ্যাকশনগুলো কনসোলে রিপোর্ট করা হবে, এবং যখনই কোনো নতুন ধীরগতির ইন্টারঅ্যাকশন পাওয়া যাবে, তা আপডেট হবে। উদাহরণস্বরূপ, সার্চ বক্সে টাইপ করে দেখুন এবং তারপর ইনপুটটি মুছে ফেলুন।

৫. অ্যাট্রিবিউশন বলতে কী বোঝায়?
চলুন শুরু করা যাক সেই প্রথম ধাপটি দিয়ে, যা বেশিরভাগ ব্যবহারকারী পেজটির সাথে সম্মুখীন হন, অর্থাৎ কুকি সম্মতির ডায়ালগ বক্সটি।
অনেক পেজে এমন স্ক্রিপ্ট থাকে যেগুলোর জন্য ব্যবহারকারী কুকি গ্রহণ করার সাথে সাথে কুকিগুলোকে সিনক্রোনাসভাবে ট্রিগার করতে হয়, যার ফলে ক্লিকটি একটি ধীরগতির ইন্টারঅ্যাকশনে পরিণত হয়। এখানেও ঠিক তাই ঘটে।
(ডেমো) কুকি গ্রহণ করতে 'হ্যাঁ'-তে ক্লিক করুন, এবং এখন DevTools কনসোলে লগ হওয়া INP ডেটা দেখে নিন।

এই শীর্ষ-স্তরের তথ্য স্ট্যান্ডার্ড এবং অ্যাট্রিবিউশন ওয়েব-ভাইটালস উভয় বিল্ডেই উপলব্ধ:
{
name: 'INP',
value: 344,
rating: 'needs-improvement',
entries: [...],
id: 'v4-1715732159298-8028729544485',
navigationType: 'reload',
attribution: {...},
}
ব্যবহারকারীর ক্লিক করার মুহূর্ত থেকে পরবর্তী পেইন্ট পর্যন্ত সময়কাল ছিল ৩৪৪ মিলিসেকেন্ড—যা একটি "উন্নতির প্রয়োজন" INP । entries অ্যারেটিতে এই ইন্টারঅ্যাকশনের সাথে সম্পর্কিত সমস্ত PerformanceEntry ভ্যালু রয়েছে—এই ক্ষেত্রে, শুধুমাত্র একটি ক্লিক ইভেন্ট।
তবে, এই সময়ে কী ঘটছে তা জানতে, আমরা attribution প্রপার্টিটির প্রতিই সবচেয়ে বেশি আগ্রহী। অ্যাট্রিবিউশন ডেটা তৈরি করার জন্য, web-vitals খুঁজে বের করে কোন লং অ্যানিমেশন ফ্রেম (LoAF) ক্লিক ইভেন্টের সাথে ওভারল্যাপ করছে। এরপর LoAF-টি সেই ফ্রেমে কীভাবে সময় ব্যয় হয়েছে সে সম্পর্কে বিস্তারিত ডেটা সরবরাহ করতে পারে—যেমন কোন স্ক্রিপ্টগুলো চলেছে, requestAnimationFrame কলব্যাক, স্টাইল এবং লেআউটে কত সময় ব্যয় হয়েছে, ইত্যাদি।
আরও তথ্য দেখতে attribution প্রপার্টিটি প্রসারিত করুন। ডেটা আরও অনেক সমৃদ্ধ।
attribution: {
interactionTargetElement: Element,
interactionTarget: '#confirm',
interactionType: 'pointer',
inputDelay: 27,
processingDuration: 295.6,
presentationDelay: 21.4,
processedEventEntries: [...],
longAnimationFrameEntries: [...],
}
প্রথমে, কীসের সাথে মিথস্ক্রিয়া করা হয়েছিল সে সম্পর্কে তথ্য রয়েছে:
-
interactionTargetElement: যে এলিমেন্টটির সাথে ইন্টারঅ্যাক্ট করা হয়েছে তার একটি সক্রিয় রেফারেন্স (যদি এলিমেন্টটি DOM থেকে সরানো না হয়ে থাকে)। -
interactionTarget: পেজের মধ্যে এলিমেন্টটি খুঁজে বের করার জন্য একটি সিলেক্টর।
এরপর, সময়টিকে উচ্চ-স্তরে বিশদভাবে ব্যাখ্যা করা হয়েছে:
-
inputDelay): ব্যবহারকারী যখন ইন্টারঅ্যাকশন শুরু করেন (যেমন, মাউস ক্লিক করেন) এবং সেই ইন্টারঅ্যাকশনের জন্য ইভেন্ট লিসেনারটি চলতে শুরু করার মধ্যবর্তী সময়। এই ক্ষেত্রে, সিপিইউ থ্রটলিং চালু থাকা সত্ত্বেও ইনপুট ডিলে ছিল মাত্র প্রায় ২৭ মিলিসেকেন্ড। -
processingDuration: ইভেন্ট লিসেনারগুলোর সম্পূর্ণ হতে যে সময় লাগে। প্রায়শই, একটি পেজের একটিমাত্র ইভেন্টের জন্য একাধিক লিসেনার থাকে (উদাহরণস্বরূপ,pointerdown),pointerup), এবংclick))। যদি সবগুলো একই অ্যানিমেশন ফ্রেমে চলে, তবে সেগুলোকে এই সময়ের মধ্যে একত্রিত করা হয়। এই ক্ষেত্রে, প্রসেসিং ডিউরেশনে ২৯৫.৬ মিলিসেকেন্ড সময় লাগে—যা INP সময়ের সিংহভাগ। -
presentationDelay: ইভেন্ট লিসেনারগুলোর কাজ শেষ হওয়ার পর থেকে ব্রাউজারের পরবর্তী ফ্রেম আঁকা শেষ হওয়া পর্যন্ত সময়। এক্ষেত্রে, ২১.৪ মিলিসেকেন্ড।
এই INP পর্যায়গুলো কী অপ্টিমাইজ করা প্রয়োজন তা নির্ণয়ের জন্য একটি গুরুত্বপূর্ণ সংকেত হতে পারে। এই বিষয়ে আরও তথ্য ‘Optimize INP’ গাইডে রয়েছে ।
আরেকটু গভীরে গেলে দেখা যায়, টপ-লেভেল INP entries অ্যারেতে যেখানে একটিমাত্র ইভেন্ট রয়েছে, সেখানে processedEventEntries এ পাঁচটি ইভেন্ট আছে। পার্থক্যটা কী?
processedEventEntries: [
{
name: 'mouseover',
entryType: 'event',
startTime: 1801.6,
duration: 344,
processingStart: 1825.3,
processingEnd: 1825.3,
cancelable: true
},
{
name: 'mousedown',
entryType: 'event',
startTime: 1801.6,
duration: 344,
processingStart: 1825.3,
processingEnd: 1825.3,
cancelable: true
},
{name: 'mousedown', ...},
{name: 'mouseup', ...},
{name: 'click', ...},
],
সর্বোচ্চ স্তরের এন্ট্রিটি হলো INP ইভেন্ট, এক্ষেত্রে এটি একটি ক্লিক। processedEventEntries ' অ্যাট্রিবিউশনটি হলো সেই সমস্ত ইভেন্ট যা একই ফ্রেমে প্রসেস করা হয়েছিল। লক্ষ্য করুন যে, এতে শুধু ক্লিক ইভেন্টই নয়, mouseover এবং mousedown এর মতো অন্যান্য ইভেন্টও অন্তর্ভুক্ত রয়েছে। এই অন্যান্য ইভেন্টগুলো সম্পর্কে জানা অত্যন্ত গুরুত্বপূর্ণ হতে পারে, যদি সেগুলোও ধীরগতির হয়ে থাকে, কারণ সবগুলোই ধীর রেসপন্সিভনেসের জন্য দায়ী।
সবশেষে রয়েছে longAnimationFrameEntries অ্যারে। এটি একটিমাত্র এন্ট্রি হতে পারে, কিন্তু এমনও ক্ষেত্র রয়েছে যেখানে একটি ইন্টারঅ্যাকশন একাধিক ফ্রেমে বিস্তৃত হতে পারে। এখানে আমরা একটিমাত্র দীর্ঘ অ্যানিমেশন ফ্রেমের সবচেয়ে সরল ক্ষেত্রটি দেখতে পাচ্ছি।
longAnimationFrameEntries
LoAF এন্ট্রিটি সম্প্রসারণ করা হচ্ছে:
longAnimationFrameEntries: [{
name: 'long-animation-frame',
startTime: 1823,
duration: 319,
renderStart: 2139.5,
styleAndLayoutStart: 2139.7,
firstUIEventTimestamp: 1801.6,
blockingDuration: 268,
scripts: [{...}]
}],
এখানে বেশ কিছু দরকারি ভ্যালু আছে, যেমন স্টাইলিং-এ ব্যয়িত সময়ের পরিমাণ আলাদাভাবে দেখানো। ‘লং অ্যানিমেশন ফ্রেমস এপিআই’ আর্টিকেলটিতে এই প্রপার্টিগুলো নিয়ে আরও বিস্তারিত আলোচনা করা হয়েছে । এই মুহূর্তে আমরা মূলত scripts প্রপার্টিটি নিয়ে আগ্রহী, যেটিতে এমন সব এন্ট্রি রয়েছে যা দীর্ঘ সময় ধরে চলা ফ্রেমটির জন্য দায়ী স্ক্রিপ্টগুলোর বিবরণ দেয়:
scripts: [{
name: 'script',
invoker: 'BUTTON#confirm.onclick',
invokerType: 'event-listener',
startTime: 1828.6,
executionStart: 1828.6,
duration: 294,
sourceURL: 'http://localhost:8080/third-party/cmp.js',
sourceFunctionName: '',
sourceCharPosition: 1144
}]
এক্ষেত্রে, আমরা বলতে পারি যে সময়টা মূলত একটিমাত্র event-listener ব্যয় হয়েছে, যা BUTTON#confirm.onclick এ কল করা হয়েছিল। এমনকি আমরা স্ক্রিপ্ট সোর্স ইউআরএল এবং ফাংশনটি ঠিক কোন ক্যারেক্টারে সংজ্ঞায়িত করা হয়েছিল, সেটাও দেখতে পাচ্ছি!
টেকঅ্যাওয়ে
এই আরোপণ তথ্য থেকে এই মামলাটি সম্পর্কে কী নির্ধারণ করা যেতে পারে?
-
button#confirmএলিমেন্টে ক্লিক করার মাধ্যমে ইন্টারঅ্যাকশনটি ট্রিগার করা হয়েছিল (attribution.interactionTargetএবং একটি স্ক্রিপ্ট অ্যাট্রিবিউশন এন্ট্রিরinvokerপ্রপার্টি থেকে)। - সময় প্রধানত ইভেন্ট লিসেনারগুলো কার্যকর করতে ব্যয় হয়েছে (মোট মেট্রিক
valueতুলনায়attribution.processingDurationথেকে)। - ধীরগতির ইভেন্ট লিসেনার কোডটি
third-party/cmp.js(scripts.sourceURLথেকে) ফাইলে সংজ্ঞায়িত একটি ক্লিক লিসেনার থেকে শুরু হয়।
কোথায় অপ্টিমাইজ করতে হবে, তা জানার জন্য এই ডেটাই যথেষ্ট!
৬. একাধিক ইভেন্ট লিসেনার
পৃষ্ঠাটি রিফ্রেশ করুন যাতে DevTools কনসোলটি খালি হয়ে যায় এবং কুকি সম্মতির ইন্টারঅ্যাকশনটি আর দীর্ঘতম ইন্টারঅ্যাকশন না থাকে।
সার্চ বক্সে টাইপ করা শুরু করুন। অ্যাট্রিবিউশন ডেটা কী দেখাচ্ছে? আপনার কী মনে হচ্ছে, কী ঘটছে?
অ্যাট্রিবিউশন ডেটা
প্রথমে, ডেমোটি পরীক্ষা করার একটি উদাহরণের সামগ্রিক পর্যালোচনা করা যাক:
{
name: 'INP',
value: 1072,
rating: 'poor',
attribution: {
interactionTargetElement: Element,
interactionTarget: '#search-terms',
interactionType: 'keyboard',
inputDelay: 3.3,
processingDuration: 1060.6,
presentationDelay: 8.1,
processedEventEntries: [...],
longAnimationFrameEntries: [...],
}
}
input#search-terms এলিমেন্টের সাথে কিবোর্ড ইন্টারঅ্যাকশনের ফলে প্রাপ্ত INP মানটি দুর্বল (সিপিইউ থ্রটলিং চালু থাকা অবস্থায়)। মোট ১০৭২ মিলিসেকেন্ড INP-এর মধ্যে ১০৬১ মিলিসেকেন্ড, অর্থাৎ বেশিরভাগ সময়ই, প্রসেসিং-এ ব্যয় হয়েছে।
তবে scripts এন্ট্রিগুলো আরও আকর্ষণীয়।
লেআউট থ্র্যাশিং
scripts অ্যারের প্রথম এন্ট্রিটি আমাদের কিছু মূল্যবান প্রেক্ষাপট প্রদান করে:
scripts: [{
name: 'script',
invoker: 'BUTTON#confirm.onclick',
invokerType: 'event-listener',
startTime: 4875.6,
executionStart: 4875.6,
duration: 497,
forcedStyleAndLayoutDuration: 388,
sourceURL: 'http://localhost:8080/js/index.js',
sourceFunctionName: 'handleSearch',
sourceCharPosition: 940
},
...]
প্রক্রিয়াকরণের বেশিরভাগ সময় এই স্ক্রিপ্টটি চলার সময় অতিবাহিত হয়, যেটি একটি input লিসেনার (এর আহ্বানকারী হলো INPUT#search-terms.oninput )। ফাংশনের নাম ( handleSearch ) দেওয়া আছে, এবং index.js সোর্স ফাইলের ভেতরে এর ক্যারেক্টার পজিশনও উল্লেখ করা আছে।
তবে, একটি নতুন প্রপার্টি আছে: forcedStyleAndLayoutDuration । এই স্ক্রিপ্ট আহ্বানের সময়টুকুতেই ব্রাউজার পৃষ্ঠাটি পুনরায় লেআউট করতে বাধ্য হয়েছিল। অন্য কথায়, এই ইভেন্ট লিসেনারটি কার্যকর করতে ব্যয় হওয়া সময়ের ৭৮%—অর্থাৎ ৪৯৭ মিলিসেকেন্ডের মধ্যে ৩৮৮ মিলিসেকেন্ড—আসলে লেআউট থ্র্যাশিংয়েই ব্যয় হয়েছে।
এটি সমাধান করা সর্বোচ্চ অগ্রাধিকার হওয়া উচিত।
বারবার শ্রোতারা
পৃথকভাবে, পরবর্তী দুটি স্ক্রিপ্ট এন্ট্রিতে বিশেষভাবে উল্লেখযোগ্য কিছু নেই:
scripts: [...,
{
name: 'script',
invoker: '#document.onkeyup',
invokerType: 'event-listener',
startTime: 5375.3,
executionStart: 5375.3,
duration: 124,
sourceURL: 'http://localhost:8080/js/index.js',
sourceFunctionName: '',
sourceCharPosition: 1526,
},
{
name: 'script',
invoker: '#document.onkeyup',
invokerType: 'event-listener',
startTime: 5673.9,
executionStart: 5673.9,
duration: 95,
sourceURL: 'http://localhost:8080/js/index.js',
sourceFunctionName: '',
sourceCharPosition: 1526
}]
উভয় এন্ট্রিই হলো keyup লিসেনার, যেগুলো একটির পর একটি এক্সিকিউট হয়। লিসেনারগুলো অ্যানোনিমাস ফাংশন (এজন্য sourceFunctionName প্রপার্টিতে কিছুই রিপোর্ট করা হয় না), কিন্তু আমাদের কাছে একটি সোর্স ফাইল এবং ক্যারেক্টার পজিশন থাকে, ফলে আমরা কোডটি কোথায় আছে তা খুঁজে বের করতে পারি।
আশ্চর্যজনক ব্যাপার হলো যে, দুটোই একই উৎস ফাইল এবং একই অক্ষর অবস্থান থেকে এসেছে ।
ব্রাউজারটি শেষ পর্যন্ত একটি একক অ্যানিমেশন ফ্রেমে একাধিক কী-প্রেস প্রসেস করে ফেলে, যার ফলে কোনো কিছু আঁকার আগেই এই ইভেন্ট লিসেনারটি দুইবার রান করে!
এই প্রভাব আরও বাড়তে পারে, যেখানে ইভেন্ট লিসেনারগুলো সম্পূর্ণ হতে যত বেশি সময় নেয়, তত বেশি অতিরিক্ত ইনপুট ইভেন্ট আসতে পারে, যা এই ধীরগতির ইন্টারঅ্যাকশনকে আরও দীর্ঘায়িত করে।
যেহেতু এটি একটি সার্চ/অটোকমপ্লিট ইন্টারঅ্যাকশন, তাই ইনপুটকে ডিবাউন্স করা একটি ভালো কৌশল হবে, যাতে প্রতি ফ্রেমে সর্বাধিক একটি কী-প্রেস প্রসেস করা হয়।
৭. ইনপুট বিলম্ব
ইনপুট বিলম্বের সাধারণ কারণ হলো—ব্যবহারকারীর ইন্টারঅ্যাকশনের পর থেকে ইভেন্ট লিসেনার কর্তৃক সেই ইন্টারঅ্যাকশনটি প্রসেস করা শুরু করার মধ্যবর্তী সময়—মূল থ্রেডটি ব্যস্ত থাকা। এর একাধিক কারণ থাকতে পারে:
- পৃষ্ঠাটি লোড হচ্ছে এবং প্রধান থ্রেডটি DOM সেট আপ করা, পৃষ্ঠার বিন্যাস ও স্টাইল করা এবং স্ক্রিপ্ট মূল্যায়ন ও চালানোর প্রাথমিক কাজগুলো নিয়ে ব্যস্ত রয়েছে।
- পৃষ্ঠাটি সাধারণত ব্যস্ত থাকে—যেমন, এতে গণনা, স্ক্রিপ্ট-ভিত্তিক অ্যানিমেশন বা বিজ্ঞাপন চলতে থাকে।
- পূর্ববর্তী আলাপচারিতাগুলো প্রক্রিয়া করতে এত বেশি সময় লাগে যে তা ভবিষ্যতের আলাপচারিতাকে বিলম্বিত করে, যা শেষ উদাহরণটিতে দেখা গেছে।
ডেমো পেজটিতে একটি গোপন বৈশিষ্ট্য রয়েছে, যেখানে পেজের উপরের দিকে থাকা শামুকের লোগোটিতে ক্লিক করলে এটি অ্যানিমেট হতে শুরু করবে এবং মেইন থ্রেডে কিছু ভারী জাভাস্ক্রিপ্ট কাজ করবে।
- অ্যানিমেশনটি শুরু করতে শামুকের লোগোটিতে ক্লিক করুন।
- যখন স্নেইলটি বাউন্সের একেবারে নিচে থাকে, তখন জাভাস্ক্রিপ্ট টাস্কগুলো ট্রিগার হয়। বাউন্সের একেবারে নিচের দিকে যতটা সম্ভব পেজটির সাথে ইন্টারঅ্যাক্ট করার চেষ্টা করুন এবং দেখুন আপনি কত উচ্চ মানের একটি INP ট্রিগার করতে পারেন।
উদাহরণস্বরূপ, আপনি অন্য কোনো ইভেন্ট লিসেনার ট্রিগার না করলেও—যেমন শামুকটি লাফানোর ঠিক মুহূর্তে সার্চ বক্সে ক্লিক করে ফোকাস করলে—মেইন-থ্রেডের কাজের কারণে পেজটি লক্ষণীয় পরিমাণ সময়ের জন্য প্রতিক্রিয়াহীন হয়ে পড়বে।
অনেক পেজেই, মূল থ্রেডের ভারী কাজ এতটা সুশৃঙ্খল থাকবে না, কিন্তু INP অ্যাট্রিবিউশন ডেটাতে এটি কীভাবে শনাক্ত করা যায়, তা দেখার জন্য এটি একটি ভালো উদাহরণ।
স্নেইল বাউন্সের সময় শুধুমাত্র সার্চ বক্সে ফোকাস করার ফলে প্রাপ্ত অ্যাট্রিবিউশনের একটি উদাহরণ এখানে দেওয়া হলো:
{
name: 'INP',
value: 728,
rating: 'poor',
attribution: {
interactionTargetElement: Element,
interactionTarget: '#search-terms',
interactionType: 'pointer',
inputDelay: 702.3,
processingDuration: 4.9,
presentationDelay: 20.8,
longAnimationFrameEntries: [{
name: 'long-animation-frame',
startTime: 2064.8,
duration: 790,
renderStart: 2065,
styleAndLayoutStart: 2854.2,
firstUIEventTimestamp: 0,
blockingDuration: 740,
scripts: [{...}]
}]
}
}
পূর্বানুমান অনুযায়ী, ইভেন্ট লিসেনারগুলো দ্রুত কার্যকর হয়েছিল—যার প্রক্রিয়াকরণের সময় ছিল ৪.৯ মিলিসেকেন্ড, এবং ত্রুটিপূর্ণ ইন্টারঅ্যাকশনের সিংহভাগই ব্যয় হয়েছিল ইনপুট বিলম্বে, যা মোট ৭২৮ মিলিসেকেন্ডের মধ্যে ৭০২.৩ মিলিসেকেন্ড সময় নিয়েছিল।
এই পরিস্থিতি ডিবাগ করা কঠিন হতে পারে। যদিও আমরা জানি ব্যবহারকারী কিসের সাথে এবং কীভাবে ইন্টারঅ্যাক্ট করেছেন, আমরা এটাও জানি যে ইন্টারঅ্যাকশনের ঐ অংশটি দ্রুত সম্পন্ন হয়েছিল এবং কোনো সমস্যা ছিল না। বরং পেজের অন্য কোনো কিছু ইন্টারঅ্যাকশনটির প্রসেসিং শুরু হতে দেরি করিয়েছিল, কিন্তু আমরা কীভাবে জানব কোথা থেকে খোঁজা শুরু করতে হবে?
LoAF স্ক্রিপ্ট এন্ট্রিগুলোই ত্রাতা হিসেবে হাজির হয়েছে:
scripts: [{
name: 'script',
invoker: 'SPAN.onanimationiteration',
invokerType: 'event-listener',
startTime: 2065,
executionStart: 2065,
duration: 788,
sourceURL: 'http://localhost:8080/js/index.js',
sourceFunctionName: 'cryptodaphneCoinHandler',
sourceCharPosition: 1831
}]
যদিও এই ফাংশনটির ইন্টারঅ্যাকশনের সাথে কোনো সম্পর্ক ছিল না, তবুও এটি অ্যানিমেশন ফ্রেমের গতি কমিয়ে দিয়েছিল, এবং সেই কারণে ইন্টারঅ্যাকশন ইভেন্টের সাথে যুক্ত LoAF ডেটাতে এটিকে অন্তর্ভুক্ত করা হয়।
এর থেকে আমরা দেখতে পারি যে, ইন্টারঅ্যাকশন প্রসেসিং-এ বিলম্বকারী ফাংশনটি কীভাবে (একটি animationiteration লিসেনারের মাধ্যমে) ট্রিগার হয়েছিল, ঠিক কোন ফাংশনটি এর জন্য দায়ী ছিল, এবং আমাদের সোর্স ফাইলগুলোতে সেটি কোথায় অবস্থিত ছিল।
৮. উপস্থাপনায় বিলম্ব: যখন কোনো আপডেট প্রদর্শিত হয় না
প্রেজেন্টেশন ডিলে হলো সেই সময়, যা ইভেন্ট লিসেনারগুলোর কাজ শেষ হওয়ার পর থেকে ব্রাউজার স্ক্রিনে একটি নতুন ফ্রেম এঁকে ব্যবহারকারীকে দৃশ্যমান ফিডব্যাক দেখানোর আগ পর্যন্ত অতিবাহিত হয়।
INP মানটি আবার রিসেট করতে পৃষ্ঠাটি রিফ্রেশ করুন, তারপর হ্যামবার্গার মেনুটি খুলুন। এটি খোলার সময় একটি সুস্পষ্ট সমস্যা হয়।
এটা দেখতে কেমন?
{
name: 'INP',
value: 376,
rating: 'needs-improvement',
delta: 352,
attribution: {
interactionTarget: '#sidenav-button>svg',
interactionType: 'pointer',
inputDelay: 12.8,
processingDuration: 14.7,
presentationDelay: 348.5,
longAnimationFrameEntries: [{
name: 'long-animation-frame',
startTime: 651,
duration: 365,
renderStart: 673.2,
styleAndLayoutStart: 1004.3,
firstUIEventTimestamp: 138.6,
blockingDuration: 315,
scripts: [{...}]
}]
}
}
এবারের ধীরগতির ইন্টারঅ্যাকশনের বেশিরভাগটাই হচ্ছে প্রেজেন্টেশন ডিলে-র কারণে । এর মানে হলো, যা কিছু মেইন থ্রেডকে ব্লক করছে, তা ইভেন্ট লিসেনারগুলোর কাজ শেষ হওয়ার পরে ঘটে।
scripts: [{
entryType: 'script',
invoker: 'FrameRequestCallback',
invokerType: 'user-callback',
startTime: 673.8,
executionStart: 673.8,
duration: 330,
sourceURL: 'http://localhost:8080/js/side-nav.js',
sourceFunctionName: '',
sourceCharPosition: 1193,
}]
scripts অ্যারের একমাত্র এন্ট্রিটি দেখলে আমরা দেখতে পাই যে, সময়টি একটি FrameRequestCallback থেকে user-callback ব্যয় হচ্ছে। এইবার প্রেজেন্টেশন বিলম্বটি একটি requestAnimationFrame কলব্যাকের কারণে ঘটেছে।
৯. উপসংহার
মাঠের তথ্য একত্রিত করা
এটা মনে রাখা দরকার যে, একটিমাত্র পেজ লোড থেকে একটিমাত্র INP অ্যাট্রিবিউশন এন্ট্রি দেখলে এই পুরো বিষয়টি বোঝা সহজ হয়। ফিল্ড ডেটার উপর ভিত্তি করে INP ডিবাগ করার জন্য এই ডেটা কীভাবে একত্রিত করা যেতে পারে? সহায়ক বিবরণের প্রাচুর্য আসলে এই কাজটিকে আরও কঠিন করে তোলে।
উদাহরণস্বরূপ, কোন পেজ এলিমেন্টটি ধীরগতির ইন্টারঅ্যাকশনের একটি সাধারণ উৎস, তা জানা অত্যন্ত দরকারি। তবে, যদি আপনার পেজের কম্পাইল করা CSS ক্লাস নেমগুলো এক বিল্ড থেকে অন্য বিল্ডে পরিবর্তিত হয়, তাহলে একই এলিমেন্টের web-vitals সিলেক্টরগুলো বিভিন্ন বিল্ডে ভিন্ন হতে পারে।
এর পরিবর্তে, কোনটি সবচেয়ে উপযোগী এবং কীভাবে ডেটা একত্রিত করা যেতে পারে তা নির্ধারণ করতে আপনাকে আপনার নির্দিষ্ট অ্যাপ্লিকেশনটি নিয়ে ভাবতে হবে। উদাহরণস্বরূপ, অ্যাট্রিবিউশন ডেটা ফেরত পাঠানোর আগে, আপনি টার্গেটটি যে কম্পোনেন্টে আছে বা টার্গেটটি যে ARIA রোলগুলো পূরণ করে, তার উপর ভিত্তি করে web-vitals সিলেক্টরটিকে আপনার নিজস্ব একটি আইডেন্টিফায়ার দিয়ে প্রতিস্থাপন করতে পারেন।
একইভাবে, scripts এন্ট্রিগুলোর sourceURL পাথে ফাইল-ভিত্তিক হ্যাশ থাকতে পারে, যা সেগুলোকে একত্রিত করা কঠিন করে তোলে, কিন্তু ডেটা বিকনিং করার আগে আপনি আপনার পরিচিত বিল্ড প্রক্রিয়ার উপর ভিত্তি করে হ্যাশগুলো বাদ দিতে পারেন।
দুর্ভাগ্যবশত, এত জটিল ডেটার ক্ষেত্রে কোনো সহজ পথ নেই, কিন্তু ডিবাগিং প্রক্রিয়ার জন্য এর একটি উপাংশ ব্যবহার করাও একেবারে কোনো অ্যাট্রিবিউশন ডেটা না থাকার চেয়ে বেশি মূল্যবান।
সর্বত্র কৃতিত্ব প্রদান!
LoAF-ভিত্তিক INP অ্যাট্রিবিউশন একটি শক্তিশালী ডিবাগিং সহায়ক। এটি একটি INP চলাকালীন নির্দিষ্টভাবে কী ঘটেছিল সে সম্পর্কে বিশদ তথ্য প্রদান করে। অনেক ক্ষেত্রে, এটি আপনাকে স্ক্রিপ্টের সেই সুনির্দিষ্ট স্থানটি দেখিয়ে দিতে পারে যেখান থেকে আপনার অপ্টিমাইজেশনের কাজ শুরু করা উচিত।
আপনি এখন যেকোনো সাইটে INP অ্যাট্রিবিউশন ডেটা ব্যবহার করার জন্য প্রস্তুত!
আপনার কোনো পৃষ্ঠা সম্পাদনা করার অ্যাক্সেস না থাকলেও, আপনি DevTools কনসোলে নিম্নলিখিত কোডটি চালিয়ে এই কোডল্যাবের প্রক্রিয়াটি পুনরায় তৈরি করতে পারেন এবং দেখতে পারেন যে আপনি কী খুঁজে পান:
const script = document.createElement('script');
script.src = 'https://unpkg.com/web-vitals@4/dist/web-vitals.attribution.iife.js';
script.onload = function () {
webVitals.onINP(console.log, {reportAllChanges: true});
};
document.head.appendChild(script);