مشكلة الأداء
إذا رأيت في المنارة أكبر طلاء مثير للجدل والذي ينهار على الهاتف المحمول على الرغم من أن محتواك بسيط، فغالباً ما يكون هناك سبب بسيط: أنت تقوم بتحميل البرامج النصية والأنماط في كل مكان، حتى في الصفحات التي لا تحتاج إليها.
تكمن المشكلة في التراكم. شريط التمرير المستخدم على واحد فقط صفحة يقوم الموقع بحقن ملفات جافا سكريبت الخاصة به في جميع أنحاء الموقع. يقوم نموذج في صفحة "اتصل بنا" بتحميل مكتبته على الصفحة الرئيسية. وفي المواقع التي تستخدم Elementor أو Divi 5 أو Avada، لاحظتُ في كثير من الأحيان تحميل حزم "عامة" (رسوم متحركة، وعروض دوارة، ونوافذ منبثقة) حتى على الصفحات الثابتة التي لا تستخدم أيًا من وحداتها.
تأثير ملموس:
- عناصر الويب الأساسية المزيد من جافا سكريبت = المزيد من العمل على الخيط الرئيسي => TBT et INP تتدهور.
- تحسين محركات البحث (SEO) : يؤدي بطء LCP وضعف INP إلى انخفاض في مستوى الرؤية، خاصة على الأجهزة المحمولة.
- UX الصفحات التي "تتقطع"، والنقرات المتأخرة، والقوائم التي لا تستجيب بشكل صحيح.
في النهاية، ستعرف:
- تحديد الهوية بدقة ما هو المقبض WordPress الرسوم، وفي أي صفحات، وكم تكلفتها؟
- قم بإلغاء تحميل البرامج النصية/الأنماط بشكل صحيح بلا استراحة المحرر، أو ذاكرة التخزين المؤقت، أو أداة إنشاء الصفحات،
- أعد الشحن فقط عند الحاجة، وفي الوقت المناسب (
defer/asyncالوحدات النمطية، التحميل المسبق المستهدف).
ملخص سريع
- قم بالقياس أولاً: سجل المقابض المبلغ المدفوع فعلياً وتكلفته (الوجه الأمامي فقط).
- قم بالتفريغ باستخدام
wp_dequeue_script()/wp_deregister_script()etwp_dequeue_style()إلى الخطاف الجيد (wp_enqueue_scripts(أولوية عالية). - أعد تحميل الصفحات ذات الصلة فقط عبر الشروط (القالب، المعرف، الرابط، نوع المحتوى، وجود رمز مختصر، وجود كتلة، إلخ).
- إضافة
defer/asyncبواسطةscript_loader_tag(ليس عن طريق "التلاعب" بلغة HTML). - بالنسبة لـ Elementor/Divi/Avada: الشرط بواسطة حضور حقيقي أداة/وحدة (أو اللجوء إلى الكشف عن المحتوى) لتجنب النتائج الإيجابية الخاطئة.
- تحقق بعد كل تغيير: مبادرة التحصين الدولية/الحواجز التقنية أمام التجارة، وشلال، واختبار "منشئ الصفحات" (المحرر + الواجهة الأمامية).
رمز التشخيص
1) تفعيل التسجيل النظيف (دون إحداث أي خلل في بيئة الإنتاج)
في ووردبريس 6.9.4 و PHP 8.1 والإصدارات الأحدث، أفضل تسجيل البيانات في ملف بدلاً من عرضها. أضف هذا إلى wp-config.php (مؤقتا).
/**
* Diagnostic performance (temporaire)
* WordPress 6.9.4+ / PHP 8.1+
*/
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
/**
* Pratique pour comparer des mesures (ne pas laisser si vous n'en avez pas l'usage)
*/
define('SAVEQUERIES', true);
صعب : SAVEQUERIES يزيد من استهلاك الذاكرة ويبطئ الأداء. أقوم بتفعيله فقط لأغراض التشخيص، ثم أقوم بإيقافه.
2) سجل البرامج النصية/الأنماط التي تم تحميلها فعليًا (الواجهة الأمامية)
الهدف: الحصول على قائمة قابلة للاستخدام من مقابض و بهم SRCصفحةً صفحة. ضع هذا المقتطف في ملف صغير-المساعد (موصى به) بدلاً من في functions.php، لمنع تغيير المظهر من حذفه.
<?php
/**
* Plugin Name: Perf - Asset Map (diagnostic)
* Description: Log les scripts et styles en file d'attente sur le front.
* Version: 0.1.0
*/
if (!defined('ABSPATH')) {
exit;
}
add_action('wp_enqueue_scripts', function () {
// Ne logguez pas sur l'admin, ni sur les requêtes AJAX/REST
if (is_admin() || wp_doing_ajax() || (defined('REST_REQUEST') && REST_REQUEST)) {
return;
}
// Éviter de spammer les logs pour les visiteurs
if (!current_user_can('manage_options')) {
return;
}
global $wp_scripts, $wp_styles;
$url = home_url(add_query_arg([]));
$screen = is_singular() ? ('singular:' . get_post_type() . ':' . get_queried_object_id()) : 'non-singular';
$scripts = [];
if ($wp_scripts instanceof WP_Scripts) {
foreach ((array) $wp_scripts->queue as $handle) {
$src = isset($wp_scripts->registered[$handle]) ? (string) $wp_scripts->registered[$handle]->src : '';
$scripts[] = $handle . ' | ' . $src;
}
}
$styles = [];
if ($wp_styles instanceof WP_Styles) {
foreach ((array) $wp_styles->queue as $handle) {
$src = isset($wp_styles->registered[$handle]) ? (string) $wp_styles->registered[$handle]->src : '';
$styles[] = $handle . ' | ' . $src;
}
}
error_log('=== ASSET MAP ===');
error_log('URL: ' . $url);
error_log('Contexte: ' . $screen);
error_log('Scripts (' . count($scripts) . '): ' . implode(' || ', $scripts));
error_log('Styles (' . count($styles) . '): ' . implode(' || ', $styles));
error_log('=== /ASSET MAP ===');
}, 999);
فتح wp-content/debug.log وقارن بين "الصفحة الرئيسية" و"المقالة" و"اتصل بنا" و"أداة إنشاء الصفحات الثقيلة". ستحدد عمومًا ثلاث فئات:
- الأصول "العالمية" المشروعة (التنقل، والسمة، والكتل)،
- الأصول "العامة" غير الضرورية (شريط التمرير، صندوق الإضاءة، الرسوم المتحركة)،
- الأصول التي تم تحميلها بواسطة إضافة لا ينبغي تحميلها (النموذج، مكافحة البريد العشوائي، التتبع).
3) قياس وقت وضغط SQL (بدون أدوات خارجية)
لا يزال برنامج Query Monitor هو الأداة رقم 1 (وهو موثوق به)، لكنني أحب أن يكون لدي مؤقت دقيق قابل للتكرار في السجلات لتأكيد المكسب.
add_action('shutdown', function () {
if (is_admin() || !current_user_can('manage_options')) {
return;
}
$time_s = timer_stop(0, 5); // Temps total WordPress en secondes (string)
$mem_mb = round(memory_get_peak_usage(true) / 1024 / 1024, 1);
$queries = 0;
if (defined('SAVEQUERIES') && SAVEQUERIES && isset($GLOBALS['wpdb'])) {
$queries = (int) $GLOBALS['wpdb']->num_queries;
}
error_log(sprintf(
'PERF shutdown | url=%s | time=%ss | peak_mem=%sMB | queries=%d',
home_url(add_query_arg([])),
$time_s,
$mem_mb,
$queries
));
}, 9999);
هذا ليس "TTFB" حقيقيًا (فهو يشمل عملية العرض)، ولكنه مؤشر مستقر للمقارنة. قبل بعد على نفس الصفحة، وبنفس الشروط.
4) WP-CLI: التحقق السريع من ذاكرة التخزين المؤقت ومسحها
بعض الأوامر التي أستخدمها بشكل متكرر خلال هذا النوع من المشاريع:
# Vérifier versions
wp core version
wp plugin list --status=active
wp theme list
# Vider caches (si un plugin de cache expose wp-cli, sinon au moins object cache)
wp cache flush
# Recompiler les permaliens si un snippet touche les règles (rare ici, mais utile en dépannage)
wp rewrite flush --hard
5) سجل الاستعلامات البطيئة (إذا كنت تشك في أن أحد المكونات الإضافية يبطئ الأمور حتى قبل تحميل الأصول)
يُعدّ التحميل المشروط مفيدًا للغاية للواجهة الأمامية (JS/CSS). إذا كان وقت استجابة الخادم (TTFB) لديك بطيئًا بالفعل، فعّل تسجيل الاستعلامات البطيئة على جانب MySQL/MariaDB (أو استخدم قسم "الاستعلامات" في Query Monitor). يمكنك أيضًا تفعيل التسجيل البطيء على جانب PHP-FPM، ولكن هذا غالبًا ما يتجاوز إمكانيات الاستضافة المشتركة.
مصادر رسمية مفيدة:
- wp_enqueue_script ()
- wp_dequeue_script ()
- ربط wp_enqueue_scripts
- فلتر script_loader_tag
- الأداء (الإدارة المتقدمة)
الخطوة 1: تحديد الأصول غير الضرورية وتفريغها
يلجأ معظم الناس إلى "التحسين" عن طريق التخمين. وينتهي هذا الأمر بالتراجع: حيث يختفي نص برمجي في صفحة مهمة، أو ما هو أسوأ من ذلك، في المحرر.
أتبع دائماً خطوتين:
- رسم الخرائط : الحصول على المقابض الدقيقة (قسم التشخيص).
- تفريغ : قم بإزالة ما هو غير ضروري موضوعيًا فقط، مع وجود إجراء وقائي (لا تقم أبدًا بلمس admin/REST/AJAX).
مثال واقعي: يتم تحميل إضافة نموذج في كل مكان
الحالة الشائعة: يقوم مكون إضافي للنماذج (أو مكافحة البريد العشوائي) بتطبيق أصوله على الموقع بأكمله، على الرغم من أن لديك نموذجًا فقط في صفحة "اتصل بنا".
قبل (بطيء): قائمة الانتظار العامة
هذا النوع من التعليمات البرمجية شائع في الإضافات الصغيرة أو المقاطع البرمجية:
add_action('wp_enqueue_scripts', function () {
// Mauvais : chargé sur toutes les pages
wp_enqueue_style('mon-form', plugins_url('form.css', __FILE__), [], '1.0');
wp_enqueue_script('mon-form', plugins_url('form.js', __FILE__), ['jquery'], '1.0', true);
});
بعد (محسّن): إزالة قائمة الانتظار العامة + إعادة التحميل المشروط
إذا لم تكن تتحكم في الملحق، يمكنك تفريغ ثم تظهر مقابضها في معظم الصفحات، ثم إعادة تنشيط على الصفحة المستهدفة. النقطة الأساسية: اتخاذ إجراء بعد الإضافة (ذات أولوية عالية).
add_action('wp_enqueue_scripts', function () {
if (is_admin() || wp_doing_ajax() || (defined('REST_REQUEST') && REST_REQUEST)) {
return;
}
// Exemple : handles repérés via debug.log / Query Monitor
$style_handle = 'mon-form';
$script_handle = 'mon-form';
// Garder uniquement sur la page "Contact" (ID 42 ici)
$keep = is_page(42);
if (!$keep) {
// Retire de la file d'attente (si déjà enqueued)
wp_dequeue_style($style_handle);
wp_dequeue_script($script_handle);
// Optionnel : empêcher la réinscription plus tard dans la requête
wp_deregister_style($style_handle);
wp_deregister_script($script_handle);
}
}, 999);
لماذا هو أسرع؟ يتم تنزيل وتحليل ملفات CSS/JS بشكل أقل في جميع الصفحات. على الأجهزة المحمولة، غالبًا ما يكون تحليل ملفات JS هو الجزء المكلف (TBT/INP).
قياس التأثير (بسيط وقابل للتكرار)
قبل التغيير، يرجى ملاحظة ما يلي:
- عدد النصوص/الأنماط في سجل "خريطة الأصول" الخاص بك،
- الوقت
PERF shutdownبناءً على 3 أحمال (متوسطة)، - في Lighthouse: إجمالي JS، TBT، INP (بيانات الحقل إن وجدت).
بعد إجراء التغيير، كرر نفس الاختبارات تمامًا، مع مسح ذاكرة التخزين المؤقت للصفحة والمتصفح. في المواقع التي تعتمد بشكل كبير على الإضافات، غالبًا ما ألاحظ انخفاضًا في 50–300 كيلوبايت عدد من أكواد جافا سكريبت لكل صفحة و TBT الذي ينحدر من 100 - 400 مللي ثانية على الهاتف المحمول، دون الحاجة إلى الاتصال بالخادم.
الخطوة الثانية: تحميل البرنامج النصي فقط عند الحاجة إليه
يُعدّ تفريغ البيانات مفيدًا، لكنّ "التنظيف" الحقيقي يتطلّب تهيئة الاستعلام من البداية. إذا كان لديك تحكّم في قالبك الفرعي أو إضافتك، فافعل ذلك من المصدر.
الشرط حسب نوع الصفحة (is_page، is_singular، template)
مثال: مكتبة lightbox تُستخدم فقط في المقالات (وليس في الصفحات).
قبل: في كل مكان
add_action('wp_enqueue_scripts', function () {
wp_enqueue_script('lightbox', get_stylesheet_directory_uri() . '/assets/lightbox.js', [], '2.3.0', true);
wp_enqueue_style('lightbox', get_stylesheet_directory_uri() . '/assets/lightbox.css', [], '2.3.0');
});
بعد: على المنتجات فقط
add_action('wp_enqueue_scripts', function () {
if (!is_singular('post')) {
return;
}
wp_enqueue_script(
'lightbox',
get_stylesheet_directory_uri() . '/assets/lightbox.js',
[],
'2.3.0',
['in_footer' => true] // API moderne : évite le bool ambigu
);
wp_enqueue_style(
'lightbox',
get_stylesheet_directory_uri() . '/assets/lightbox.css',
[],
'2.3.0'
);
}, 20);
أصر على ['in_footer' => true] : إنه أكثر قابلية للقراءة، ويتجنب أخطاء "المعامل 5" عندما يقوم شخص ما بإعادة هيكلة الكود.
التكييف بناءً على وجود رمز مختصر (بدون نتائج إيجابية خاطئة)
كثيرون يفعلون ذلك has_shortcode(get_the_content(), 'gallery') إذا كان ذلك مبكراً جداً، أو خارج دائرة النقاش، فلن ينجح الأمر (أو قد ينجح "بالصدفة"). لذا، أقوم أنا بذلك بدلاً من ذلك:
- أقوم باسترداد المنشور الكامل في الصفحات الفردية.
- je teste
post_contentعلى وجه صحيح.
add_action('wp_enqueue_scripts', function () {
if (!is_singular()) {
return;
}
$post = get_queried_object();
if (!$post instanceof WP_Post) {
return;
}
// Exemple : script nécessaire uniquement si le shortcode [mon_slider] est présent
if (!has_shortcode($post->post_content, 'mon_slider')) {
return;
}
wp_enqueue_script(
'mon-slider',
get_stylesheet_directory_uri() . '/assets/mon-slider.js',
[],
'1.4.0',
['in_footer' => true]
);
}, 20);
إيدج سبي إذا كان مُنشئ الصفحات لديك يخزن المحتوى على شكل JSON متداخل/رموز مختصرة، فقد يفشل هذا الاختبار. في هذه الحالة، انتقل إلى اكتشاف "المنشئ" (الخطوة 4) أو استخدم علامة صريحة (meta، option، block).
مشروط بوجود كتلة (غوتنبرغ)
في ووردبريس 6.9.4، الخيار الأكثر فعالية هو استخدام has_block() بخصوص محتوى المنشور.
add_action('wp_enqueue_scripts', function () {
if (!is_singular()) {
return;
}
$post = get_queried_object();
if (!$post instanceof WP_Post) {
return;
}
// Exemple : script uniquement si le bloc "core/gallery" est utilisé
if (!has_block('core/gallery', $post)) {
return;
}
wp_enqueue_script(
'gallery-enhancements',
get_stylesheet_directory_uri() . '/assets/gallery.js',
[],
'1.0.0',
['in_footer' => true]
);
}, 20);
في تجربتي، هذا أحد أكثر التحسينات "أمانًا" على المواقع التحريرية الحديثة: لا حاجة للاحتفاظ بقائمة الصفحات، أو المحتوى التجريبي.
الخطوة 3: التحميل في الوقت المناسب (التأجيل/غير المتزامن، الوحدات النمطية والتحميل المسبق)
يقلل التحميل المشروط من حجم الملف. علاوة على ذلك، يمكنك تحقيق مكاسب أكبر بتجنب حظر عملية العرض.
أضف خاصية defer/async بشكل صحيح عبر script_loader_tag
لا تستخدم التعابير النمطية في كود HTML النهائي. استخدم فلتر ووردبريس المدمج.
add_filter('script_loader_tag', function ($tag, $handle, $src) {
// Liste blanche : ne touchez pas aux scripts critiques ou dépendants de l'ordre sans audit
$defer_handles = [
'gallery-enhancements',
'mon-slider',
];
if (!in_array($handle, $defer_handles, true)) {
return $tag;
}
// Ne pas modifier si déjà "defer" (évite doublons)
if (str_contains($tag, ' defer')) {
return $tag;
}
// Ajoute defer avant src (HTML valide)
return str_replace(' src=', ' defer src=', $tag);
}, 10, 3);
مكافحة النمط متكرر: ضع defer يحدث هذا الخطأ في سكربت يجب تنفيذه قبل سكربت آخر (مثلاً، تبعيات غير مُعلنة، أو سكربت مضمّن ينتظر قيمة متغير). والنتيجة: أخطاء متقطعة في جافا سكريبت، غالباً ما تكون غير مرئية على أجهزة الكمبيوتر المكتبية ولكنها تظهر على الأجهزة المحمولة.
قم بتحميل البرنامج كوحدة نمطية (type="module") عند التحكم في البرنامج النصي
إذا كان كود جافا سكريبت الخاص بك حديثًا ومتوافقًا مع المتصفحات الحالية، فيمكنك استخدامه كوحدة نمطية. ملاحظة: هذا يُغيّر نطاق المكتبة وكيفية التعامل مع عمليات الاستيراد.
add_filter('script_loader_tag', function ($tag, $handle, $src) {
if ($handle !== 'gallery-enhancements') {
return $tag;
}
// Remplace type='text/javascript' implicite par type="module"
// Note : WordPress génère généralement <script src="..."></script> sans attribut type.
$tag = str_replace('<script ', '<script type="module" ', $tag);
// Les modules sont defer par défaut, inutile d'ajouter defer
return $tag;
}, 10, 3);
التحميل المسبق المستهدف (فقط إذا كنت متأكدًا)
تحميل "كل شيء" مسبقاً خطأ شائع. حمّل فقط الملفات المهمة في الجزء العلوي من الصفحة، وعلى الصفحات ذات الصلة فقط.
add_action('wp_head', function () {
if (!is_front_page()) {
return;
}
// Exemple : hero.css uniquement sur la home
$href = get_stylesheet_directory_uri() . '/assets/hero.css';
echo '<link rel="preload" href="' . esc_url($href) . '" as="style">' . "n";
}, 1);
تقييم الأداء إذا كانت عملية التخزين المؤقت/التصغير لديك تُجمّع ملفات CSS، فقد يصبح هذا التحميل المسبق غير ضروري أو قد يُشير إلى عنوان URL يتغير. تحقق من مسار العمل الخاص بك.
الخطوة 4: التحميل المشروط للكتل، Elementor، Divi 5 و Avada
تكمن المشكلة في أدوات إنشاء الصفحات في عملية الكشف. فأنت تريد تحميل أحد الأصول فقط إذا كانت الوحدة/الأداة موجودة، ولكن المحتوى يُخزن أحيانًا في علامات التعريف (وليس في علامة `<head>`). post_content).
غوتنبرغ / الكتل: الحالة الأبسط
إذا كنت مهتمًا تمامًا بالبناء، فاستمر. has_block() (الخطوة 2). إنها سريعة وموثوقة.
Elementor: مشروط عند قيام Elementor بعرض الصفحة
يحتوي Elementor على واجهات برمجة تطبيقات داخلية، لكنها قيد التطوير. في عام 2026، أفضّل اتباع نهج قوي.
- الكشف عما إذا كانت الصفحة قد تم إنشاؤها باستخدام Elementor عبر علامات التعريف الوصفية (meta tags)،
- ثم قم بتحميل أصول محددة فقط في هذه الحالة.
add_action('wp_enqueue_scripts', function () {
if (!is_singular()) {
return;
}
$post_id = get_queried_object_id();
if (!$post_id) {
return;
}
// Elementor stocke généralement un flag dans le meta _elementor_edit_mode
$edit_mode = get_post_meta($post_id, '_elementor_edit_mode', true);
if (empty($edit_mode)) {
return;
}
// Exemple : script d'animation uniquement pour pages Elementor
wp_enqueue_script(
'site-animations',
get_stylesheet_directory_uri() . '/assets/animations.js',
[],
'1.2.0',
['in_footer' => true]
);
}, 20);
ملاحظة لقد رأيت بالفعل مواقع حيث _elementor_edit_mode الصفحة فارغة على الرغم من أنها مبنية باستخدام Elementor (نقل البيانات، استيرادها). في هذه الحالة، يمكنك أيضًا التحقق من وجود _elementor_data :
function bpcab_is_elementor_page(int $post_id): bool {
$data = get_post_meta($post_id, '_elementor_data', true);
return !empty($data);
}
Divi 5: تجنب اتباع نهج "التطبيق الشامل على كل شيء" واستخدم الرموز المختصرة/الوحدات النمطية للشروط.
لطالما استخدم Divi الرموز المختصرة في المحتوى. يُجري Divi 5 تحديثات كثيرة، ولكن عمليًا ما زلت أرى العديد من الصفحات بتوقيعات مألوفة. post_content (أو عبر البيانات الوصفية حسب التكوين).
النهج العملي: مشروط بناءً على وجود نمط (رمز مختصر/وحدة نمطية) et قم بتضمين خيار احتياطي لكل قالب.
add_action('wp_enqueue_scripts', function () {
if (!is_singular('page')) {
return;
}
$post = get_queried_object();
if (!$post instanceof WP_Post) {
return;
}
// Détection simple (à adapter selon votre contenu Divi)
$looks_like_divi = str_contains($post->post_content, '[et_pb_') || str_contains($post->post_content, 'et_pb_section');
// Fallback : template spécifique "page-landing.php"
$looks_like_landing = is_page_template('page-landing.php');
if (!$looks_like_divi && !$looks_like_landing) {
return;
}
wp_enqueue_script(
'landing-effects',
get_stylesheet_directory_uri() . '/assets/landing-effects.js',
[],
'1.0.0',
['in_footer' => true]
);
}, 20);
لماذا أفعل ذلك بهذه الطريقة؟ إن محاولة تحديد وحدة Divi المحددة في جانب PHP أمر غير موثوق به. هنا، ستوفر الكثير بتجنب تحميل المقالات والتصنيفات والصفحات البسيطة.
Avada / Fusion Builder: التكييف حسب المحتوى وحسب بيانات المنشورات الوصفية
غالباً ما تخزن Avada رموز Fusion المختصرة في المحتوى. نفس المنطق: الكشف عن طريق التوقيع، ثم الإضافة إلى قائمة الانتظار.
add_action('wp_enqueue_scripts', function () {
if (!is_singular()) {
return;
}
$post = get_queried_object();
if (!$post instanceof WP_Post) {
return;
}
// Fusion shortcodes (exemples fréquents)
$uses_fusion = str_contains($post->post_content, '[fusion_') || str_contains($post->post_content, 'fusion_builder');
if (!$uses_fusion) {
return;
}
wp_enqueue_style(
'fusions-helpers',
get_stylesheet_directory_uri() . '/assets/fusion-helpers.css',
[],
'1.0.0'
);
}, 20);
الحالة "الأداة موجودة ولكن يتم عرضها عبر AJAX"
إذا قام أحد المكونات بتحميل محتوى لاحقاً (نوافذ منبثقة، فلاتر منتجات، بحث فوري)، فقد يفشل الكشف القائم على المحتوى. في هذه الحالة، يكون الحل الأمثل هو:
- أن تسأل علامة صريحة (منشور ميتا، خيار، حقل ACF)،
- والتحقيق وفقًا لهذه العلامة.
function bpcab_page_needs_filters(int $post_id): bool {
return (bool) get_post_meta($post_id, '_needs_filters_js', true);
}
add_action('wp_enqueue_scripts', function () {
if (!is_singular()) {
return;
}
$post_id = get_queried_object_id();
if (!$post_id || !bpcab_page_needs_filters($post_id)) {
return;
}
wp_enqueue_script(
'filters',
get_stylesheet_directory_uri() . '/assets/filters.js',
[],
'1.0.0',
['in_footer' => true]
);
}, 20);
هذا النمط مستقر للغاية في الفريق: لا حاجة إلى "فحص" أداة البناء، يقوم المحرر بتحديد خانة، وتقوم الصفحة بتحميل ما هو مطلوب.
الخطوة 5: تقليل تكلفة CSS والخطوط لكل صفحة
كثيراً ما نتحدث عن جافا سكريبت، لكن CSS العالمي ضار بنفس القدر على الأجهزة المحمولة: فهو يؤخر عملية العرض، ويزيد من تكلفة إعادة حساب الأنماط.
تقسيم ملف CSS حسب القالب (الصفحة الرئيسية، المقالة، الصفحة)
مثال بسيط: بدلاً من theme.css ضخم، أنت تحتفظ بـ "جوهر" وتضيف طبقات مشروطة.
add_action('wp_enqueue_scripts', function () {
// CSS de base partout
wp_enqueue_style(
'theme-core',
get_stylesheet_directory_uri() . '/assets/css/core.css',
[],
'1.0.0'
);
// CSS spécifique home
if (is_front_page()) {
wp_enqueue_style(
'theme-home',
get_stylesheet_directory_uri() . '/assets/css/home.css',
['theme-core'],
'1.0.0'
);
}
// CSS spécifique articles
if (is_singular('post')) {
wp_enqueue_style(
'theme-post',
get_stylesheet_directory_uri() . '/assets/css/post.css',
['theme-core'],
'1.0.0'
);
}
}, 20);
قياس تحقق من قسم "التغطية" في أدوات مطوري Chrome (أو تقارير أداة تجميع الملفات لديك) للتأكد من تقليل استخدام CSS غير المُستخدَم. لقد لاحظتُ تحسناً ملحوظاً في LCP بمجرد إزالة CSS الخاص بصفحة الهبوط من القسم العام.
الخطوط: لا تقم بتحميل 6 خطوط كبيرة الحجم في كل مكان
دون الخوض في نقاشات التصميم: في العديد من المدونات، يكفي استخدام نوعين من الخطوط العريضة لـ 90% من الصفحات. أما الباقي فيمكن أن يكون مشروطاً (صفحات الهبوط التسويقية) أو يُحذف تماماً.
إذا قمت بتثبيت الخطوط عبر @import في لغة CSS، تفقد السيطرة. من الأفضل استخدام الاستعلامات والعبارات الشرطية.
add_action('wp_enqueue_scripts', function () {
// Exemple : font marketing uniquement sur pages de vente
if (!is_page_template('page-sales.php')) {
return;
}
wp_enqueue_style(
'marketing-fonts',
'https://example-cdn.invalid/fonts/marketing.css',
[],
'1.0.0'
);
}, 20);
ملاحظة السلامة يُضيف تحميل الموارد الخارجية مخاطر (من حيث التوافر والسرية). يُنصح، إن أمكن، باستضافة الموقع ذاتيًا وتقديمه عبر شبكة توصيل المحتوى (CDN).
تكوين الخادم
يؤثر التحميل المشروط بشكل أساسي على حجم الملفات وتنفيذ واجهة المستخدم. مع ذلك، إذا لم يقم خادمك بتخزين الأصول الثابتة مؤقتًا بشكل صحيح، فستفقد بعضًا من هذه المزايا.
ملف .htaccess (أباتشي): ذاكرة تخزين مؤقتة طويلة للأصول ذات الإصدارات
إذا كانت ملفاتك تحتوي على رقم إصدار في عنوان URL (يضيف ووردبريس ?ver=لا يزال بإمكانك استخدام ذاكرة تخزين مؤقت طويلة، ولكن من الأفضل أن تُحدِّد الإصدارات حسب اسم الملف (رقم الإصدار). في حال تعذّر ذلك، إليك نقطة بداية معقولة.
# .htaccess - cache statique (à placer dans le vhost ou le .htaccess racine si autorisé)
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 30 days"
ExpiresByType application/javascript "access plus 30 days"
ExpiresByType text/javascript "access plus 30 days"
ExpiresByType image/svg+xml "access plus 30 days"
ExpiresByType image/png "access plus 30 days"
ExpiresByType image/jpeg "access plus 30 days"
ExpiresByType font/woff2 "access plus 180 days"
</IfModule>
<IfModule mod_headers.c>
<FilesMatch ".(css|js|png|jpg|jpeg|svg|woff2)$">
Header set Cache-Control "public, max-age=2592000, immutable"
</FilesMatch>
</IfModule>
فخ إذا لم يقم خط أنابيب البيانات الخاص بك بتغيير عناوين URL مطلقًا (لا يوجد تجزئة، ولا يوجد إصدار)، immutable قد يؤدي هذا إلى منع التحديثات من جانب الزائر. اختبر ذلك على متصفح جديد.
wp-config.php: قم بتعطيل دمج عناصر لوحة التحكم فقط في حالة تصحيح الأخطاء
عندما تبحث عن تعارض في الأصول، يمكن أن يساعد تعطيل ربط المسؤول (لكن لا تتركه قيد التشغيل بشكل دائم).
// Uniquement pour debug en admin : facilite l'identification des fichiers
define('CONCATENATE_SCRIPTS', false);
php.ini / PHP-FPM: OPcache (إذا كان لديك صلاحية الوصول)
لا يُسرّع OPcache عمليات جافا سكريبت/CSS بشكل مباشر، ولكنه يُقلل من وقت استجابة PHP (TTFB). إذا كنت تُجري الكثير من الاختبارات، فستحتاج إلى بنية خادم قوية.
; php.ini (exemple)
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
مصدر: تكوين PHP OPcache
التحقق من النتائج
بعد كل سلسلة من التغييرات، أقوم بالتحقق من ثلاث طبقات: سجلات ووردبريس، والمتصفح، والمقاييس.
1) تحقق من اختفاء المقابض (debug.log)
أعد تحميل صفحة لا ينبغي تحميل البرنامج النصي عليها. debug.logابحث عن المقبض. إذا كان لا يزال موجوداً:
- أنت لست على الخطاف الصحيح.
- أو أن أولويتك منخفضة للغاية.
- أو يقوم مكون إضافي آخر بإعادة إضافته إلى قائمة الانتظار بعدك.
2) تحقق من وجود أي أخطاء في جافا سكريبت (وحدة التحكم)
غالباً ما يظهر التحميل المشروط سيئ التنفيذ من خلال:
Uncaught ReferenceError(غياب المكتبة)،Cannot read properties of undefined(تم خرق أمر التنفيذ)- تظهر الأخطاء فقط في صفحات معينة (وهذا هو الأسوأ، لأنه يمر إلى قسم ضمان الجودة).
3) القياس (Lighthouse / WebPageTest / PageSpeed)
للحفاظ على "التركيز على الكود"، إليك قائمة مرجعية بالتدابير:
- إجمالي JS تم تنزيلها إلى الصفحة (يجب أن تظهر على الصفحات غير المتأثرة)
- TBT (غالباً ما ينخفض إذا قمت بإزالة المكتبات)
- INP (إذا قمت بإزالة المستمعين/الرسوم المتحركة العامة)
- نظام الإجراءات الجزائية (إذا قمت بتقليل استخدام CSS المانع)
أحتفظ أيضاً بنقطة مرجعية بسيطة: السجل PERF shutdown بناءً على 5 عمليات تحميل، متوسط. ليس مثاليًا، ولكنه يكشف عن حالات التراجع بسرعة.
إذا لم يتحسن الأداء
عندما لا يُحدث التحميل المشروط أي تغيير يُذكر، فغالباً ما يكون السبب هو عدم وجود عنق الزجاجة. إليكم خطة معالجة المشكلة.
1) وقت استجابة الخادم (TTFB) سيء بالفعل: ابحث عن PHP/SQL قبل الأصول
- قم بتعطيل الإضافات غير الأساسية مؤقتًا (بيئة الاختبار)،
- استخدم أداة مراقبة الاستعلامات لتحديد الاستعلامات البطيئة.
- تحقق مما إذا كان أ ذاكرة التخزين المؤقت للكائنات نشط (Redis/Memcached) ويعمل بشكل سليم.
2) لم يتبق سوى نص برمجي "وحشي" واحد عالميًا
غالباً ما يكون عبارة عن حزمة من الرسوم المتحركة، أو إطار عمل، أو "نواة" بناء. لديك هنا خياران واقعيان:
- قم بتهيئته قدر الإمكان (باستخدام أداة إنشاء الصفحات فقط)،
- استبدله بـ JS أخف (لكن هذا مشروع كبير).
3) التخزين المؤقت/التصغير يخفي التغييرات
كثيراً ما رأيت هذا: تقوم بإزالة مؤشر من قائمة الانتظار، لكن إضافة تحسين الأداء تُقدّم ملفاً مُدمجاً لا يزال يحتوي على الكود. في هذه الحالة:
- تعطيل خاصية التصغير/الدمج مؤقتًا،
- أعد رسم الخرائط.
- ثم أعد التنشيط وتحقق من أن خط الأنابيب يفي بشروطك.
4) للهواتف المحمولة فقط: وحدة المعالجة المركزية، بدون شبكة
إذا انخفض الوزن ولكن لم يتغير INP/TBT، فهذا يعني أن تنفيذ كود جافا سكريبت المتبقي مكلف. ابحث عن:
- مستمعون عالميون غير مقيدين (التمرير/تغيير الحجم)،
- رسوم متحركة معقدة،
- يتم تهيئة الشرائح الدوارة حتى عندما تكون غير مرئية.
الأخطاء الشائعة والمزالق
أخطاء أراها طوال الوقت
- نسخ الكود إلى المكان الخطأ : مقتطف في إضافة ذاكرة التخزين المؤقت/التصغير التي تعيد كتابته، أو في قالب رئيسي سيتم تحديثه.
- خطاف غير لائق : للاستخدام
initبدلا منwp_enqueue_scriptsثم اسأل نفسك لماذا لا ينجح الأمر. - الأولوية منخفضة للغاية : لك
wp_dequeue_script()يتم تنفيذه قبل استعلام الملحق، لذلك ليس له أي تأثير. - نسيان مسح ذاكرة التخزين المؤقت : ذاكرة التخزين المؤقت للصفحة، ذاكرة التخزين المؤقت للمتصفح، شبكة توصيل المحتوى (CDN).
- الاختبار في بيئة الإنتاج بدون حفظ/تجهيز: يمكن أن يتعطل منشئ الصفحات بصمت.
- كود من درس تعليمي قديم : استخدام معلمات قائمة الانتظار بشكل غير صحيح، أو استخدام عوامل التصفية على الخطافات القديمة.
مخطط تشخيصي
| عرض | السبب المحتمل | التحقق | الحلول |
|---|---|---|---|
| لا يزال البرنامج النصي قيد التحميل على الرغم من عملية إزالة العنصر من قائمة الانتظار. | الأولوية منخفضة جدًا أو الخطاف سيئ | سجل الطلب عبر error_log وقارن الأولويات |
استمر wp_enqueue_scripts priorité 999 (أو أكثر) وتجنب init |
| يحدث خلل في الصفحة (خطأ جافا سكريبت) بعد التحسين | تبعية غير معلنة، تم كسر ترتيب التنفيذ بواسطة defer |
وحدة التحكم: ReferenceErrorشلال جافا سكريبت |
قم بتعريف التبعيات في wp_enqueue_scriptيزيل defer المقبض المعني |
| يعمل على أجهزة الكمبيوتر المكتبية، وليس على الهواتف المحمولة. | الشروط صارمة للغاية، محتوى مختلف (أداة البناء، ذاكرة التخزين المؤقت) | قارن بين HTML المُعالَج وخريطة الأصول على الهاتف المحمول/سطح المكتب | يتم تحديد الشروط حسب نوع المحتوى أو العلامة الوصفية بدلاً من اكتشاف الهشاشة |
| ليس للتغييرات أي تأثير | يؤدي التصغير/الدمج إلى خدمة حزمة واحدة | قم بتعطيل خاصية التصغير مؤقتًا، ثم أعد الاختبار. | قم بتهيئة إضافة الأداء بحيث تحترم الاستثناءات حسب المعرّف أو حسب عنوان URL |
| بعض صفحات الويب تفتقر إلى CSS | تم وضع النمط المشروط في قائمة الانتظار ولكن هناك حاجة عامة (مكون مشترك) | فحص: قواعد مفقودة، فئات غير منسقة | احتفظ بـ core.css عالمي وتكييف "الطبقات" فقط |
| محرر Elementor/Divi/Avada غير مستقر | يمكنك إزالة الأصول من قائمة الانتظار في جانب الإدارة/المعاينة | اختبر المحرر + المعاينة، وتحقق من الشروط. is_admin() |
أضف إجراءات وقائية: اترك ملف admin/REST/AJAX دون تغيير، واختبر أوضاع المعاينة. |
نصائح الصيانة
- احتفظ بقائمة بيضاء المقابض المعدلة (إزالة من قائمة الانتظار/تأجيل). عند تحديث أحد المكونات الإضافية، ستعرف ما يجب إعادة التحقق منه.
- أضف سجلات مؤقتة أثناء التحديثات الرئيسية (أداة الإنشاء، والقوالب، ومكونات تحسين الأداء)، قم بإزالتها.
- توثيق الظروف "هذا النص موجود فقط في الصفحة X أو في الكتلة Y". بعد ستة أشهر، ستنسى السبب.
- اختبار دائما : صفحة بسيطة، صفحة بناء ثقيلة، صفحة نموذج، والمحرر.
- أتمتة إن أمكن: اختبار Lighthouse CI على 3-5 عناوين URL مهمة (حتى داخليًا) يكشف عن حالات التراجع.
الموارد
- wp_enqueue_script() (مرجع للمطورين)
- wp_enqueue_style() (مرجع للمطورين)
- wp_dequeue_script() (مرجع للمطورين)
- wp_deregister_script() (مرجع للمطورين)
- script_loader_tag (فلتر رسمي)
- has_block() (الكشف عن الكتل)
- مراقبة الاستعلامات (إضافة)
- نواة ووردبريس (نسخة احتياطية على جيت هاب)
- تتبع نظام WordPress الأساسي
- memory_get_peak_usage() (PHP)
الأسئلة الشائعة
هل يجب عليّ استخدام wp_dequeue_script() أم wp_deregister_script()؟
wp_dequeue_script() يزيل البرنامج النصي من قائمة الانتظار لهذا الطلب. wp_deregister_script() كما أنه يحذف سجله، مما يمنعه من إعادة إضافته لاحقًا. عمليًا، غالبًا ما أقوم بالأمرين معًا عندما أريد التأكد من أن أحد المكونات الإضافية لن يعيد إضافته.
لماذا لا تعمل عملية إزالة العنصر من قائمة الانتظار على الرغم من أن المقبض صحيح؟
في 80% من الحالات: يتم تنفيذ التعليمات البرمجية الخاصة بك مبكرًا جدًا. انقل دالة الاستدعاء إلى wp_enqueue_scripts بأولوية عالية (على سبيل المثال: 999) ليتم استخدامها بعد الإضافات.
هل يمكنني استخدام is_page('contact') كشرط؟
نعم، ولكن توخَّ الحذر عند استخدام الترجمات (WPML/Polylang) وتغييرات الروابط. يُعدّ مُعرّف الصفحة أكثر استقرارًا. في المواقع متعددة اللغات، أُفضّل استخدام قالب مُخصّص أو علامة وصفية.
هل دالة `has_shortcode()` موثوقة مع Elementor/Divi/Avada؟
ليس دائمًا. إذا كان المُنشئ يخزن البنية في بيانات تعريفية (JSON)، post_content قد لا يحتوي على الرمز المختصر المتوقع. استخدم خاصية الكشف عن البيانات الوصفية (على سبيل المثال، _elementor_data) أو علامة صريحة.
هل يمكنني استخدام خاصية "defer" في jQuery لتسريع الأمور؟
أتجنب استخدامه. تفترض العديد من البرامج النصية (القوالب، والإضافات) أن مكتبة jQuery متوفرة تلقائيًا. إذا كنت مصراً على استخدامها، فراجع جميع التبعيات واختبرها على الأجهزة المحمولة، ومنصات إنشاء الصفحات، والنماذج.
كيفية تجنب تعطيل المحرر (Gutenberg / Elementor)؟
أضف إجراءات وقائية: لا تقم بتعديل أي شيء على is_admin(), wp_doing_ajax()ولا على طلبات REST. واختبر أوضاع المعاينة الخاصة بالمنشئين، والتي يمكنها استخدام الواجهة الأمامية بمعلمات محددة.
تقوم إضافة التخزين المؤقت الخاصة بي بتجميع كل شيء في ملف واحد. هل ما زال التحميل المشروط مستخدماً؟
نعم، ولكن عليك ضبط إعدادات إضافة التخزين المؤقت/التصغير لتراعي الاستثناءات حسب المعرّف أو عنوان URL. وإلا، ستظن أن شيئًا لم يتغير، لأن الحزمة ستبقى كما هي.
ما هو الشرط الأفضل: القالب، المعرّف، الكتلة، الرمز المختصر؟
لموقع إلكتروني تحريري: كتلة (أو المحتوى) غالبًا ما يكون الأكثر قوة. بالنسبة لصفحات التسويق: قالب ou IDبالنسبة للحالات الديناميكية (AJAX): علامة البيانات الوصفية.
هل يجب عليّ فعل ذلك في ملف functions.php؟
نادراً ما أفعل ذلك. إضافة "أداء الموقع" المصغّرة أكثر متانة، وقابلة للتحديث، ومستقلة عن القوالب. كما يسهل تعطيلها عند الحاجة إلى استكشاف الأخطاء وإصلاحها بسرعة.
كيف يمكن إثبات الفائدة بدون أداة خارجية؟
سجل المقابض (قبل/بعد)، ثم سجل timer_stop()الذاكرة وعدد الطلبات. إنه ليس معيارًا مثاليًا، ولكنه يكتشف بسرعة التحسينات، والأهم من ذلك، التراجعات.