একটি নিখুঁত Lighthouse স্কোরের পেছনের প্রতিটি অপ্টিমাইজেশন

আমি আমার সাইটে প্রতিনিয়ত Lighthouse চালাই। প্রতিটি লোকাল রানে এটি ১০০ থাকে এবং Vercel-এর Real Experience Score-এও ১০০ থাকে।

এই স্কোরগুলো কোনো সাধারণ চেকলিস্ট থেকে আসে না। আমি ব্রাউজার থেকে কাজের চাপ বিল্ড স্টেজে (build stage) সরিয়ে নিয়ে এগুলো অর্জন করি।

আমি ঠিক যেভাবে এটি করি তা নিচে দেওয়া হলো।

কাজ বিল্ড টাইমে (build time) সরিয়ে নিন

বেশিরভাগ গাইড আপনাকে সবকিছু lazy-load করতে বলে। আমি prerender করা পছন্দ করি। আমি prerendering এনাবল করা অবস্থায় TanStack Start ব্যবহার করি।

এটি বিল্ড করার সময় পুরো সাইটটিকে স্ট্যাটিক HTML-এ রূপান্তরিত করে। প্রথম পেজটি দেখানোর জন্য ব্রাউজারকে ভারী JavaScript এক্সিকিউট করতে হয় না। ইউজার যখন এন্টার চাপেন, HTML তখন ইতিমধ্যেই প্রস্তুত থাকে।

জটিল লজিক প্রি-কম্পিউট (Precompute) করুন

আমার হোমপেজে ৫,০০০টি ডটসহ একটি বিশ্ব মানচিত্র আছে। সাধারণত, একটি লাইব্রেরি GeoJSON পার্স করে এবং মেইন থ্রেডে (main thread) গাণিতিক কাজ সম্পন্ন করে। এটি ১,০০০ মিলি-সেকেন্ডের জন্য পেজটিকে ব্লক করে রাখে।

আমি এই গাণিতিক কাজটিকে একটি বিল্ড স্ক্রিপ্টে সরিয়ে নিয়ে এই সমস্যার সমাধান করেছি।

  • আমি ৫,০০০টি ডটের জন্য একটি মাত্র SVG path string তৈরি করি।
  • ৫,০০০টি আলাদা সার্কেলের তুলনায় একটি পাথ ব্রাউজারের জন্য রেন্ডার করা অনেক দ্রুত।
  • আমি কোঅর্ডিনেট লুকআপ টেবিলগুলো (coordinate lookup tables) প্রি-ক্যালকুলেট করে রাখি যাতে রানটাইমে ব্রাউজারকে কোনো গণনাই করতে না হয়।

১,০০০ মিলি-সেকেন্ডের বিলম্বটি একটি তাৎক্ষণিক পেইন্টে (instant paint) পরিণত হয়।

ফন্ট লোডিং অপ্টিমাইজ করুন

আমি আমার প্রাইমারি ফন্টগুলোর জন্য rel="preload" ব্যবহার করি।

একটি সাধারণ ভুল হলো crossOrigin অ্যাট্রিবিউটটি ভুলে যাওয়া। আপনি যদি এটি বাদ দেন, তবে ব্রাউজার ফন্টটি দুবার ফেচ (fetch) করে। এটি আপনার Largest Contentful Paint (LCP)-কে নষ্ট করে দেয়। আমি শুধুমাত্র 'above the fold' অংশে ব্যবহৃত তিনটি ফন্ট প্রি-লোড করি।

অ্যানিমেশনের জন্য সঠিক টুল ব্যবহার করুন

আমি আমার ম্যাপ মার্কারগুলোর সাধারণ পালস অ্যানিমেশনের জন্য SMIL ব্যবহার করি। অ্যানিমেশন লুপ চালানোর জন্য React state ব্যবহার করার চেয়ে এটি অনেক সাশ্রয়ী। এটি ব্রাউজারকে কম্পোজিটর থ্রেডে (compositor thread) কাজ করার সুযোগ দেয়।

জটিল পাথের জন্য আমি motion ব্যবহার করি। আমি এটিকে সহজ রাখি। আমি মাউন্ট (mount) হওয়ার সময় একবার অ্যানিমেট করি এবং স্ক্রল পজিশন লিসেন করা এড়িয়ে চলি।

ভেক্টর এবং WebP ব্যবহার করুন

এটি যদি কোনো লোগো বা শেপ হয়, তবে SVG ব্যবহার করুন। যদি কোনো ছবি হয়, তবে WebP ব্যবহার করুন। এটি ফাইলের সাইজ কম রাখে এবং লেআউট শিফট (layout shift) প্রতিরোধ করে।

ওভার-ইঞ্জিনিয়ারিং এড়িয়ে চলুন

আমি কোনো ইমেজ CDN ব্যবহার করি না। আমি জটিল code-splitting ব্যবহার করি না। আমার সাইটটি ছোট, তাই route-level splitting-ই যথেষ্ট।

একটি নিখুঁত স্কোর কেবল একটি ভ্যানিটি মেট্রিক (vanity metric) হতে পারে। আসল লক্ষ্য হলো আপনার পারফরম্যান্স পরিমাপ করা এবং ইউজারের ডিভাইস থেকে যতটা সম্ভব কাজের চাপ সরিয়ে নেওয়া।

My Portfolio: brodin.dev

Source code: github.com/NathanBrodin/Portfolio

TanStack Start prerendering: tanstack.com/start

Paper Shaders: shaders.paper.design

Full post: https://dev.to/nathan-brodin/every-optimization-behind-a-perfect-lighthouse-score-283n