تجنب مزالق التعليمات البرمجية المضمنة تلقائيًا

نشرت: 2022-03-10
ملخص سريع ↬ الإفراط في استخدام كود CSS أو JS المضمن ، بدلاً من تقديم الكود من خلال موارد ثابتة ، يمكن أن يضر بأداء الموقع. في هذه المقالة ، سوف نتعلم كيفية تحميل التعليمات البرمجية الديناميكية من خلال الملفات الثابتة بدلاً من ذلك ، وتجنب عيوب الكثير من التعليمات البرمجية المضمنة.

التضمين هو عملية تضمين محتويات الملفات مباشرة في مستند HTML: يمكن تضمين ملفات CSS داخل عنصر style ، ويمكن تضمين ملفات JavaScript داخل عنصر script :

 <style> /* CSS contents here */ </style> <script> /* JS contents here */ </script>

بطباعة الكود الموجود بالفعل في إخراج HTML ، يتجنب التضمين طلبات حظر العرض وينفذ الكود قبل عرض الصفحة. على هذا النحو ، من المفيد تحسين الأداء المتصور للموقع (أي الوقت الذي تستغرقه الصفحة لتصبح قابلة للاستخدام.) على سبيل المثال ، يمكننا استخدام المخزن المؤقت للبيانات التي يتم تسليمها فورًا عند تحميل الموقع (حوالي 14 كيلوبايت) للتضمين الأنماط الهامة ، بما في ذلك أنماط المحتوى في الجزء العلوي من الصفحة (كما تم القيام به في موقع مجلة Smashing السابق) ، وأحجام الخطوط وعرض التخطيط والارتفاعات لتجنب إعادة عرض التخطيط السريع عند تسليم بقية البيانات .

ومع ذلك ، عند المبالغة في ذلك ، يمكن أن يكون للشفرة المضمّنة أيضًا تأثيرات سلبية على أداء الموقع: نظرًا لأن الرمز غير قابل للتخزين المؤقت ، يتم إرسال نفس المحتوى إلى العميل بشكل متكرر ، ولا يمكن تخزينه مؤقتًا مسبقًا من خلال عمال الخدمة ، أو مخبأة والوصول إليها من شبكة توصيل المحتوى. بالإضافة إلى ذلك ، تعتبر البرامج النصية المضمنة غير آمنة عند تنفيذ سياسة أمان المحتوى (CSP). بعد ذلك ، تضع إستراتيجية معقولة لتضمين تلك الأجزاء الهامة من CSS و JS التي تجعل تحميل الموقع أسرع ولكن يتم تجنبها قدر الإمكان بخلاف ذلك.

بهدف تجنب التضمين ، سنستكشف في هذه المقالة كيفية تحويل التعليمات البرمجية المضمنة إلى أصول ثابتة: بدلاً من طباعة الكود في إخراج HTML ، نقوم بحفظه على القرص (إنشاء ملف ثابت بشكل فعال) وإضافة <script> أو علامة <link> لتحميل الملف.

هيا بنا نبدأ!

يوصى بقراءة : أمن ووردبريس كعملية

المزيد بعد القفز! أكمل القراءة أدناه ↓

متى تتجنب التبطين

لا توجد وصفة سحرية لتحديد ما إذا كان يجب تضمين بعض التعليمات البرمجية أم لا ، ومع ذلك ، يمكن أن يكون واضحًا جدًا عندما لا يجب تضمين بعض التعليمات البرمجية: عندما تتضمن جزءًا كبيرًا من التعليمات البرمجية ، وعندما لا تكون هناك حاجة إليها على الفور.

كمثال ، مواقع WordPress مضمنة في قوالب JavaScript لعرض Media Manager (يمكن الوصول إليه في صفحة مكتبة الوسائط تحت /wp-admin/upload.php ) ، وطباعة كمية كبيرة من التعليمات البرمجية:

