چطور یه hydration mismatch کوچیک LCP رو خراب میکنه
خلاصهٔ کاملتر
نویسنده که کارش کمک به بهبود پرفورمنس اپهای ریاکته میگه یه مشکل رایج ولی کمشناخته رو مدام میبینه: فقط یه hydration mismatch روی صفحه میتونه معیار Largest Contentful Paint یا LCP رو از سبز ببره تهِ قرمز. hydration همون مرحلهایه که ریاکت DOM رندرشدهی سمت سرور رو تعاملی میکنه، روش قدم میزنه و بهش event listener مثل onClick وصل میکنه؛ اگه چیزی که کلاینت رندر میکنه با نسخهی سرور فرق کنه، بهش میگن mismatch.
مثلاً این کامپوننت سمت سرور یه div برمیگردونه ولی سمت کلاینت یه button؛ ریاکت موقع پیمایش DOM انتظار button داره ولی div پیدا میکنه و همینجا mismatch اتفاق میافته:
function MyButton() {
if (typeof window === 'undefined')
return <div>Please wait for the app to load...</div>;
return <button onClick={() => console.log('Clicked')}>Click me</button>;
}به گفتهی نویسنده برای فهمیدن چرایی مشکل باید سهتا واقعیت رو کنار هم بذاری. اولیش اینه که از ریاکت ۱۸ به بعد، mismatch باعث میشه ریاکت کل DOM رو از صفر بسازه. قبلاً ریاکت فقط تفاوتها رو وصله میکرد ولی این رفتار برای درستی حذف شد. حالا اگه سرور «Current time: 10:09» بده و کلاینت «10:10»، ریاکت نزدیکترین مرز Suspense رو پیدا میکنه و کل DOM زیرش رو دوباره میسازه؛ اگه هیچ Suspenseای نباشه، کل صفحه remount میشه.
واقعیت دوم اینه که وقتی وبفونتها با font-display: swap لود میشن، متن جابهجا و کمی بزرگتر یا کوچیکتر میشه، چون یه کاراکتر تو فونتهای مختلف اندازهی فیزیکی متفاوتی داره. واقعیت سوم هم اینه که LCP فقط اِلمانهای جدیدی که به DOM اضافه میشن رو اندازه میگیره؛ اگه یه اِلمان موجود بزرگتر بشه، مرورگر نادیدهش میگیره.
حالا این سهتا رو قاطی کن: یه بلاک متنی داری که با لود شدن فونت بزرگتر میشه، و یه mismatch که همون بلاک رو remount میکنه. بدون mismatch، LCP همون لحظهای ثبت میشد که متن اولین بار دیده میشه. ولی چون hydration همهی اِلمانها رو پاک و از نو میسازه، از دید مرورگر یه مشت نودِ کاملاً جدیدِ بزرگتر اضافه شده، پس LCP رو آپدیت میکنه و میبره رو لحظهی تموم شدن hydration.
نویسنده میگه روی یه اتصال 4G معمولی و یه صفحهی ریاکت معمولی، این اتفاق حدود ۵ ثانیه یا بیشتر طول میکشه و LCP رو از سبز یا زرد میبره عمق قرمز، که برای رتبهی سئو هم بده. راهحل اصلی اینه که mismatchها رو نداشته باشی و اگه پیش اومدن درستشون کنی. اگه نمیتونی، اون اِلمانی که mismatch میسازه رو با بپیچ تا ریاکت فقط همون مرز رو remount کنه نه کل صفحه رو؛ مگه اینکه خودِ همون اِلمان، اِلمان LCP باشه.
نکات کلیدی:
- فقط یه hydration mismatch میتونه LCP رو از سبز به قرمز ببره.
- از ریاکت ۱۸، mismatch باعث ساخت دوبارهی کل DOM از صفر میشه.
- LCP فقط نودهای جدید رو اندازه میگیره، نه بزرگتر شدن نودهای موجود.
- لود شدن وبفونت متن رو بزرگتر میکنه و با remount ترکیب میشه.
- راهحل: رفع mismatch یا پیچیدن اِلمان مقصر با Suspense.




