Product Updates إضافة Statnive · Parhum Khoshbakht

كيف هندسنا Statnive لحمل أداء منخفض

ثلاثة تغييرات معمارية — التحميل async، tracker النواة inline، وidle callbacks — قطعت تأثير LCP في معيار Statnive إلى النصف. إليك القصّة الهندسية والتحفّظات الصادقة.

من المركز الأخير إلى أدنى حمل في اختبارنا

عندما قِسنا Statnive لأوّل مرّة مقابل 7 plugins تحليلات WordPress أخرى، كانت النتائج مُذِلّة. كان TTFB لدينا ممتازًا — رابع أسرع. لكن Largest Contentful Paint لدينا احتلّ المرتبة الأخيرة بين الـ plugins المستضافة ذاتيًا. الفجوة بين استجابة خادمنا ومتى يرى الزوّار المحتوى فعلًا كانت 202 مللي ثانية. حقّق Koko Analytics 94 مللي ثانية. حقّق Burst Statistics 80 مللي ثانية. كنّا عند 202 مللي ثانية.

المشكلة لم تكن في كود الـ tracker نفسه. كانت في كيفية تحميل WordPress له.

بحلول نهاية اليوم، قطعنا تلك الفجوة إلى 79 مللي ثانية. انخفض LCP من 504 مللي ثانية إلى 288 مللي ثانية — تحسين بنسبة 43%. تحت حمل خفيف، وصلنا إلى تعادل في المركز الثاني. تحت اختبار ضغط اصطناعي تابع (50 مستخدم HTTP متزامن، دون تخزين مؤقت للصفحات)، كان لـ Statnive أدنى حمل LCP من بين الـ 8 plugins التي قِسناها. إليك ما غيّرناه ولماذا — بالإضافة إلى تحفّظات صادقة بشأن ما تعنيه أرقام المعيار وما لا تعنيه.

المعيار: 8 plugins، Chromium حقيقي، حمل حقيقي

بنينا إطار اختبار آلي يبدّل plugins التحليلات عبر REST API الخاص بـ WordPress، ويشغّل زيارات متصفّح Chromium حقيقية عبر k6، ويجمع Core Web Vitals من خلال PerformanceObserver. يشتغل كل plugin في عزلة كاملة — كل plugins التحليلات الأخرى معطَّلة، التخزينات المؤقتة مفرَّغة، الخادم محمَّى بـ 5 طلبات قبل بدء القياس.

النتائج قبل وبعد:

المقياسقبلبعدالتغيّر
Statnive TTFB294ms209ms-29%
Statnive FCP496ms288ms-42%
Statnive LCP504ms288ms-43%
فجوة TTFB-إلى-LCP202ms79ms-61%
الترتيب (LCP)#7 من 8#2 (تعادل)+5 مواقع

السبب الجذري: ثلاثة قتلة أداء

تتبّعنا فجوة 202 مللي ثانية إلى ثلاث مشكلات في FrontendHandler.php، كلّ منها مؤكَّدة بشكل مستقلّ من وثائق WordPress Core وأبحاث أداء الويب.

المشكلة 1: wp_localize_script يُجبر على وضع الحجب. قدّم WordPress 6.3 دعمًا أصيلًا لـ async/defer عبر معامل strategy. لكن wp_localize_script() يولّد نصًا inline في موضع «after»، الذي — حسب WordPress Core Trac #58632 — يجبر النص الأب على وضع الحجب ويتسلسل عبر شجرة الاعتماد بأكملها. كل نص في السلسلة يفقد استراتيجيّته async/defer.

المشكلة 2: لا سمة async أو defer. كان tracker لدينا مُهيَّأ بـ ['in_footer' => true] لكن دون معامل استراتيجية. حتى في footer، نص متزامن يحجب المتصفّح من إطلاق حدث 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. لـ trackers التحليلات التي لا تحتاج إلى وصول DOM، async أفضل من defer. ينتظر defer تحليل HTML الكامل (500 مللي ثانية+ على صفحات معقّدة). ينفّذ async بمجرّد اكتمال التنزيل. tracker لدينا يقرأ window.StatniveConfig ويُطلق navigator.sendBeacon() — لا أحدهما يتطلّب DOM.

خزّن تجزئة SRI في transient في WordPress. مفتاحًا بـ filemtime()، تُحسَب التجزئة مرّة وتُعاد استخدامها حتى يتغيّر الملف. بناء جديد = وقت تعديل جديد = إبطال تخزين مؤقت تلقائي.

المرحلة 2: تحرير الخيط الرئيسي

مع وجود التحميل async، التفتنا إلى JavaScript الـ tracker نفسه.

أزل غلاف DOMContentLoaded. مع async، ينفّذ النص بمجرّد تنزيله. يقرأ الـ tracker window وnavigator كـ globals — لا حاجة لـ DOM. كان مستمع حدث DOMContentLoaded يضيف تأخيرًا غير ضروري.