لقطة شاشة لكود المصدر لصفحة مكتبة الوسائط
قوالب جافا سكريبت المضمنة بواسطة WordPress Media Manager.

يشغل حجمًا كاملًا يبلغ 43 كيلوبايت ، ولا يُهمل حجم هذا الجزء من الشفرة ، ولأنه يقع في أسفل الصفحة ، فلا داعي له على الفور. وبالتالي ، سيكون من المنطقي خدمة هذا الرمز من خلال أصول ثابتة بدلاً من ذلك أو طباعته داخل إخراج HTML.

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

التسبب في إنشاء ملفات ثابتة

إذا كانت المحتويات (التي سيتم تضمينها) تأتي من ملف ثابت ، فلا يوجد الكثير للقيام به سوى طلب هذا الملف الثابت بدلاً من تضمين الكود.

بالنسبة إلى الكود الديناميكي ، يجب أن نخطط لكيفية / وقت إنشاء الملف الثابت بمحتوياته. على سبيل المثال ، إذا كان الموقع يوفر خيارات التكوين (مثل تغيير نظام الألوان أو صورة الخلفية) ، فمتى يجب إنشاء الملف الذي يحتوي على القيم الجديدة؟ لدينا الفرص التالية لإنشاء ملفات ثابتة من الكود الديناميكي:

  1. تحت الطلب
    عندما يصل المستخدم إلى المحتوى لأول مرة.
  2. على التغيير
    عندما يتغير مصدر الشفرة الديناميكية (مثل قيمة التكوين).

دعونا نفكر عند الطلب أولاً. في المرة الأولى التي يصل فيها المستخدم إلى الموقع ، دعنا نقول من خلال /index.html ، لم يكن الملف الثابت (مثل header-colors.css ) موجودًا بعد ، لذا يجب إنشاؤه بعد ذلك. تسلسل الأحداث هو كما يلي:

  1. يطلب المستخدم /index.html ؛
  2. عند معالجة الطلب ، يتحقق الخادم من وجود الملف header-colors.css . نظرًا لأنه لم يحدث ذلك ، فإنه يحصل على شفرة المصدر ويقوم بإنشاء الملف على القرص ؛
  3. يقوم بإرجاع استجابة إلى العميل ، بما في ذلك العلامة <link rel="stylesheet" type="text/css" href="/staticfiles/header-colors.css">
  4. يقوم المتصفح بجلب جميع الموارد المضمنة في الصفحة ، بما في ذلك header-colors.css ؛
  5. بحلول ذلك الوقت ، يكون هذا الملف موجودًا ، لذلك يتم تقديمه.

ومع ذلك ، قد يكون تسلسل الأحداث مختلفًا أيضًا ، مما يؤدي إلى نتيجة غير مرضية. على سبيل المثال:

  1. يطلب المستخدم /index.html ؛
  2. تم تخزين هذا الملف مؤقتًا بالفعل بواسطة المتصفح (أو وكيل آخر ، أو من خلال عمال الخدمة) ، لذلك لا يتم إرسال الطلب أبدًا إلى الخادم ؛
  3. يقوم المتصفح بجلب جميع الموارد المضمنة في الصفحة ، بما في ذلك header-colors.css . ومع ذلك ، لا يتم تخزين هذه الصورة مؤقتًا في المتصفح ، لذلك يتم إرسال الطلب إلى الخادم ؛
  4. لم يقم الخادم بإنشاء header-colors.css حتى الآن (على سبيل المثال ، تم إعادة تشغيله للتو) ؛
  5. سيعود 404.

بدلاً من ذلك ، يمكننا إنشاء header-colors.css ليس عند طلب /index.html ، ولكن عند طلب /header-colors.css نفسه. ومع ذلك ، نظرًا لعدم وجود هذا الملف في البداية ، تم التعامل مع الطلب بالفعل على أنه 404. على الرغم من أنه يمكننا اختراق طريقنا ، وتغيير الرؤوس لتغيير رمز الحالة إلى 200 ، وإعادة محتوى الصورة ، هذه طريقة رهيبة للقيام بالأشياء ، لذلك لن نفكر في هذا الاحتمال (نحن أفضل بكثير من هذا!)

