متن سایت شما چند میلیثانیه نامرئی است
صفحه را باز میکنید. محتوا هست، اما حروف نیستند. دو ثانیه بعد، ناگهان همهچیز ظاهر میشود و چیدمان صفحه جابهجا میخورد. این دقیقاً همان لحظهای است که کاربر ایرانی هنگام کار با سایت شما تجربه میکند و دلیلش هم فونت فارسی سایت است.
مشکل از آنجا شروع میشود که فونتهای فارسی وزن سنگینی دارند. یک فایل وزیرمتن کامل با ۴ وزن معمولی، بین ۴۰۰ تا ۷۰۰ کیلوبایت است. فونت وزیرمتن نسخهٔ Variable هم که به ۱ مگابایت هم میرسد. این حجم را با فایل woff2 مقایسه کنید که بعد از فشردهسازی، معمولاً ۲۰ تا ۳۰ درصد حجم اصلی میشود. اما حتی همین حجم فشرده هم وقتی روی شبکهٔ موبایل ایران با تأخیر ۸۰ تا ۱۵۰ میلیثانیهای بارگذاری میشود، یعنی چند صد میلیثانیه تأخیر در نمایش متن.
در این فاصله، مرورگر دو انتخاب دارد. یا متن را با فونت پیشفرض سیستم نشان میدهد (FOUT) یا آن را کاملاً مخفی نگه میدارد (FOIT). هر دو حالت برای تجربهٔ کاربری فاجعهاند. اولی باعث پرش چیدمان یا همان CLS میشود و دومی کاربر را با صفحهای خالی روبهرو میکند.
راهحل، حذف فونت نیست. راهحل، کنترل دقیق همین لحظه است.
font-display: اولین تصمیمی که باید بگیرید
خروجی CSS زیر را در نظر بگیرید:
@font-face {
font-family: 'Vazirmatn';
src: url('/fonts/vazirmatn.woff2') format('woff2');
font-display: swap;
font-weight: 400;
}
مقدار swap به مرورگر میگوید: «متن را فوراً با فونت پیشفرض نشان بده؛ وقتی فونت آماده شد، جایگزین کن.» این یعنی کاربر هیچوقت متن خالی نمیبیند. اما هزینهاش پرش چیدمان است. چون فونت فارسی معمولاً ارتفاع خط و عرض حروف متفاوتی با فونت پیشفرض سیستم دارد.
مقدار block دقیقاً برعکس عمل میکند. مرورگر تا ۳ ثانیه صبر میکند و اگر فونت نیامد، متن را با فونت جایگزین نشان میدهد. این حالت FOIT را به حداکثر میرساند و برای سایت فارسی که فونتش روی سرور داخلی است، معمولاً انتخاب اشتباهی است.
مقدار optional را هم جدی بگیرید. این مقدار به مرورگر میگوید اگر فونت خیلی سریع نیامد، اصلاً استفادهاش نکن و برای همیشه فونت سیستم را نگه دار. نتیجه؟ کاربری که فونت را نمیبیند اما پرشی هم ندارد. برای سایتهایی که فونت برایشان هویت برند است، این یعنی شکست کامل طراحی.
اینجا اشتباه میکنند: خیلیها فکر میکنند swap همهٔ مشکلات را حل میکند. اما نتیجهٔ کارشان را در Google PageSpeed Insights ببینید. CLS از ۰.۱۵ به ۰.۳۸ میرود و گزارش میگوید «از font-display: optional استفاده کنید». این توصیه را کورکورانه اجرا نکنید. اگر فونت فارسی برای خوانایی متن شما حیاتی است، swap را نگه دارید و مشکل CLS را با تکنیکهای زیرساخت حل کنید، نه با قربانی کردن فونت.
اندازهگیری واقعی مشکل
قبل از هر تغییری، وضعیت فعلی را ثبت کنید. در Chrome DevTools، تب Performance را باز کنید و یک بار صفحه را با Network Throttling روی Slow 4G بارگذاری کنید. دو عدد را یادداشت کنید: FCP و CLS. سپس در تب Network، فایل فونت را پیدا کنید و ستون Waterfall را ببینید. اگر فونت بعد از ۱.۵ ثانیه شروع به دانلود شده باشد، یعنی مشکل از جای دیگری است: فونت شما پایینتر از حد لازم در HTML ارجاع داده شده یا توسط CSS دیگری مسدود شده است.
یک نکتهٔ عملی دیگر: فایل فونت را با دستور زیر بررسی کنید:
curl -sI https://example.com/fonts/vazirmatn.woff2 | grep -E "HTTP|content-length|cache-control"
اگر cache-control شامل max-age کمتر از ۳۱۵۳۶۰۰۰ نباشد، مرورگر کاربر فونت را در هر بازدید دوباره دانلود میکند. این یعنی هر بار که کاربر به صفحهٔ بعدی سایت شما میرود، دوباره منتظر فونت میماند.
زیرمجموعهسازی فونت فارسی؛ جایی که حجم واقعاً کم میشود
فونت کامل فارسی شامل ۶۰۰+ گلیف است: حروف، اعداد، علائم، ترکیبات. اما صفحهٔ شما در یک لحظه به چند تای آنها نیاز دارد؟ جواب: به کمتر از ۱۰۰ تای آنها. زیرمجموعهسازی (subsetting) یعنی فایل فونت را به چند بخش تقسیم کنید و فقط بخشی را که مرورگر واقعاً نیاز دارد ارسال کنید.
ابزار رایگان pyftsubset از پروژهٔ fonttools این کار را انجام میدهد. نصب و اجرا:
pip install fonttools brotli
pyftsubset vazirmatn.ttf \
--unicodes="U+0600-06FF,U+0750-077F,U+FB50-FDFF,U+FE70-FEFF" \
--flavor=woff2 \
--output-file=vazirmatn-fa.woff2
این دستور فقط گلیفهای عربی و فارسی را نگه میدارد و بقیه را حذف میکند. نتیجه برای وزیرمتن معمولاً از ۴۰۰ کیلوبایت به ۱۲۰ تا ۱۵۰ کیلوبایت میرسد. حالا این فایل را با unicode-range در CSS تعریف کنید:
@font-face {
font-family: 'Vazirmatn';
src: url('/fonts/vazirmatn-fa.woff2') format('woff2');
unicode-range: U+0600-06FF, U+0750-077F, U+FB50-FDFF, U+FE70-FEFF;
font-display: swap;
}
مرورگرهای مدرن فقط وقتی این فایل را دانلود میکنند که متنی با این محدودهٔ یونیکد در صفحه پیدا کنند. برای صفحهای که فقط فارسی دارد، یعنی دانلود فوری. برای صفحهای که انگلیسی است، یعنی هیچ دانلودی.
یک قدم فراتر بروید: فونت را بر اساس کاراکترهای پرتکرار زیرمجموعه کنید. ابزار fonttools subset با فلگ --text این امکان را میدهد:
pyftsubset vazirmatn.ttf \
--text="ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیئ،.؛:!؟۱۲۳۴۵۶۷۸۹۰" \
--flavor=woff2 \
--output-file=vazirmatn-body.woff2
این فایل برای متن بدنهٔ سایت کافی است و حجمش به ۳۰ تا ۴۰ کیلوبایت میرسد. اما مراقب باشید: اگر کاربر متنی با کاراکتری خارج از این لیست تایپ کند یا صفحهای با کلمات خاص داشته باشید، آن حروف نمایش داده نمیشوند. برای همین، این روش را فقط برای فونت بدنه به کار ببرید و فونت کامل را برای تیترها و المانهای خاص نگه دارید.
ترکیب subsetting با preload
زیرمجموعهسازی حجم را کم میکند اما تأخیر شبکه را نه. برای اینکه فونت از همان ابتدای بارگذاری صفحه دانلود شود، آن را با preload به مرورگر اعلام کنید:
<link rel="preload" href="/fonts/vazirmatn-fa.woff2" as="font" type="font/woff2" crossorigin>
این خط را در <head> قرار دهید. مرورگر بلافاصله دانلود فونت را شروع میکند، بدون اینکه منتظر رسیدن CSS باشد. نتیجهٔ عملی: فونت معمولاً ۲۰۰ تا ۴۰۰ میلیثانیه زودتر آماده میشود.
اینجا اشتباه میکنند: preload را روی فایل فونت کامل اعمال میکنند. نتیجه این است که ۴۰۰ کیلوبایت از همان ابتدا دانلود میشود، حتی اگر صفحه فقط ۵۰ کیلوبایت فونت نیاز داشته باشد. همیشه preload را روی کوچکترین زیرمجموعهای بگذارید که برای رندر اولیه لازم است.
FOUT را بپذیرید، اما آن را مدیریت کنید
با font-display: swap، کاربر ابتدا متن را با فونت سیستم میبیند و بعد با فونت اصلی. این پرش اگر کنترل نشود، CLS را بالا میبرد و تجربهٔ خواندن را خراب میکند. دو راه برای مدیریت این پرش دارید.
راه اول: فونت جایگزین را طوری انتخاب کنید که ابعادش نزدیک به فونت اصلی باشد. برای وزیرمتن، فونت پیشنهادی سیستم در لینوکس معمولاً DejaVu Sans است که ارتفاع خطش حدود ۱۵٪ بیشتر است. با تنظیم line-height در CSS میتوانید این اختلاف را جبران کنید:
body {
font-family: 'Vazirmatn', 'DejaVu Sans', sans-serif;
line-height: 1.8;
}
.fonts-loaded body {
line-height: 1.7;
}
راه دوم: با جاوااسکریپت، کلاس fonts-loaded را بعد از اتمام بارگذاری فونت به <body> اضافه کنید. این کار با document.fonts.ready ساده است:
document.fonts.ready.then(function () {
document.body.classList.add('fonts-loaded');
});
این روش به شما کنترل کامل میدهد. میتوانید فونت را برای المانهای خاص مثل تیترها زودتر فعال کنید و برای متن بدنه صبر کنید. نتیجهٔ نهایی، پرشی است که کاربر متوجه آن نمیشود، نه پرشی که کل صفحه را جابهجا میکند.
فونت فارسی را روی سرور داخلی نگه دارید
اگر فونت شما روی CDN خارجی یا سرور گوگل فونتس باشد، هر درخواست فونت باید از مرز عبور کند. تأخیر این مسیر در بدترین حالت به ۳۰۰ میلیثانیه میرسد. فونت را روی همان سروری بگذارید که سایت شما روی آن اجرا میشود و مسیر آن را با same-origin نگه دارید. این کار یک مزیت دیگر هم دارد: با HTTP/2 روی سرور داخلی، فونت و CSS و جاوااسکریپت در یک اتصال TCP بارگذاری میشوند و هزینهٔ برقراری اتصال جدید حذف میشود.
برای بررسی اینکه فونت شما واقعاً از کجا سرو میشود، از ابزار بررسی فنی دامنه و DNS استفاده کنید و ببینید آدرس فونت به کدام IP ختم میشود. اگر IP خارج از ایران است، یعنی هر بار که فونت دانلود میشود، کاربر منتظر عبور از مرز است.
ترتیب بهینهسازی؛ از کجا شروع کنید
اگر سایت شما همین حالا مشکل پرش متن دارد، این ترتیب را دنبال کنید:
- فونت را زیرمجموعهسازی کنید و فقط گلیفهای فارسی را نگه دارید. حجم را از ۴۰۰ به زیر ۱۵۰ کیلوبایت برسانید.
font-display: swapرا فعال کنید وpreloadرا روی فایل زیرمجموعهشده بگذارید.- فونت را روی سرور داخلی میزبانی کنید و هدر
cache-control: max-age=31536000را تنظیم کنید. - با
document.fonts.readyپرش چیدمان را مدیریت کنید. - در پایان، با PageSpeed Insights و Chrome DevTools اندازهگیری کنید که CLS زیر ۰.۱ و FCP زیر ۱.۸ ثانیه باشد.
اگر این مراحل را انجام دادید و هنوز CLS بالا بود، مشکل از فونت نیست. احتمالاً تصاویر یا المانهای دیگر صفحه بدون ابعاد مشخص رندر میشوند. راهنمای رفع پرش چیدمان CLS در سایت فارسی را بخوانید و سراغ آنها بروید.
یک نکتهٔ نهایی: فونت را برای همیشه روی سرور خودتان نگه ندارید. هر چند ماه یک بار نسخهٔ جدید فونت را بررسی کنید. فونتهای فارسی مثل وزیرمتن بهطور مرتب بهروزرسانی میشوند و نسخههای جدید معمولاً گلیفهای بهتری دارند و گاهی حجم کمتری. بهروزرسانی فونت، یک تغییر سادهٔ CSS است که ارزشش را دارد.
اگر بهدنبال بهبود کلی سرعت سایت هستید، راهنمای افزایش سرعت سایت از کلیک تا رندر را مطالعه کنید. و اگر این بهینهسازیها برایتان زمانبر است، تیم خدمات سئو میتواند این کار را برای شما انجام دهد.
پرسشهای پرتکرار
چرا فونت فارسی سایت من دیر بارگذاری میشود؟
دلایل اصلی سهگانه است: حجم بالای فایل فونت، نبود زیرمجموعهسازی، و میزبانی فونت روی سرور خارجی. فونت کامل فارسی معمولاً بالای ۴۰۰ کیلوبایت است و اگر روی CDN خارجی باشد، هر بار باید از مرز عبور کند. با زیرمجموعهسازی و میزبانی داخلی، زمان بارگذاری معمولاً به یکسوم کاهش مییابد.
font-display: swap بهتر است یا optional؟
اگر فونت فارسی برای هویت برند و خوانایی سایت شما حیاتی است، swap را انتخاب کنید و پرش چیدمان را با تکنیکهای دیگر مدیریت کنید. optional فقط وقتی مناسب است که فونت یک بهبود تزئینی باشد و نبودش به تجربهٔ کاربری آسیب نزند. در سایتهای خبری و فروشگاهی فارسی، معمولاً swap انتخاب درست است.
زیرمجموعهسازی فونت فارسی چه حجمی را کم میکند؟
یک فونت کامل فارسی با ۶۰۰ گلیف معمولاً ۴۰۰ تا ۷۰۰ کیلوبایت حجم دارد. با نگه داشتن فقط گلیفهای عربی و فارسی، حجم به ۱۲۰ تا ۱۵۰ کیلوبایت میرسد. اگر فقط کاراکترهای پرتکرار را نگه دارید، حجم به ۳۰ تا ۴۰ کیلوبایت کاهش مییابد. این کاهش معمولاً ۷۰ تا ۹۰ درصد است.
چطور بفهمم فونت سایت من باعث پرش چیدمان میشود؟
در Chrome DevTools، تب Performance را باز کنید و صفحه را با throttling روی Slow 4G بارگذاری کنید. اگر در ضبط، نوار Layout Shift را ببینید که همزمان با بارگذاری فونت فعال میشود، فونت مقصر است. همچنین میتوانید فونت را موقتاً غیرفعال کنید و CLS را دوباره اندازه بگیرید. اگر CLS به زیر ۰.۰۵ رسید، فونت عامل اصلی است.
دیدگاهها ۰
هنوز دیدگاهی ثبت نشده؛ اولین نفر باشید!