JavaScript على WordPress هو الجحيم ... وإليك السبب

نشرت: 2022-05-05

لقد تغيرت حزمة تطوير WordPress كثيرًا في السنوات الأخيرة. منذ وصول Gutenberg ، أصبح دور JavaScript في نظام إدارة المحتوى المفضل لدينا أكثر أهمية من أي وقت مضى. لقد تحدثنا في هذه المدونة مطولاً عن المزايا التي ينطوي عليها ذلك للمطورين (على سبيل المثال لا الحصر ، تحدثنا عن ملحقات Gutenberg ، ونصائح حول TypeScript و React ، وأدوات التطوير ، وأمثلة المكونات الإضافية ، والمزيد) ولكن كل للقصة جانبها المظلم ... وهذا ما سنتحدث عنه هنا اليوم.

في منشور اليوم ، سأشارك معك المشكلات الرئيسية الثلاث التي قد تواجهها أنت ، مطور مكونات WordPress الإضافية ، في المستقبل. والجزء المضحك هو أن كل واحد منهم لديه مذنب مختلف: إما WordPress نفسه ، أو المطورين الآخرين ، أو نفسك. ها نحن ذا: أكثر أنواع صداع JavaScript شيوعًا التي قد تواجهها وما يمكنك فعله لتجنب / إصلاحها.

# 1 إضافات WPO التي ستكسر المكون الإضافي الخاص بك وموقعك

لنبدأ بالمشكلة التي كانت مصدر الكثير من التذاكر هنا في Nelio: WPO plugins.

أنا متأكد من أنك قرأت الكثير من المقالات والمدونات التي توضح أهمية وجود موقع ويب خفيف يتم تحميله بسرعة. أعني أننا كتبنا عنه في مناسبات عديدة! من بين النصائح التي يقدمونها عادةً ، ستجد أشياء مثل التبديل إلى مزود استضافة أفضل ، واستخدام المكونات الإضافية لذاكرة التخزين المؤقت وشبكات CDN ، والحفاظ على تحديث الخادم و WordPress ، أو (وهنا تأتي المشكلة الأولى) تثبيت مكون WPO الإضافي. بعض الأمثلة على هذا الأخير تشمل:

  • W3 Total Cache مع أكثر من مليون تثبيت
  • SiteGround Optimizer مع أكثر من مليون عملية تثبيت أيضًا (أحدها ، بالمناسبة ، نستخدمه في موقعنا على الويب)
  • تعديلات وتحسينات WordPress WPO بواسطة صديقنا العزيز فرناندو تيلادو

تعد هذه المكونات الإضافية بتسريع موقع الويب الخاص بك من خلال سلسلة من التحسينات المعقولة التي ، بشكل عام ، "يمكن لأي موقع WordPress الاستفادة منها." تشمل هذه التحسينات:

  • فصل البرامج النصية غير الضرورية على الواجهة الأمامية ، مثل الرموز التعبيرية أو Dashicons
  • صفحات التخزين المؤقت واستعلامات قاعدة البيانات
  • تقليل كمية المعلومات المضمنة في الرأس
  • دمج نصوص JavaScript وأنماط CSS وتصغيرها
  • تصغير HTML
  • إزالة استعلام الإصدار من عناوين URL الخاصة بأصولك الثابتة
  • تأجيل نصوص جافا سكريبت و / أو تحميلها بشكل غير متزامن
  • إلخ