هذا يترك خيارًا واحدًا فقط: إنشاء ملف ثابت بعد تغيير مصدره.

إنشاء ملف ثابت عند تغيير المصدر

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

باختصار ، لدينا هاتان الحالتان:

  1. تكوين المستخدم
    يجب بدء العملية عندما يقوم المستخدم بتحديث التكوين.
  2. تصميم الموقع
    يجب بدء العملية عندما يقوم المسؤول بتحديث تكوين للموقع ، أو قبل نشر الموقع.

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

عند تصميم العملية ، سيحتاج الكود الخاص بنا إلى التعامل مع الظروف المحددة لكل من # 1 و # 2:

  • الإصدار
    يجب الوصول إلى الملف الثابت باستخدام معلمة "version" ، لإلغاء صلاحية الملف السابق عند إنشاء ملف ثابت جديد. بينما يمكن أن يحتوي # 2 ببساطة على نفس إصدار الموقع ، يحتاج # 1 إلى استخدام إصدار ديناميكي لكل مستخدم ، وربما يتم حفظه في قاعدة البيانات.
  • موقع الملف الذي تم إنشاؤه
    # 2 ينشئ ملفًا ثابتًا فريدًا للموقع بالكامل (على سبيل المثال /staticfiles/header-colors.css ) ، بينما ينشئ # 1 ملفًا ثابتًا لكل مستخدم (على سبيل المثال /staticfiles/users/leo/header-colors.css ).
  • بدء الحدث
    بينما بالنسبة إلى رقم 1 ، يجب تنفيذ الملف الثابت في وقت التشغيل ، بالنسبة إلى # 2 ، يمكن أيضًا تنفيذه كجزء من عملية الإنشاء في بيئة التدريج الخاصة بنا.
  • الانتشار والتوزيع
    يمكن دمج الملفات الثابتة في رقم 2 بسلاسة داخل حزمة نشر الموقع ، مما لا يمثل أي تحديات ؛ ومع ذلك ، لا يمكن للملفات الثابتة في رقم 1 ، لذلك يجب أن تعالج العملية مخاوف إضافية ، مثل عدة خوادم خلف موازن التحميل (هل سيتم إنشاء الملفات الثابتة في خادم واحد فقط ، أم في كل منهم ، وكيف؟).

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

  1. header-colors.css ، مع بعض الأنماط من القيم المحفوظة في قاعدة البيانات
  2. welcomeuser-data.js ، يحتوي على كائن JSON ببيانات مستخدم تحت بعض المتغيرات: window.welcomeUserData = {name: "Leo"}; .

أدناه ، سأصف عملية إنشاء الملفات الثابتة لـ WordPress ، والتي يجب أن نبني المكدس على وظائف PHP و WordPress. يمكن تشغيل وظيفة إنشاء الملفات الثابتة قبل النشر عن طريق تحميل صفحة خاصة تنفذ الرمز القصير [create_static_files] كما وصفته في مقالة سابقة.

يوصى بمزيد من القراءة : عمل عامل خدمة: دراسة حالة

تمثيل الملف ككائن

يجب علينا نمذجة ملف ككائن PHP مع جميع الخصائص المقابلة ، حتى نتمكن من حفظ الملف على القرص في موقع محدد (على سبيل المثال إما تحت /staticfiles/ أو /staticfiles/users/leo/ ) ، ومعرفة كيفية طلب ملف وبالتالي. لهذا ، نقوم بإنشاء واجهة Resource تُرجع كلاً من البيانات الوصفية للملف (اسم الملف ، dir ، النوع: "css" أو "js" ، الإصدار ، والتبعيات على الموارد الأخرى) ومحتواها.

 interface Resource { function get_filename(); function get_dir(); function get_type(); function get_version(); function get_dependencies(); function get_content(); }