أجِّل الوحدات غير الحرجة عبر requestIdleCallback. إصابة مشاهدة الصفحة هي العملية الوحيدة في المسار الحرج. تتبّع التفاعل (عمق التمرير، الوقت على الصفحة)، التتبّع التلقائي (روابط خارجية، إرسالات نماذج)، وتتبّع أحداث CSS، كلّها يمكن أن تنتظر حتى يصبح المتصفّح خاملًا. دعم Safari requestIdleCallback بشكل أصيل منذ سبتمبر 2024، لذا لا حاجة لـ 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 على التنفيذ حتى أثناء تفاعل المستخدم، ممّا يمكن أن يسبّب تأتأة ويضرّ بدرجات INP. دع المتصفّح يقرّر متى يكون خاملًا فعلًا.

المرحلة 3: إلغاء الطلب الخارجي

التحسين النهائي يلغي تنزيل النص الخارجي من مسار التصيير الحرج تمامًا. مستلهَمين من كيفية استخدام Google’s gtag.js لـ inline bootstrap قائم على طابور، وكيفية تضمين Koko Analytics لـ tracker كامل بحجم 468 بايت inline، أنشأنا بنية ثنائية المرحلة.

المرحلة 1: tracker النواة inline (~1.7 KB خام / ~0.9 KB مضغوط بـ gzip). IIFE أدنى يقرأ الإعداد، يفحص إشارات الخصوصية (DNT/GPC)، يشغّل 4 heuristics لاكتشاف bot، يبني حمولة مشاهدة الصفحة، ويُطلقها عبر navigator.sendBeacon(). هذا مطبوع مباشرةً في HTML عبر wp_print_inline_script_tag() في wp_footer. صفر طلبات خارجية.

المرحلة 2: tracker كامل async (~5.5 KB خام / ~2.4 KB مضغوط بـ gzip). الـ tracker الكامل مع التفاعل والأحداث والتتبّع التلقائي وإدارة الموافقة يُحمَّل بـ strategy: 'async'. عندما يُهيَّأ، يفحص window.statnive_hit_sent — إذا أطلق inline core بالفعل مشاهدة الصفحة، يتخطّى مباشرةً إلى تهيئة الوحدات المؤجَّلة. لا إصابات مكرَّرة.

النتيجة: تُطلَق مشاهدة الصفحة من JavaScript inline قبل اكتمال أي مورد خارجي. مجموعة الميزات الكاملة تُحمَّل في الخلفية دون التأثير على أي Core Web Vital.

النتائج حسب المرحلة

كل مرحلة نُشرت وقِيست بشكل مستقلّ:

المرحلةالتغييرالفجوةLCP
قبل التحسيننص حاجب، لا استراتيجية202ms504ms
المرحلة 1: async + إعداد inlineتنزيل غير حاجب~80ms~374ms
المرحلة 2: requestIdleCallbackتحرير الخيط الرئيسي~65ms~359ms
المرحلة 3: tracker النواة inlineصفر طلبات خارجية79ms288ms

اختبار الضغط الاصطناعي: كيف تتصرّف البُنى تحت الحمل

الحمل الخفيف يستطيع إخفاء الفروق المعمارية. لاختبار ضغط كل الـ 8 plugins، أعدنا تشغيل المعيار مع 10 مستخدمي متصفّح Chromium يقيسون Core Web Vitals بينما يضغط 50 مستخدم HTTP متزامن على الخادم. لم يُثبَّت plugin تخزين مؤقت للصفحات. كل طلب اخترق مسار WordPress PHP الكامل — حالة مرَضية مصمَّمة لكشف أي plugins تتدهور تحت التنازع.

النتائج — حمل LCP مقابل الأساس في اختبار الضغط أحادي التشغيل لدينا، ~150 عيّنة لكل plugin:

الترتيبPluginLCP ΔImpact Score
1Statnive+260ms6.7
2Independent Analytics+566ms14.2
3Jetpack+776ms19.5
4MonsterInsights (GA4)+964ms24.1
5WP Slimstat+1030ms25.4
6WP Statistics+1424ms35.9
7Koko Analytics+2278ms56.3
8Burst Statistics+3592ms89.6

هذه ليست أرقام إنتاج. هي من تشغيل واحد على جهاز مطوّر بدون تخزين مؤقت. موقع WordPress إنتاجي مع W3TC أو WP Rocket أو تخزين CDN للصفحات سيُظهر فروقًا أصغر بكثير لأن الصفحات المخزّنة لا تنفّذ كود PHP الخاص بالتحليلات أبدًا. على الأرجح أن الفروق الكبيرة في LCP لـ Koko Analytics وBurst Statistics خصيصًا تعكس مشاكل تنازع خاصّة بالاختبار (معالجة دفعات WP-Cron، تسلسل كتابة قاعدة البيانات) بدلًا من حمل في حالة الاستقرار على موقع حقيقي.

ما يُظهره الاختبار هو أن بنية Statnive تُبقي مسار التصيير الحرج خاليًا بصرف النظر عن التنازع من جانب الخادم: tracker النواة inline يُطلق navigator.sendBeacon() قبل أن يحدث أي عمل خادم، لذا تُلتقط مشاهدة الصفحة حتى لو كانت قاعدة البيانات تحت حمل ثقيل. الفوز المعماري هو القصّة — لا المضاعفات المحدّدة. شغّل الاختبار على عتادك الخاص قبل استخلاص الاستنتاجات حول إعدادك المحدّد.

