چطور Statnive را برای سربار عملکردی پایین مهندسی کردیم
سه تغییر معماری — بارگذاری ناهمگام، درج درونخطی هستهٔ ردیاب و فراخوانیهای زمان بیکاری — تأثیر LCP در آزمون عملکرد Statnive را نصف کرد. این داستان مهندسی، همراه با هشدارهای صادقانه است.
از جایگاه آخر تا کمترین سربار در آزمون ما
وقتی برای نخستین بار Statnive را در برابر 7 افزونهٔ تحلیل آماری دیگر WordPress سنجیدیم، نتایج فروتنانه بود. TTFB ما عالی بود — چهارمین سرعت برتر. اما Largest Contentful Paint ما در میان افزونههای خودمیزبان در قعر جدول قرار گرفت. فاصلهٔ میان پاسخ سرور ما و لحظهای که بازدیدکنندگان واقعاً محتوا را میدیدند، 202 میلیثانیه بود. Koko Analytics به 94ms رسید. Burst Statistics به 80ms رسید. ما روی 202ms بودیم.
مشکل، خودِ کد ردیاب نبود. مشکل این بود که WordPress چگونه آن را بارگذاری میکرد.
تا پایان آن روز، آن فاصله را به 79 میلیثانیه رسانده بودیم. LCP از 504ms به 288ms کاهش یافت — یعنی 43% بهبود. زیر بار سبک، به تساوی برای جایگاه دوم رسیدیم. در یک آزمون فشار مصنوعی بعدی (50 کاربر HTTP همزمان، بدون ذخیرهسازی صفحه)، Statnive کمترین سربار LCP را در میان 8 افزونهای که سنجیدیم داشت. در ادامه میبینید که چه چیزی را تغییر دادیم و چرا — بهعلاوهٔ هشدارهای صادقانه دربارهٔ اینکه آن اعداد آزمون چه معنایی میدهند و چه معنایی نمیدهند.
آزمون عملکرد: 8 افزونه، Chromium واقعی، بار واقعی
ما یک چارچوب آزمون خودکار ساختیم که افزونههای تحلیل آماری را از طریق API REST وردپرس روشن و خاموش میکند، بازدیدهای مرورگر واقعی Chromium را با k6 اجرا میکند و Core Web Vitals را از طریق PerformanceObserver جمع میکند. هر افزونه در انزوای کامل اجرا میشود — همهٔ افزونههای تحلیل آماری دیگر غیرفعال، حافظههای نهان پاکشده، و سرور پیش از آغاز سنجش با 5 درخواست گرمشده است.
نتایج پیش و پس:
| سنجه | پیش | پس | تغییر |
|---|---|---|---|
| TTFB در Statnive | 294ms | 209ms | -29% |
| FCP در Statnive | 496ms | 288ms | -42% |
| LCP در Statnive | 504ms | 288ms | -43% |
| فاصلهٔ TTFB تا LCP | 202ms | 79ms | -61% |
| رتبه (LCP) | #7 از 8 | #2 (مساوی) | +5 جایگاه |
ریشهٔ مشکل: سه قاتل عملکرد
ما آن فاصلهٔ 202ms را به سه مشکل در FrontendHandler.php ردیابی کردیم که هر کدام بهطور مستقل با مستندات هستهٔ WordPress و پژوهشهای عملکرد وب تأیید شدهاند.
مشکل 1: wp_localize_script حالت مسدودکننده را تحمیل میکند. وردپرس 6.3 پشتیبانی بومی async/defer را از طریق پارامتر strategy معرفی کرد. اما wp_localize_script() یک اسکریپت درونخطی در موقعیت «after» تولید میکند که — بنا بر WordPress Core Trac #58632 — اسکریپت والد را به حالت مسدودکننده وادار میکند و این اثر در سراسر درخت وابستگی آبشاری میشود. هر اسکریپت در این زنجیره راهبرد async/defer خود را از دست میدهد.
مشکل 2: نبودِ ویژگی async یا defer. ردیاب ما با ['in_footer' => true] در صف قرار گرفته بود اما هیچ پارامتر راهبردی نداشت. حتی در پاورقی، یک اسکریپت همگام، مرورگر را از فراخوانی رویداد load تا کاملشدن دانلود و اجرا بازمیدارد.
مشکل 3: محاسبهٔ درهمسازی SRI در هر بارگذاری صفحه. ما در هر درخواست صفحه file_get_contents() بهعلاوهٔ hash('sha256', ...) را فرامیخواندیم تا درهمسازی Subresource Integrity را تولید کنیم. این یعنی یک خواندن از سیستم فایل بهعلاوهٔ یک درهمسازی پرمصرف برای پردازنده، بهازای هر بازدیدکننده.
Statnive را دریافت کنید: تحلیل آماری خودمیزبان با اولویت عملکرد
همهٔ بهینهسازیهایی که اینجا توضیح داده شد، امروز همراه Statnive عرضه میشوند. رایگان از WordPress.org نصب کنید — دادههای شما روی سرور خودتان میماند و صفحههایتان سریع باقی میمانند.
فاز 1: راهبرد بارگذاری را اصلاح کنید
بزرگترین دستاورد از سه تغییر در FrontendHandler.php به دست آمد:
جایگزینی wp_localize_script با wp_add_inline_script('before'). موقعیت 'before' حیاتی است — به حالت مسدودکننده آبشاری نمیشود. موقعیت 'after' (که پیشفرض است) آبشاری میشود. این تمایز در اعلامیهٔ رسمی بارگذاری اسکریپت در WordPress 6.3 مستند شده، اما بهراحتی از چشم میافتد.
// Before (forces blocking):
wp_localize_script( 'statnive-tracker', 'StatniveConfig', $config );
// After (safe with async):
wp_add_inline_script(
'statnive-tracker',
'window.StatniveConfig=' . wp_json_encode( $config ) . ';',
'before' // MUST be 'before' — 'after' cascades to blocking
);
افزودن strategy: 'async' به wp_enqueue_script. برای ردیابهای تحلیل آماری که به دسترسی DOM نیاز ندارند، async بهتر از defer است. defer منتظر تجزیهٔ کامل HTML میماند (بیش از 500ms روی صفحههای پیچیده). async بهمحض کاملشدن دانلود اجرا میشود. ردیاب ما window.StatniveConfig را میخواند و navigator.sendBeacon() را فرامیخواند — هیچکدام به DOM نیاز ندارند.
ذخیرهٔ درهمسازی SRI در یک transient وردپرس. با کلیدی بر پایهٔ filemtime()، درهمسازی یکبار محاسبه و تا تغییر فایل دوباره استفاده میشود. بیلد جدید = زمان تغییر جدید = بیاعتبارسازی خودکار حافظهٔ نهان.
فاز 2: نخ اصلی را آزاد کنید
با برقراری بارگذاری ناهمگام، به سراغ خودِ JavaScript ردیاب رفتیم.
حذف پوشش DOMContentLoaded. با async، اسکریپت بهمحض دانلود اجرا میشود. ردیاب سراسریهای window و navigator را میخواند — به DOM نیازی نیست. شنوندهٔ رویداد DOMContentLoaded تأخیر غیرضروری اضافه میکرد.
به تعویق انداختن ماژولهای غیربحرانی از طریق requestIdleCallback. ثبت بازدید صفحه تنها عملیات مسیر بحرانی است. ردیابی تعامل (عمق پیمایش، زمان روی صفحه)، ردیابی خودکار (پیوندهای خروجی، ارسال فرمها) و ردیابی رویداد CSS، همگی میتوانند منتظر بمانند تا مرورگر بیکار شود. Safari از سپتامبر 2024 بهطور بومی از requestIdleCallback پشتیبانی میکند، بنابراین برای مرورگرهای امروزی به هیچ polyfill نیازی نیست.
// Critical path: fires immediately
sendHit(buildPayload());
// Deferred: runs when browser is idle
var idle = window.requestIdleCallback || function(cb) { setTimeout(cb, 80); };
idle(function() {
engagementTracker.start();
registerAutoTracking(sendEvent);
});
نکتهٔ کلیدی پژوهش ما این بود: پارامتر timeout را به requestIdleCallback پاس ندهید. یک مهلت زمانی، اجرا را حتی هنگام تعامل کاربر تحمیل میکند که میتواند باعث لرزش شود و به امتیاز INP آسیب بزند. بگذارید مرورگر تصمیم بگیرد که واقعاً کِی بیکار است.
فاز 3: درخواست بیرونی را حذف کنید
بهینهسازی پایانی، دانلود اسکریپت بیرونی را بهطور کامل از مسیر رندر بحرانی حذف میکند. با الهام از اینکه gtag.js گوگل چگونه از یک بوتاسترپ درونخطی صفمحور استفاده میکند و اینکه Koko Analytics چگونه کل ردیاب 468 بایتی خود را درونخطی میکند، یک معماری دومرحلهای ساختیم.
مرحلهٔ 1: هستهٔ ردیاب درونخطی (حدود 1.7 KB خام / حدود 0.9 KB با gzip). یک IIFE کمینه که پیکربندی را میخواند، نشانههای حریم خصوصی (DNT/GPC) را بررسی میکند، 4 روش اکتشافی تشخیص ربات را اجرا میکند، بار دادهٔ بازدید صفحه را میسازد و آن را از طریق navigator.sendBeacon() میفرستد. این مستقیماً از طریق wp_print_inline_script_tag() در wp_footer درون HTML چاپ میشود. صفر درخواست بیرونی.
مرحلهٔ 2: ردیاب کامل ناهمگام (حدود 5.5 KB خام / حدود 2.4 KB با gzip). ردیاب کامل با ردیابی تعامل، رویدادها، ردیابی خودکار و مدیریت رضایت، با strategy: 'async' بارگذاری میشود. هنگام مقداردهی اولیه، window.statnive_hit_sent را بررسی میکند — اگر هستهٔ درونخطی پیشتر بازدید صفحه را فرستاده باشد، مستقیم به مقداردهی ماژولهای بهتعویقافتاده میرود. هیچ ثبت تکراریای رخ نمیدهد.
نتیجه: بازدید صفحه از JavaScript درونخطی فرستاده میشود، پیش از آنکه هر منبع بیرونی بارگذاریاش کامل شود. مجموعهٔ کامل امکانات در پسزمینه بارگذاری میشود بدون آنکه روی هیچیک از Core Web Vital اثر بگذارد.
نتایج به تفکیک فاز
هر فاز بهطور مستقل مستقر و سنجیده شد:
| فاز | تغییر | فاصله | LCP |
|---|---|---|---|
| پیش از بهینهسازی | اسکریپت مسدودکننده، بدون راهبرد | 202ms | 504ms |
| فاز 1: async + پیکربندی درونخطی | دانلود بدون مسدودسازی | حدود 80ms | حدود 374ms |
| فاز 2: requestIdleCallback | آزادسازی نخ اصلی | حدود 65ms | حدود 359ms |
| فاز 3: هستهٔ ردیاب درونخطی | صفر درخواست بیرونی | 79ms | 288ms |
آزمون فشار مصنوعی: معماریها زیر بار چگونه رفتار میکنند
بار سبک میتواند تفاوتهای معماری را پنهان کند. برای آزمون فشار روی هر 8 افزونه، آزمون عملکرد را با 10 کاربر مرورگر Chromium که Core Web Vitals را میسنجیدند بازاجرا کردیم، در حالی که 50 کاربر HTTP همزمان به سرور فشار میآوردند. هیچ افزونهٔ ذخیرهسازی صفحه نصب نشده بود. هر درخواست از کل مسیر PHP وردپرس عبور میکرد — شرایطی بیمارگونه که برای آشکارکردن اینکه کدام افزونهها زیر تنش افت میکنند طراحی شده بود.
نتایج — سربار LCP در برابر خط مبنا در آزمون فشار تکاجرای ما، حدود 150 نمونه بهازای هر افزونه:
| رتبه | افزونه | LCP Δ | امتیاز تأثیر |
|---|---|---|---|
| 1 | Statnive | +260ms | 6.7 |
| 2 | Independent Analytics | +566ms | 14.2 |
| 3 | Jetpack | +776ms | 19.5 |
| 4 | MonsterInsights (GA4) | +964ms | 24.1 |
| 5 | WP Slimstat | +1030ms | 25.4 |
| 6 | WP Statistics | +1424ms | 35.9 |
| 7 | Koko Analytics | +2278ms | 56.3 |
| 8 | Burst Statistics | +3592ms | 89.6 |
اینها اعداد محیط تولید نیستند. اینها از یک اجرای تنها روی یک ماشین توسعهدهنده بدون ذخیرهسازی به دست آمدهاند. یک سایت وردپرس در محیط تولید با W3TC، WP Rocket یا یک حافظهٔ نهان صفحه روی CDN، تفاوتهای بهمراتب کوچکتری نشان میداد، چون صفحههای ذخیرهشده اصلاً کد PHP تحلیل آماری را اجرا نمیکنند. بهویژه فاصلههای بزرگ LCP برای Koko Analytics و Burst Statistics احتمالاً تنشهای ویژهٔ این آزمون را بازتاب میدهند (پردازش دستهای WP-Cron، ترتیبیشدن نوشتن در پایگاه داده) نه سربار حالت پایدار روی یک سایت واقعی.
آنچه این آزمون نشان میدهد این است که معماری Statnive مسیر رندر بحرانی را فارغ از تنش سمت سرور پاک نگه میدارد: هستهٔ درونخطی navigator.sendBeacon() را پیش از هر کار سرور میفرستد، بنابراین بازدید صفحه حتی اگر پایگاه داده زیر بار سنگین باشد ثبت میشود. داستان اصلی، دستاوردهای معماری است — نه ضریبهای مشخص. آزمون را روی سختافزار خودتان اجرا کنید پیش از آنکه دربارهٔ تنظیمات خاص خود نتیجهگیری کنید.
تصمیمهای پژوهشمحور
هر تصمیم فنی در برابر پژوهشهای منتشرشده و مستندات رسمی اعتبارسنجی شد. ما بیش از 100 منبع را در میان تیکتهای WordPress Core Trac، راهنماهای عملکرد web.dev، مشخصات W3C و مطالعات قابلیت اطمینان محیط تولید بررسی کردیم. یافتههای کلیدی که رویکرد ما را شکل دادند:
-
wp_add_inline_script('before')صراحتاً بهعنوان امن در کنار راهبردهای async/defer مستند شده است (Make WordPress Core، ژوئیه 2023) - تزریق اسکریپت از طریق
createElement2.1 ثانیه کندتر از<script async>بومی است، چون از پویشگر پیشبارگذاری مرورگر عبور میکند (Ilya Grigorik، گوگل) -
navigator.sendBeacon()در کنار رویدادهایvisibilitychangeوpagehide، به قابلیت اطمینان تحویل 95.8 تا 98 درصدی میرسد (مطالعهٔ محیط تولید NicJ.net، بیش از 2 میلیون بازدید صفحه) - تجزیه و کامپایل JavaScript روی موبایل 2 تا 5 برابر کندتر از دسکتاپ است، اما ردیاب ما با حدود 2.4 KB با gzip، بهخوبی زیر آستانهٔ 50KB قرار دارد که در آن تقسیمکردن ضروری میشود (Addy Osmani، گوگل)
پرسشهای پرتکرار
آیا هستهٔ ردیاب درونخطی با خطمشیهای امنیت محتوا کار میکند؟
بله. wp_print_inline_script_tag() به فیلتر wp_inline_script_attributes وردپرس احترام میگذارد که میتواند برای سازگاری با CSP یک nonce اضافه کند. اسکریپت درونخطی سمت سرور تولید میشود و هیچ ورودی کاربری ندارد.
اگر ردیاب کامل ناهمگام در بارگذاری شکست بخورد چه میشود؟
بازدید صفحه پیشتر توسط هستهٔ درونخطی ثبت شده است. ردیابی تعامل و رویداد را برای آن نشست از دست میدهید، اما دادهٔ اصلی تحلیل آماری ثبت میشود. این یک افت آراسته است — مهمترین سنجه (بازدید صفحه) قابلاعتمادترین تحویل را دارد.
چرا async بهجای defer برای ردیاب کامل؟
defer پیش از اجرا منتظر تجزیهٔ کامل HTML میماند. برای یک ردیاب تحلیل آماری که DOM را دستکاری نمیکند، این تأخیری غیرضروری است. async بهموازات دانلود میکند و بیدرنگ اجرا میشود. اسکریپت درونخطی 'before' تضمین میکند که StatniveConfig پیش از اجرای اسکریپت ناهمگام در دسترس است.
آیا این رویکرد روی نسخههای وردپرس پیش از 6.3 کار میکند؟
پارامتر strategy به وردپرس 6.3 و بالاتر نیاز دارد. روی نسخههای قدیمیتر، این پارامتر بیصدا نادیده گرفته میشود و اسکریپت بهعنوان یک اسکریپت پاورقی استاندارد بارگذاری میشود — همچنان کارآمد، فقط بدون بهینهسازی async. حداقل نسخهٔ اعلامشدهٔ Statnive وردپرس 6.2 است — سایتهایی که 6.2 را اجرا میکنند همچنان افزونه را دریافت میکنند، فقط بدون راهبرد async.
گام بعدی چیست
ردیاب ما در آزمون فشار مصنوعی ما اول شد، اما یک آزمون عملکرد تکاجرا با اثباتشدن در محیط تولید یکی نیست. زمینههای بعدی بررسی:
- آزمون عملکرد چنداجرا با گزارش پراکندگی: آزمون ردهسنگین را 5 بار با ترتیب پیکربندی تصادفی اجرا کنیم و میانه بهعلاوهٔ دامنهٔ میانچارکی را گزارش دهیم، نه میانههای تکاجرا
- آزمون عملکرد با ذخیرهسازی صفحهٔ فعال: همهٔ افزونهها را در کنار W3TC و WP Rocket آزمون کنیم تا نشان دهیم مقایسه در یک تنظیمات واقعگرایانهٔ محیط تولید چگونه است
- تأیید مستقل: کل چارچوب متنباز است — خوشحال میشویم ببینیم اشخاص ثالث آن را اجرا و نتایج خودشان را منتشر کنند
- گونههای امکاناتِ زمان کامپایل (مدل Plausible): بیلدهای متفاوتی از ردیاب بر پایهٔ امکانات فعال تولید کنیم، تا سایتهایی که از ردیابی تعامل استفاده نمیکنند اسکریپتی حتی کوچکتر بگیرند
- ماندگاری Service Worker: رویدادها را در یک service worker در صف بگذاریم تا قابلیت اطمینان تحویل حتی روی اتصالهای ناپایدار موبایل حفظ شود
- کاهش TTFB سمت سرور: نقطهٔ پایانی ثبت در PHP را پروفایل کنیم تا چند میلیثانیه از پاسخ سرور کم کنیم
عملکرد، امکانی نیست که یکبار عرضه کنید. یک نظم است که در هر انتشار تمرین میکنید — و سنجش صادقانه بخشی از همین نظم است.
ببینید عملکرد Statnive چگونه با Google Analytics، MonsterInsights و دیگر افزونههای تحلیل آماری WordPress مقایسه میشود. یا همهٔ امکانات Statnive را کاوش کنید.