كما كنت أقول ، قد تكون هذه الأنواع من التحسينات مفيدة بشكل عام. ولكن في تجربتنا ، تميل جميع تحسينات JS على موقع WordPress على الويب إلى حدوث المزيد من المشكلات ، مما يجعل التحسينات المفترضة عديمة الفائدة. الأمثلة الحقيقية التي رأيتها على مر السنين هي:

  • الجمع بين النصوص. كلما قل عدد البرامج النصية التي يطلبها متصفحك ، كان ذلك أفضل. هذا هو السبب في أنه من المنطقي دمج جميع البرامج النصية في برنامج واحد. ومع ذلك ، يمكن أن يكون هذا مشكلة. بشكل عام ، في حالة تعطل برنامج JavaScript ، ينتهي تنفيذه ويتم الإبلاغ عن الخطأ في وحدة تحكم المتصفح. لكن فقط تنفيذ هذا النص توقف ؛ سيتم تشغيل البرامج النصية الأخرى بشكل طبيعي. ولكن إذا جمعتهم جميعًا ... حسنًا ، بمجرد فشل أحد البرامج النصية ، لن يتم تشغيل البرامج النصية الأخرى (بما في ذلك ربما نصك) ، وسيعتقد المستخدمون أن المكون الإضافي الخاص بك هو المكون الذي لا يعمل كما هو متوقع.
  • تصغير النصوص. لقد رأيت بعض عمليات التصغير التي ، صدق أو لا تصدق ، كسرت التعبيرات العادية وأدت إلى نصوص جافا سكريبت بها أخطاء في بناء الجملة. بالتأكيد ، لقد مرت فترة منذ آخر مرة واجهت فيها هذا ولكن ...: - /
  • وسيطات الاستعلام. عند إدراج برنامج نصي في قائمة الانتظار في WordPress ، يمكنك القيام بذلك باستخدام رقم الإصدار الخاص به (والذي ، بالمناسبة ، ربما تم إنشاؤه تلقائيًا بواسطة @wordpress/scripts ). أرقام الإصدار مفيدة للغاية: إذا قمت بتحديث المكون الإضافي الخاص بك وتغيير النص البرمجي ، فسيضمن رقم الإصدار الجديد هذا أن يرى جميع الزوار عنوان URL مختلفًا ، وبالتالي ، ستطلب مستعرضاتهم الإصدار الجديد. لسوء الحظ ، إذا أزال مكون WPO الإضافي سلسلة الاستعلام ، فقد لا يدرك زوار موقعك أن البرنامج النصي قد تغير وسيستخدمون نسخة مخبأة من البرنامج النصي المذكور ... مما قد يؤدي أو لا ينتج عنه عواقب غير مقصودة. اللعنة!

كارثة كاملة ، أليس كذلك؟ لكن انتظر حتى تسمع المقطع التالي:

تأجيل النصوص

