چگونه Statnive را برای سربار عملکردی کم مهندسی کردیم
سه تغییر معماری — بارگذاری async، tracker هسته inline و idle callbackها — تأثیر LCP benchmark Statnive را نصف کرد. اینجا داستان مهندسی و ملاحظات صادقانه.
از آخرین جای به کمترین سربار در آزمون ما
وقتی برای اولین بار Statnive را در برابر 7 plugin تحلیلی WordPress دیگر benchmark کردیم، نتایج فروتن کننده بودند. TTFB ما عالی بود — 4 سریعترین. اما Largest Contentful Paint ما در میان پلاگینهای خود-میزبان آخرین رتبه را داشت. شکاف بین پاسخ سرور ما و زمانی که بازدیدکنندگان واقعاً محتوا را میدیدند 202 میلیثانیه بود. Koko Analytics به 94ms رسید. Burst Statistics به 80ms رسید. ما در 202ms بودیم.
مشکل خود کد tracker نبود. این بود که WordPress چگونه آن را بارگذاری میکرد.
تا پایان روز، آن شکاف را به 79 میلیثانیه کاهش داده بودیم. LCP از 504ms به 288ms کاهش یافت — بهبود 43%. تحت بار سبک، به یک تساوی برای رتبه دوم رسیدیم. تحت یک آزمون فشار مصنوعی پیگیری (50 کاربر HTTP همزمان، بدون page caching)، Statnive کمترین سربار LCP از 8 پلاگینی که اندازه گرفتیم را داشت. اینجا آنچه تغییر دادیم و چرا — بهعلاوه ملاحظات صادقانه درباره آنچه آن اعداد benchmark معنا میدهند و نمیدهند.
Benchmark: 8 plugin، Chromium واقعی، بار واقعی
ما یک framework آزمون خودکار ساختیم که پلاگینهای تحلیلی را از طریق REST API WordPress تغییر میدهد، بازدیدهای مرورگر Chromium واقعی را از طریق k6 اجرا میکند، و Core Web Vitals را از طریق PerformanceObserver جمعآوری میکند. هر plugin در ایزولاسیون کامل اجرا میشود — همه پلاگینهای تحلیلی دیگر غیرفعال شدهاند، cacheها flush شدهاند، سرور با 5 درخواست قبل از شروع اندازهگیری warmup شده است.
نتایج قبل-و-بعد:
| متریک | قبل | بعد | تغییر |
|---|---|---|---|
| Statnive TTFB | 294ms | 209ms | -29% |
| Statnive FCP | 496ms | 288ms | -42% |
| Statnive LCP | 504ms | 288ms | -43% |
| شکاف TTFB-به-LCP | 202ms | 79ms | -61% |
| رتبهبندی (LCP) | #7 از 8 | #2 (تساوی) | +5 موقعیت |
ریشه: سه قاتل عملکرد
ما شکاف 202ms را به سه مسئله در FrontendHandler.php ردیابی کردیم، که هر یک بهطور مستقل توسط مستندات هسته WordPress و تحقیق عملکرد وب تأیید شد.
مشکل 1: wp_localize_script حالت مسدود کننده را اجبار میکند. WordPress 6.3 پشتیبانی بومی async/defer را از طریق پارامتر strategy معرفی کرد. اما wp_localize_script() یک script inline در موقعیت “after” تولید میکند، که — طبق Trac WordPress Core #58632 — script والد را به حالت مسدود کننده اجبار میکند و در طول کل درخت وابستگی پیدرپی میشود. هر script در زنجیره استراتژی async/defer خود را از دست میدهد.
مشکل 2: بدون ویژگی async یا defer. tracker ما با ['in_footer' => true] اما بدون پارامتر strategy enqueue شده بود. حتی در footer، یک script synchronous مرورگر را از شلیک رویداد load تا تکمیل دانلود و اجرا مسدود میکند.
مشکل 3: هش SRI در هر بارگذاری صفحه محاسبه میشد. ما file_get_contents() + hash('sha256', ...) را در هر درخواست صفحه منفرد فراخوانی میکردیم تا هش Subresource Integrity را تولید کنیم. آن یک خواندن filesystem بهعلاوه هشسازی CPU-فشرده در هر بازدیدکننده است.
Statnive را بگیرید: تحلیلگری خود-میزبان عملکرد-محور
همه بهینهسازیهای توصیف شده اینجا با Statnive امروز عرضه میشوند. رایگان از WordPress.org نصب کنید — دادههای شما روی سرور شما باقی میماند، صفحات شما سریع باقی میمانند.
فاز 1: استراتژی بارگذاری را اصلاح کنید
بزرگترین برد منفرد از سه تغییر در FrontendHandler.php آمد:
جایگزینی wp_localize_script با wp_add_inline_script('before'). موقعیت 'before' بحرانی است — به حالت مسدود کننده پیدرپی نمیشود. موقعیت 'after' (که پیشفرض است) پیدرپی میشود. این تمایز در اعلام بارگذاری script رسمی 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. برای trackerهای تحلیلی که به دسترسی DOM نیاز ندارند، async بهتر از defer است. Defer منتظر تجزیه کامل HTML میماند (500ms+ روی صفحات پیچیده). Async به محض تکمیل دانلود اجرا میشود. tracker ما window.StatniveConfig را میخواند و navigator.sendBeacon() را شلیک میکند — هیچکدام به DOM نیاز ندارند.
کش کردن هش SRI در یک transient WordPress. کلیددار شده توسط filemtime()، هش یک بار محاسبه میشود و تا تغییر فایل دوباره استفاده میشود. Build جدید = زمان modification جدید = invalidation کش خودکار.
فاز 2: رشته اصلی را آزاد کنید
با بارگذاری async در جای خود، به خود JavaScript tracker روی آوردیم.
حذف wrapper DOMContentLoaded. با async، script به محض دانلود اجرا میشود. tracker window و navigator globalها را میخواند — بدون نیاز به DOM. listener رویداد DOMContentLoaded تأخیر غیرضروری اضافه میکرد.
موکول کردن ماژولهای غیربحرانی از طریق requestIdleCallback. بازدید pageview تنها عملیات مسیر-بحرانی است. ردیابی تعامل (عمق scroll، زمان روی صفحه)، auto-tracking (لینکهای خروجی، ارسال form) و ردیابی رویداد CSS همه میتوانند منتظر باشند تا مرورگر idle شود. 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 پاس ندهید. یک timeout اجرا را حتی در طول تعامل کاربر اجبار میکند، که میتواند باعث jank شود و به امتیازهای INP آسیب برساند. اجازه دهید مرورگر تصمیم بگیرد چه زمانی واقعاً idle است.
فاز 3: حذف درخواست خارجی
بهینهسازی نهایی دانلود script خارجی را از مسیر رندر بحرانی بهطور کامل حذف میکند. الهام گرفته از نحوه استفاده gtag.js Google از یک bootstrap inline مبتنی بر صف و نحوه inline کردن tracker 468 بایتی کامل توسط Koko Analytics، یک معماری دو-مرحلهای ایجاد کردیم.
مرحله 1: tracker هسته inline (1.1KB). یک IIFE حداقلی که config را میخواند، سیگنالهای حریم خصوصی (DNT/GPC) را بررسی میکند، 4 heuristic تشخیص ربات را اجرا میکند، payload pageview را میسازد و آن را از طریق navigator.sendBeacon() شلیک میکند. این مستقیماً در HTML از طریق wp_print_inline_script_tag() در wp_footer چاپ میشود. صفر درخواست خارجی.
مرحله 2: tracker کامل async (5KB). tracker کامل با تعامل، رویدادها، auto-tracking و مدیریت رضایت با strategy: 'async' بارگذاری میشود. وقتی initialize میشود، window.statnive_hit_sent را بررسی میکند — اگر هسته inline قبلاً pageview را شلیک کرده باشد، مستقیماً به initialization ماژول موکول شده میرود. بدون بازدیدهای تکراری.
نتیجه: pageview از JavaScript inline قبل از تکمیل بارگذاری هر منبع خارجی شلیک میشود. مجموعه ویژگی کامل در پسزمینه بدون تأثیر بر هیچ Core Web Vital بارگذاری میشود.
نتایج بر اساس فاز
هر فاز بهطور مستقل deploy و اندازهگیری شد:
| فاز | تغییر | شکاف | LCP |
|---|---|---|---|
| قبل از بهینهسازی | Script مسدود کننده، بدون strategy | 202ms | 504ms |
| فاز 1: async + config inline | دانلود غیرمسدود کننده | حدود 80ms | حدود 374ms |
| فاز 2: requestIdleCallback | رشته اصلی آزاد شد | حدود 65ms | حدود 359ms |
| فاز 3: tracker هسته inline | صفر درخواست خارجی | 79ms | 288ms |
آزمون فشار مصنوعی: چگونه معماریها تحت بار رفتار میکنند
بار سبک میتواند تفاوتهای معماری را پنهان کند. برای آزمون فشار همه 8 plugin، benchmark را با 10 کاربر مرورگر Chromium که Core Web Vitals را اندازه میگیرند در حالی که 50 کاربر HTTP همزمان سرور را میکوبیدند دوباره اجرا کردیم. هیچ page caching plugin نصب نشده بود. هر درخواست مسیر کامل PHP WordPress را طی میکرد — یک شرط پاتولوژیک طراحی شده برای آشکار کردن کدام پلاگینها تحت رقابت تنزل مییابند.
نتایج — سربار LCP در برابر baseline در آزمون فشار تکاجرایی ما، حدود 150 نمونه به ازای هر plugin:
| رتبه | Plugin | LCP Δ | امتیاز Impact |
|---|---|---|---|
| 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 |
اینها اعداد تولید نیستند. آنها از یک اجرای منفرد روی یک ماشین توسعهدهنده بدون caching هستند. یک سایت تولیدی WordPress با W3TC، WP Rocket یا یک page cache CDN تفاوتهای بسیار کوچکتری نشان میداد زیرا صفحات کششده هرگز کد PHP تحلیلی را اجرا نمیکنند. اختلافهای LCP بزرگ برای Koko Analytics و Burst Statistics بهخصوص، احتمالاً مسائل خاص-آزمون رقابت (پردازش دستهای WP-Cron، سریالسازی نوشتن پایگاه داده) را بهجای سربار حالت پایدار روی یک سایت واقعی منعکس میکنند.
آنچه آزمون نشان میدهد این است که معماری Statnive مسیر رندر بحرانی را بدون توجه به رقابت سمت سرور پاک نگه میدارد: هسته inline قبل از انجام هر کار سرور navigator.sendBeacon() را شلیک میکند، بنابراین pageview حتی اگر پایگاه داده تحت بار سنگین باشد ضبط میشود. بردهای معماری داستان هستند — نه ضرایب خاص. آزمون را روی سختافزار خودتان قبل از نتیجهگیری درباره راهاندازی خاص خود اجرا کنید.
تصمیمات تحقیق-محور
هر تصمیم فنی در برابر تحقیق منتشر شده و مستندات رسمی اعتبارسنجی شد. ما با بیش از 100 منبع در سراسر تیکتهای WordPress Core Trac، راهنماهای عملکرد web.dev، مشخصات W3C و مطالعات قابلیت اعتماد تولید مشورت کردیم. یافتههای کلیدی که رویکرد ما را شکل دادند:
wp_add_inline_script('before')صریحاً بهعنوان امن با استراتژیهای async/defer مستند است (Make WordPress Core، ژوئیه 2023)- تزریق script از طریق
createElement2.1 ثانیه کندتر از<script async>بومی است زیرا preload scanner مرورگر را دور میزند (Ilya Grigorik، Google) navigator.sendBeacon()به قابلیت اعتماد تحویل 95.8 تا 98% میرسد وقتی با رویدادهایvisibilitychangeوpagehideجفت شود (مطالعه تولید NicJ.net، 2 میلیون+ pageview)- تجزیه/کامپایل JavaScript موبایل 2 تا 5 برابر کندتر از دسکتاپ است، اما در 5KB tracker ما بهخوبی زیر آستانه 50KB است که در آن splitting ضروری میشود (Addy Osmani، Google)
پرسشهای متداول
آیا tracker هسته inline با سیاستهای امنیت محتوا کار میکند؟
بله. wp_print_inline_script_tag() فیلتر wp_inline_script_attributes WordPress را احترام میگذارد، که میتواند یک nonce برای انطباق CSP اضافه کند. script inline سمت سرور تولید میشود و حاوی هیچ ورودی کاربر نیست.
اگر tracker کامل async در بارگذاری شکست بخورد چه اتفاقی میافتد؟
pageview از قبل توسط هسته inline ضبط شده است. شما ردیابی تعامل و رویداد را برای آن نشست از دست میدهید، اما داده تحلیلی هسته ضبط شده است. این یک تنزل معتدل است — مهمترین متریک (pageview) قابل اعتمادترین تحویل را دارد.
چرا async به جای defer برای tracker کامل؟
Defer منتظر تجزیه کامل HTML قبل از اجرا میماند. برای یک tracker تحلیلی که DOM را دستکاری نمیکند، این تأخیر غیرضروری است. Async بهطور موازی دانلود میکند و فوراً اجرا میکند. script 'before' inline تضمین میکند StatniveConfig قبل از اجرای script async در دسترس است.
آیا این رویکرد روی نسخههای WordPress قبل از 6.3 کار میکند؟
پارامتر strategy به WordPress 6.3+ نیاز دارد. روی نسخههای قدیمیتر، پارامتر بیصدا نادیده گرفته میشود و script بهعنوان یک script footer استاندارد بارگذاری میشود — همچنان عملکردی، فقط بدون بهینهسازی async. Statnive به WordPress 6.4+ نیاز دارد.
چه چیزی بعد است
tracker ما در آزمون فشار مصنوعی ما اول قرار گرفت، اما یک benchmark تکاجرایی همان اثبات-تولید نیست. حوزههای بعدی تحقیقات:
- Benchmark چند-اجرایی با گزارش variance: آزمون tier سنگین را 5 بار با ترتیب config تصادفی اجرا کنید و میانه بهعلاوه دامنه میانچارکی را به جای میانههای تکاجرایی گزارش کنید
- Benchmark با page caching فعال: همه پلاگینها را در کنار W3TC و WP Rocket آزمون کنید تا نشان دهید مقایسه در یک راهاندازی تولید واقعبینانه چگونه بهنظر میرسد
- راستیآزمایی مستقل: کل framework متنباز است — ما خوشحال خواهیم شد ببینیم اشخاص ثالث آن را اجرا میکنند و نتایج خود را منتشر میکنند
- انواع compile-time ویژگی (مدل Plausible): buildهای tracker متفاوت بر اساس ویژگیهای فعال شده تولید کنید، بنابراین سایتهایی که از ردیابی تعامل استفاده نمیکنند یک script حتی کوچکتر میگیرند
- پایداری Service Worker: رویدادها را در یک service worker برای قابلیت اعتماد تحویل حتی روی اتصالات موبایل ناپایدار صف کنید
- کاهش TTFB سمت سرور: endpoint بازدید PHP را پروفایل کنید تا میلیثانیهها را از پاسخ سرور تراش دهید
عملکرد یک ویژگی نیست که یک بار عرضه میکنید. یک انضباط است که در هر release تمرین میکنید — و اندازهگیری صادقانه بخشی از آن انضباط است.
ببینید عملکرد Statnive چگونه با Google Analytics، MonsterInsights و سایر پلاگینهای تحلیلی WordPress مقایسه میشود. یا همه ویژگیهای Statnive را بررسی کنید.