من أجل جعل الكود قابلاً للصيانة وإعادة الاستخدام ، نتبع مبادئ SOLID ، والتي وضعنا لها مخططًا لوراثة الكائن للموارد لإضافة الخصائص تدريجياً ، بدءًا من الفئة المجردة ResourceBase التي سترث منها جميع تطبيقات الموارد لدينا:

 abstract class ResourceBase implements Resource { function get_dependencies() { // By default, a file has no dependencies return array(); } }

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

 abstract class UserResourceBase extends ResourceBase { function get_dir() { // A different file and folder for each user $user = wp_get_current_user(); return "/staticfiles/users/{$user->user_login}/"; } function get_version() { // Save the resource version for the user under her meta data. // When the file is regenerated, must execute `update_user_meta` to increase the version number $user_id = get_current_user_id(); $meta_key = "resource_version_".$this->get_filename(); return get_user_meta($user_id, $meta_key, true); } } abstract class SiteResourceBase extends ResourceBase { function get_dir() { // All files are placed in the same folder return "/staticfiles/"; } function get_version() { // Same versioning as the site, assumed defined under a constant return SITE_VERSION; } }

أخيرًا ، في المستوى الأخير ، نقوم بتنفيذ الكائنات للملفات التي نريد إنشاءها ، وإضافة اسم الملف ونوع الملف والشفرة الديناميكية من خلال الوظيفة get_content :

 class HeaderColorsSiteResource extends SiteResourceBase { function get_filename() { return "header-colors"; } function get_type() { return "css"; } function get_content() { return sprintf( " .site-title a { color: #%s; } ", esc_attr(get_header_textcolor()) ); } } class WelcomeUserDataUserResource extends UserResourceBase { function get_filename() { return "welcomeuser-data"; } function get_type() { return "js"; } function get_content() { $user = wp_get_current_user(); return sprintf( "window.welcomeUserData = %s;", json_encode( array( "name" => $user->display_name ) ) ); } }

بهذا ، قمنا بنمذجة الملف ككائن PHP. بعد ذلك ، نحتاج إلى حفظه على القرص.

حفظ الملف الثابت على القرص

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

 class ResourceUtils { protected static function get_file_relative_path($fileObject) { return $fileObject->get_dir().$fileObject->get_filename().".".$fileObject->get_type(); } static function get_file_path($fileObject) { // Notice that we must add constant WP_CONTENT_DIR to make the path absolute when saving the file return WP_CONTENT_DIR.self::get_file_relative_path($fileObject); } } class ResourceGenerator { static function save($fileObject) { $file_path = ResourceUtils::get_file_path($fileObject); $handle = fopen($file_path, "wb"); $numbytes = fwrite($handle, $fileObject->get_content()); fclose($handle); } }

بعد ذلك ، عندما يتغير المصدر ويحتاج الملف الثابت إلى إعادة إنشاء ، نقوم بتنفيذ ResourceGenerator::save تمرير الكائن الذي يمثل الملف كمعامل. يُعيد الرمز أدناه إنشاء ملفات "header-colours.css" و "welcomeuser-data.js" وحفظها على القرص:

 // When need to regenerate header-colors.css, execute: ResourceGenerator::save(new HeaderColorsSiteResource()); // When need to regenerate welcomeuser-data.js, execute: ResourceGenerator::save(new WelcomeUserDataUserResource());

بمجرد وجودها ، يمكننا إدراج الملفات في قائمة الانتظار ليتم تحميلها من خلال علامتي <script> و <link> .

استدراج الملفات الثابتة

لا يختلف وضع الملفات الثابتة في قائمة انتظار عن أي مورد في WordPress: من خلال الدالتين wp_enqueue_script و wp_enqueue_style . بعد ذلك ، نقوم ببساطة بتكرار جميع مثيلات الكائن واستخدام خطاف واحد أو آخر اعتمادًا على قيمة get_type() إما "js" أو "css" .

نضيف أولاً وظائف الأداة المساعدة لتوفير عنوان URL للملف ، ولإخبار النوع بأنه إما JS أو CSS:

 class ResourceUtils { // Continued from above... static function get_file_url($fileObject) { // Add the site URL before the file path return get_site_url().self::get_file_relative_path($fileObject); } static function is_css($fileObject) { return $fileObject->get_type() == "css"; } static function is_js($fileObject) { return $fileObject->get_type() == "js"; } }

سيحتوي مثيل الفئة ResourceEnqueuer على كافة الملفات التي يجب تحميلها ؛ عند الاستدعاء ، فإن وظائفها enqueue_scripts و enqueue_styles ستعمل على وضع قوائم الانتظار ، عن طريق تنفيذ وظائف WordPress المقابلة ( wp_enqueue_script و wp_enqueue_style على التوالي):

 class ResourceEnqueuer { protected $fileObjects; function __construct($fileObjects) { $this->fileObjects = $fileObjects; } protected function get_file_properties($fileObject) { $handle = $fileObject->get_filename(); $url = ResourceUtils::get_file_url($fileObject); $dependencies = $fileObject->get_dependencies(); $version = $fileObject->get_version(); return array($handle, $url, $dependencies, $version); } function enqueue_scripts() { $jsFileObjects = array_map(array(ResourceUtils::class, 'is_js'), $this->fileObjects); foreach ($jsFileObjects as $fileObject) { list($handle, $url, $dependencies, $version) = $this->get_file_properties($fileObject); wp_register_script($handle, $url, $dependencies, $version); wp_enqueue_script($handle); } } function enqueue_styles() { $cssFileObjects = array_map(array(ResourceUtils::class, 'is_css'), $this->fileObjects); foreach ($cssFileObjects as $fileObject) { list($handle, $url, $dependencies, $version) = $this->get_file_properties($fileObject); wp_register_style($handle, $url, $dependencies, $version); wp_enqueue_style($handle); } } }

أخيرًا ، قمنا بإنشاء مثيل لكائن من الفئة ResourceEnqueuer بقائمة من كائنات PHP التي تمثل كل ملف ، وأضفنا ربط WordPress لتنفيذ عملية وضع قائمة الانتظار:

 // Initialize with the corresponding object instances for each file to enqueue $fileEnqueuer = new ResourceEnqueuer( array( new HeaderColorsSiteResource(), new WelcomeUserDataUserResource() ) ); // Add the WordPress hooks to enqueue the resources add_action('wp_enqueue_scripts', array($fileEnqueuer, 'enqueue_scripts')); add_action('wp_print_styles', array($fileEnqueuer, 'enqueue_styles'));

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

بعد ذلك ، يمكننا تطبيق العديد من التحسينات لتحقيق مكاسب إضافية في الأداء.

يوصى بالقراءة : مقدمة للاختبار الآلي لملحقات WordPress باستخدام PHPUnit

تجميع الملفات معًا

على الرغم من أن HTTP / 2 قد قلل من الحاجة إلى تجميع الملفات ، إلا أنه لا يزال يجعل الموقع أسرع ، لأن ضغط الملفات (على سبيل المثال من خلال GZip) سيكون أكثر فعالية ، ولأن المتصفحات (مثل Chrome) لديها عبء أكبر لمعالجة العديد من الموارد .

حتى الآن ، قمنا بنمذجة ملف ككائن PHP ، مما يسمح لنا بمعاملة هذا الكائن كمدخل إلى عمليات أخرى. على وجه الخصوص ، يمكننا تكرار نفس العملية أعلاه لتجميع جميع الملفات من نفس النوع معًا وخدمة الإصدار المجمع بدلاً من جميع الملفات المستقلة. لهذا ، قمنا بإنشاء دالة get_content التي تقوم ببساطة باستخراج المحتوى من كل مورد ضمن $fileObjects ، وطباعته مرة أخرى ، مما ينتج عنه تجميع كل المحتوى من جميع الموارد:

 abstract class SiteBundleBase extends SiteResourceBase { protected $fileObjects; function __construct($fileObjects) { $this->fileObjects = $fileObjects; } function get_content() { $content = ""; foreach ($this->fileObjects as $fileObject) { $content .= $fileObject->get_content().PHP_EOL; } return $content; } }

يمكننا تجميع كل الملفات معًا في ملف bundled-styles.css عن طريق إنشاء فئة لهذا الملف:

 class StylesSiteBundle extends SiteBundleBase { function get_filename() { return "bundled-styles"; } function get_type() { return "css"; } }

أخيرًا ، نقوم ببساطة بإدراج هذه الملفات المجمعة في قائمة ، كما في السابق ، بدلاً من جميع الموارد المستقلة. بالنسبة إلى CSS ، نقوم بإنشاء حزمة تحتوي على ملفات header-colors.css و background-image.css و font-sizes.css ، والتي من أجلها نقوم ببساطة بإنشاء مثيل StylesSiteBundle مع كائن PHP لكل من هذه الملفات (وبالمثل يمكننا إنشاء JS ملف حزمة):

 $fileObjects = array( // CSS new HeaderColorsSiteResource(), new BackgroundImageSiteResource(), new FontSizesSiteResource(), // JS new WelcomeUserDataUserResource(), new UserShoppingItemsUserResource() ); $cssFileObjects = array_map(array(ResourceUtils::class, 'is_css'), $fileObjects); $jsFileObjects = array_map(array(ResourceUtils::class, 'is_js'), $fileObjects); // Use this definition of $fileEnqueuer instead of the previous one $fileEnqueuer = new ResourceEnqueuer( array( new StylesSiteBundle($cssFileObjects), new ScriptsSiteBundle($jsFileObjects) ) );

هذا هو. الآن سنطلب ملف JS واحد فقط وملف CSS واحد بدلاً من العديد.

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

سمات async / defer لموارد JS

يمكننا إضافة سمات غير async defer إلى علامة <script> ، لتغيير وقت تنزيل ملف جافا سكريبت وتحليله وتنفيذه ، وذلك لإعطاء الأولوية لجافا سكريبت المهم ودفع كل شيء غير حاسم إلى وقت متأخر قدر الإمكان ، وبالتالي تقليل التحميل الظاهري للموقع زمن.

لتنفيذ هذه الميزة ، باتباع مبادئ SOLID ، يجب علينا إنشاء واجهة JSResource جديدة (التي ترث من Resource ) تحتوي على وظائف is_async و is_defer . ومع ذلك ، سيؤدي هذا إلى إغلاق الباب أمام علامات <style> التي تدعم هذه السمات في النهاية أيضًا. لذلك ، مع وضع القدرة على التكيف في الاعتبار ، فإننا نتبع نهجًا أكثر انفتاحًا: نضيف ببساطة طريقة عامة get_attributes إلى واجهة Resource لإبقائها مرنة للإضافة إلى أي سمة (سواء الموجودة بالفعل أو لم يتم اختراعها بعد) لكليهما <script> و <link> العلامتان:

 interface Resource { // Continued from above... function get_attributes(); } abstract class ResourceBase implements Resource { // Continued from above... function get_attributes() { // By default, no extra attributes return ''; } }

لا يقدم WordPress طريقة سهلة لإضافة سمات إضافية إلى الموارد المدرجة في قائمة الانتظار ، لذلك نقوم بذلك بطريقة مبتذلة إلى حد ما ، بإضافة خطاف يحل محل سلسلة داخل العلامة من خلال الوظيفة add_script_tag_attributes :

 class ResourceEnqueuerUtils { protected static tag_attributes = array(); static function add_tag_attributes($handle, $attributes) { self::tag_attributes[$handle] = $attributes; } static function add_script_tag_attributes($tag, $handle, $src) { if ($attributes = self::tag_attributes[$handle]) { $tag = str_replace( " src='${src}'>", " src='${src}' ".$attributes.">", $tag ); } return $tag; } } // Initize by connecting to the WordPress hook add_filter( 'script_loader_tag', array(ResourceEnqueuerUtils::class, 'add_script_tag_attributes'), PHP_INT_MAX, 3 );

نضيف سمات أحد الموارد عند إنشاء مثيل الكائن المقابل:

 abstract class ResourceBase implements Resource { // Continued from above... function __construct() { ResourceEnqueuerUtils::add_tag_attributes($this->get_filename(), $this->get_attributes()); } }

أخيرًا ، إذا لم يكن من الضروري تنفيذ المورد welcomeuser-data.js على الفور ، فيمكننا تعيينه على أنه defer :

 class WelcomeUserDataUserResource extends UserResourceBase { // Continued from above... function get_attributes() { return "defer='defer'"; } }

نظرًا لأنه يتم تحميله على أنه مؤجل ، فسيتم تحميل النص البرمجي لاحقًا ، مما يعرض النقطة الزمنية التي يمكن للمستخدم فيها التفاعل مع الموقع. فيما يتعلق بمكاسب الأداء ، نحن جميعًا على استعداد الآن!

هناك مشكلة واحدة متبقية يجب حلها قبل أن نتمكن من الاسترخاء: ماذا يحدث عندما يتم استضافة الموقع على خوادم متعددة؟

التعامل مع خوادم متعددة خلف موازن التحميل

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

الحل الذي توصلت إليه هو إضافة مستوى من المراوغة: بدلاً من طلب الملفات الثابتة من عنوان URL للموقع ، يتم طلبها من موقع في السحابة ، مثل حاوية AWS S3. بعد ذلك ، عند إعادة إنشاء الملف ، سيقوم الخادم فورًا بتحميل الملف الجديد إلى S3 وتقديمه من هناك. تم شرح تنفيذ هذا الحل في مقالتي السابقة مشاركة البيانات بين الخوادم المتعددة من خلال AWS S3.

خاتمة

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

ومن ثم ، فقد ابتكرنا طريقة لجعل موقع الويب أسرع عن طريق تحويل كود JS و CSS المضمّن الديناميكي إلى موارد ثابتة ، والتي يمكن أن تعزز التخزين المؤقت على عدة مستويات (في العميل ، عمال الخدمة ، CDN) ، مما يسمح بتجميع جميع الملفات معًا. في مورد JS / CSS واحد فقط لتحسين النسبة عند ضغط الإخراج (مثل من خلال GZip) ولتجنب الحمل الزائد في المتصفحات من معالجة عدة موارد بشكل async (مثل Chrome) ، ويسمح بالإضافة إلى ذلك بإضافة سمات غير متزامنة أو defer إلى العلامة <script> لتسريع تفاعل المستخدم ، وبالتالي تحسين وقت التحميل الظاهري للموقع.

كأثر جانبي مفيد ، يتيح تقسيم الكود إلى موارد ثابتة أيضًا أن تكون الشفرة أكثر وضوحًا ، والتعامل مع وحدات التعليمات البرمجية بدلاً من نقاط HTML الكبيرة ، مما قد يؤدي إلى صيانة أفضل للمشروع.

الحل الذي طورناه تم تنفيذه في PHP ويتضمن بضع أجزاء محددة من التعليمات البرمجية لـ WordPress ، ومع ذلك ، فإن الكود نفسه بسيط للغاية ، وبالكاد توجد واجهات قليلة تحدد الخصائص والكائنات التي تنفذ تلك الخصائص وفقًا لمبادئ SOLID ، ووظيفة لحفظ ملف على القرص. هذا الى حد كبير ذلك. والنتيجة النهائية واضحة ومضغوطة ومباشرة لإعادة إنشائها لأي لغة ومنصة أخرى ، وليس من الصعب تقديمها لمشروع قائم - مما يوفر مكاسب سهلة في الأداء.