قرارات مدعومة بالأبحاث

تأكَّد كل قرار تقني مقابل أبحاث منشورة ووثائق رسمية. استشرنا أكثر من 100 مصدر عبر تذاكر WordPress Core Trac، وأدلّة أداء web.dev، ومواصفات W3C، ودراسات موثوقية الإنتاج. النتائج الرئيسية التي شكّلت نهجنا:

  • wp_add_inline_script('before') موثَّق صراحةً كآمن مع استراتيجيات async/defer (Make WordPress Core، يوليو 2023)
  • حقن النص عبر createElement أبطأ بـ 2.1 ثانية من <script async> الأصيل لأنّه يتجاوز مُسبق المسح في المتصفّح (Ilya Grigorik، Google)
  • navigator.sendBeacon() يحقّق موثوقية تسليم 95.8-98% عند اقترانه بأحداث visibilitychange وpagehide (دراسة إنتاج NicJ.net، 2 مليون+ مشاهدة صفحة)
  • تحليل/ترجمة JavaScript على الهاتف أبطأ بـ 2-5 مرّات من سطح المكتب، لكن عند ~2.4 KB مضغوط بـ gzip فإن tracker لدينا أقلّ بكثير من عتبة 50KB حيث يصبح التقسيم ضروريًا (Addy Osmani، Google)

أسئلة شائعة

هل يعمل tracker النواة inline مع سياسات أمن المحتوى؟

نعم. wp_print_inline_script_tag() يحترم filter wp_inline_script_attributes في WordPress، الذي يستطيع إضافة nonce لامتثال CSP. النص inline مولَّد من جانب الخادم ولا يحتوي على مدخلات مستخدم.

ماذا يحدث إذا فشل tracker الكامل async في التحميل؟

مشاهدة الصفحة مسجَّلة بالفعل بواسطة inline core. تخسر تتبّع التفاعل والأحداث لتلك الجلسة، لكن البيانات الأساسية للتحليلات مُلتقَطة. هذا تدهور رقيق — أهمّ مقياس (مشاهدة الصفحة) لديه أكثر تسليم موثوق.

لماذا async بدلًا من defer لـ tracker الكامل؟

ينتظر defer اكتمال تحليل HTML قبل التنفيذ. لـ tracker تحليلات لا يتلاعب بـ DOM، هذا تأخير غير ضروري. async يُنزِّل بالتوازي وينفّذ فورًا. نص inline 'before' يضمن توفّر StatniveConfig قبل تشغيل النص async.

هل سيعمل هذا النهج على إصدارات WordPress قبل 6.3؟

معامل strategy يتطلّب WordPress 6.3+. على الإصدارات الأقدم، يُتجاهَل المعامل بصمت ويُحمَّل النص كنص footer قياسي — لا يزال يعمل، فقط دون تحسين async. الحدّ الأدنى المعلَن لـ Statnive هو WordPress 6.2 — المواقع التي تشغّل 6.2 لا تزال تحصل على الـ plugin، فقط دون استراتيجية async.

ما التالي

احتلّ tracker لدينا المركز الأوّل في اختبار الضغط الاصطناعي لدينا، لكن معيارًا أحادي التشغيل ليس الشيء ذاته كمثبَت في الإنتاج. مجالات التحقيق التالية:

  • معيار متعدّد التشغيل مع تقرير التباين: شغّل اختبار الفئة الثقيلة 5× مع ترتيب إعداد عشوائي وأبلغ الوسيط مع المدى الربعي بدلًا من وسطاء أحادي التشغيل
  • معيار مع تفعيل تخزين مؤقت للصفحات: اختبر كل الـ plugins إلى جانب W3TC وWP Rocket لإظهار كيف تبدو المقارنة في إعداد إنتاجي واقعي
  • التحقّق المستقلّ: الإطار بأكمله مفتوح المصدر — نودّ أن نرى أطرافًا ثالثة تشغّله وتنشر نتائجها الخاصّة
  • متغيّرات ميزات وقت الترجمة (نموذج Plausible): توليد بنى tracker مختلفة بناءً على الميزات المفعَّلة، بحيث تحصل المواقع التي لا تستخدم تتبّع التفاعل على نص أصغر
  • استمرار Service Worker: اطّابر الأحداث في service worker لموثوقية التسليم حتى على اتصالات هاتف متقطّعة
  • خفض TTFB من جانب الخادم: حلّل نقطة نهاية إصابة PHP لحلق المللي ثانية من استجابة الخادم

الأداء ليس ميزة تشحنها مرّة واحدة. هو انضباط تمارسه على كل إصدار — والقياس الصادق جزء من ذلك الانضباط.

شاهد كيف يقارن أداء Statnive بـ Google Analytics وMonsterInsights وplugins تحليلات WordPress أخرى. أو استكشف كل ميزات Statnive.

Get Statnive Free