في Nelio ، قمنا بتنفيذ مكون إضافي لاختبار A / B لتتبع زوار موقعك واكتشاف التصميم والمحتوى الذي يحصل على أكبر عدد من التحويلات. كما يمكنك أن تتخيل ، يبدو نص التتبع الخاص بنا مشابهًا لما يلي:

 window.NelioABTesting = window.NelioABTesting || {}; window.NelioABTesting.init = ( settings ) => { // Add event listeners to track visitor events... console.log( settings ); };

أي أنه يعرض وظيفة init التي يتعين علينا الاتصال بها من أجل السماح للبرنامج النصي بمعرفة الاختبارات التي يتم تشغيلها حاليًا. لاستدعاء هذه الطريقة ، نضع نصًا برمجيًا مضمنًا في PHP على النحو التالي:

 function nab_enqueue_tracking_script() { wp_enqueue_script( 'nab-tracking', ... ); wp_add_inline_script( 'nab-tracking', sprintf( 'NelioABTesting.init( %s );', wp_json_encode( nab_get_tracking_settings() ) ) ); } add_action( 'wp_enqueue_scripts', 'nab_enqueue_tracking_script' );

مما ينتج عنه علامات HTML التالية:

 <head> ... <script type="text/javascript" src="https://.../dist/tracking.js" ></script> <script type="text/javascript" > NelioABTesting.init( {"experiments":[...],...} ); </script> ... </head> <body> ...

ولكن ماذا يحدث عندما يضيف المكون الإضافي defer الخاص بك سمة التأجيل إلى البرنامج النصي الخاص بنا؟

 <head> ... <script defer <!-- This delays its loading... --> type="text/javascript" src="https://.../dist/tracking.js" ></script> <script type="text/javascript" > NelioABTesting.init( {"experiments":[...],...} ); </script> ... </head> <body> ...

حسنًا ، تم تأجيل النص الآن ... مما يعني أن المقتطف السابق يعادل هذا:

 <head> ... <script type="text/javascript" > NelioABTesting.init( {"experiments":[...],...} ); </script> ... </head> <body> ... <script type="text/javascript" src="https://.../dist/tracking.js" ></script> </body> </html>

ونتيجة لذلك ، لم يعد يتم تحميل nab-tracking-js عندما يُفترض به ، وبالتالي ، فإن النص البرمجي المضمن الذي يأتي بعده ويعتمد عليه سيفشل ببساطة: يستخدم nab nab-tracking-js-after NelioABTesting.init بفضل توجيه defer ، لم يتوفر بعد. سيى!

المحلول

الحل الأكثر فاعلية واضح: أخبر المستخدمين بتعطيل تحسين البرنامج النصي واستدعوه يوميًا. بعد كل شيء ، إدارة التبعية في JavaScript معقدة للغاية بشكل عام (خاصة إذا استخدمنا توجيهات defer و async ) ، و WordPress ليس استثناءً. ما عليك سوى إلقاء نظرة على هذه المناقشة التي تبلغ من العمر 12 عامًا حول هذا الموضوع!

ولكن إذا لم يكن ذلك ممكنًا (وأنا أعلم أنه ليس كذلك) ، فإنني أوصيك بفعل الشيء نفسه الذي فعلناه: التخلص من طريقة init وعكس مسؤولياتك على البرامج النصية العادية والمضمنة. أي ، أضف نصًا نصيًا مضمنًا قبل البرنامج النصي العادي واستخدمه لتعريف متغير عام بالإعدادات المطلوبة:

 function nab_enqueue_tracking_script() { wp_enqueue_script( 'nab-tracking', ... ); wp_add_inline_script( 'nab-tracking', sprintf( 'NelioABTestingSettings = %s;', wp_json_encode( nab_get_tracking_settings() ) ), 'before' ); } add_action( 'wp_enqueue_scripts', 'nab_enqueue_tracking_script' );

بحيث يبدو HTML الناتج على النحو التالي:

 <head> ... <script type="text/javascript" > NelioABTestingSettings = {"experiments":[...],...}; </script> <script type="text/javascript" src="https://.../dist/tracking.js" ></script> ... </head> <body> ...

ولذا لا يهم ما إذا كان تنفيذ البرنامج النصي الخارجي قد تأخر أم لا - سيظهر دائمًا بعد البرنامج النصي المضمن ، وبالتالي يرضي التبعية بينهما.

أخيرًا ، إذا كنت تريد التأكد من عدم قيام أي شخص بتعديل إعداداتك ، فقم بتعريف المتغير على أنه const وقم بتجميد قيمته باستخدام Object.freeze :

 ... sprintf( 'const NelioABTestingSettings = Object.freeze( %s );', wp_json_encode( nab_get_tracking_settings() ) ), ...

التي تدعمها جميع المتصفحات الحديثة.

# 2 التبعيات في WordPress التي قد تعمل أو لا تعمل ...

يمكن أن تكون إدارة التبعية أيضًا مشكلة في WordPress ، خاصة عند الحديث عن البرامج النصية المضمنة في WordPress. دعني أوضح.

تخيل ، على سبيل المثال ، أننا نقوم بإنشاء امتداد صغير لـ Gutenberg ، كما أوضحنا هنا. من المحتمل أن تحتوي شفرة مصدر المكون الإضافي الخاص بنا على بعض عبارات import مثل هذه:

 import { RichTextToolbarButton } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; import { registerFormatType } from '@wordpress/rich-text'; // ...

عندما يتم ترجمة كود مصدر JS هذا ، فإن Webpack (أو الأداة التي تستخدمها) ستجمع كل التبعيات وكود المصدر الخاص بك في ملف JS واحد. هذا هو الملف الذي ستدرجه لاحقًا في قائمة WordPress حتى يعمل كل شيء كما تتوقع.

إذا استخدمت @wordpress/scripts لإنشاء مثل هذا الملف ، فلن يتم تضمين بعض التبعيات في ملف الإخراج ، لأن العملية المضمنة تفترض أن الحزم ستكون متاحة في النطاق العام. هذا يعني أنه سيتم تحويل الواردات السابقة إلى شيء مشابه لهذا:

 const { RichTextToolbarButton } = window.wp.blockEditor; const { __ } = window.wp.i18n; const { registerFormatType } = window.wp.richText; // ...

للتأكد من أنك لا تفوت أيًا من تبعيات البرنامج النصي الخاص بك ، فإن @wordpress/scripts لن يقوم فقط بتحويل كود JS الخاص بك ، ولكنه سينشئ أيضًا ملف PHP مع تبعيات WordPress الخاصة به:

 <?php return array( 'dependencies' => array('wp-block-editor','wp-i18n','wp-rich-text'), 'version' => 'a12850ccaf6588b1e10968124fa4aba3', );

أنيق جدا ، أليس كذلك؟ إذن ما هي المشكلة؟ حسنًا ، حزم WordPress هذه قيد التطوير المستمر وهي تتغير كثيرًا ، مضيفة ميزات وتحسينات جديدة. لذلك ، إذا قمت بتطوير المكون الإضافي الخاص بك باستخدام أحدث إصدار من WordPress ، فقد ينتهي بك الأمر عن غير قصد باستخدام الوظائف أو الميزات المتوفرة في هذا الإصدار الأخير (وبالتالي يعمل كل شيء كما ينبغي) ولكن ليس في إصدارات WordPress "الأقدم" ...

كيف تستطيع أن تقول ذلك؟

المحلول

نصيحتي هنا بسيطة للغاية: قم بتطوير المكونات الإضافية باستخدام أحدث إصدار من WordPress ، ولكن اختبر إصداراتك على الإصدارات القديمة. على وجه الخصوص ، أقترح عليك اختبار المكون الإضافي الخاص بك ، على الأقل ، مع الحد الأدنى من إصدار WordPress الذي من المفترض أن يدعمه المكون الإضافي. إصدار أدنى ستجده في readme.txt الخاص بالمكون الإضافي:

 === Nelio Content === ... Requires PHP: 7.0 Requires at least: 5.4 Tested up to: 5.9 ...

يعد التبديل من إصدار WordPress إلى آخر سهلاً مثل تشغيل أمر WP CLI التالي:

 wp core update --version=5.4 --force

# 3 وظائف الأسهم أصعب مما تعتقد

أخيرًا ، اسمحوا لي أن أشارك واحدة من أحدث المشاكل التي واجهتها قبل أيام قليلة فقط والتي دفعتني إلى الجنون. باختصار ، كان لدينا ملف JavaScript مشابه لهذا الملف:

 import domReady from '@wordpress/dom-ready'; domReady( () => [ ...document.querySelectorAll( '.nelio-forms-form' ) ] .forEach( initForm ) ); // Helpers // ------- const initForm = ( form ) => { ... } // ...

الذي يهيئ نماذج Nelio الخاصة بك على الواجهة الأمامية. النص واضح ومباشر ، أليس كذلك؟ إنها تحدد وظيفة مجهولة تسمى عندما يكون DOM جاهزًا. تستخدم هذه الوظيفة وظيفة مساعد (سهم) تسمى initForm . حسنًا ، كما اتضح ، يمكن لمثل هذا المثال البسيط أن يتعطل! ولكن فقط في ظل بعض الظروف المحددة (على سبيل المثال ، إذا تم "تحسين" البرنامج النصي بواسطة ملحق WPO باستخدام السمة defer ).

إليك كيفية تنفيذ JS للنص السابق:

  1. تم تحديد الوظيفة المجهولة داخل domReady
  2. يدير domReady
  3. إذا لم يكن DOM جاهزًا بعد (وعادة لا يتم ذلك عند تحميل البرنامج النصي) ، فلن يقوم domReady بتشغيل وظيفة رد الاتصال. بدلاً من ذلك ، يتتبعه ببساطة حتى يتمكن من الاتصال به لاحقًا
  4. يستمر JavaScript في تحليل الملف وتحميل وظيفة initForm
  5. بمجرد أن يصبح DOM جاهزًا ، يتم استدعاء وظيفة رد الاتصال أخيرًا

الآن ، ماذا لو ، بحلول الوقت الذي وصلنا فيه إلى الخطوة الثالثة ، يكون DOM جاهزًا ، وبالتالي ، يستدعي domReady الوظيفة المجهولة مباشرةً؟ حسنًا ، في هذه الحالة ، سيقوم البرنامج النصي بتشغيل خطأ غير محدد ، لأن initForm لا تزال undefined .

في الواقع ، الشيء الأكثر فضولًا حول كل هذا هو أن هذين الحلين متكافئان:

 domReady( aux ); const aux = () => {};
 domReady( () => aux() ); const aux = () => {}

سينتج عن JavaScript linter خطأ في الخطأ الأول فقط ، وليس الأحدث.

المحلول

هناك حلان محتملان: إما أن تحدد وظيفة المساعد باستخدام الكلمة الأساسية function وتنسى وظيفة السهم ، أو تنقل عبارة domReady في النهاية ، بعد تحديد جميع وظائف المساعد:

 domReady( aux ); function aux() { // ... }
 const aux = () => { // ... }; domReady( aux );

إذا كنت تتساءل عن سبب نجاح الحل الأول إذا كان مكافئًا للحل الأصلي الذي كان لدينا ، فالأمر كله يتعلق بكيفية عمل رفع JavaScript. باختصار ، في JavaScript ، يمكنك استخدام دالة (معرّفة function ) قبل تعريفها ، لكن لا يمكنك فعل الشيء نفسه مع المتغيرات والثوابت (وبالتالي وظائف الأسهم).

باختصار

هناك عدد كبير من الأشياء التي يمكن أن تحدث خطأ في JavaScript. لحسن الحظ ، لديهم جميعًا حلًا ، خاصة إذا انتبهنا لما نقوم به. أتمنى أن تكون قد تعلمت شيئًا جديدًا اليوم وأثق أنه بفضل الأخطاء والأخطاء التي ارتكبتها في الماضي ، ستتمكن من تجنب معاناتها في جسدك في المستقبل.

صورة مميزة بواسطة إيان ستوفر على Unsplash.