تحميل البرنامج النصي الحديث

لا يعد تمرير الرمز الصحيح لكل متصفح مهمة سهلة.

في هذه المقالة ، سننظر في عدة خيارات لكيفية حل هذه المشكلة.



يمكن أن يؤدي تمرير الكود الحديث بواسطة متصفح حديث إلى تحسين الأداء إلى حد كبير. ستكون حزم JavaScript الخاصة بك قادرة على احتواء بناء جملة حديث أكثر إحكاما أو تحسين ودعم المتصفحات القديمة.

من بين الأدوات للمطورين ، نمط الوحدة / الترميز للتحميل التعريفي لهيمنة الشفرة الحديثة أو المهيمنة ، والتي تزود المتصفحات بمصادر وتسمح لك بتحديد أي منها لاستخدام:

<script type="module" src="/modern.js"></script> <script nomodule src="/legacy.js"></script> 

لسوء الحظ ، ليس كل شيء في غاية البساطة. تؤدي طريقة HTML الموضحة أعلاه إلى إعادة تشغيل البرنامج النصي في Edge و Safari .

ما الذي يمكن عمله؟


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

أولاً ، هناك Safari Fix . يدعم Safari 10.1 وحدات JS ، وليس سمة nomodule في البرامج النصية ، مما يسمح لها بتنفيذ كلا من الكود الحديث nomodule . ومع ذلك ، يمكن استخدام حدث beforeload غير القياسي المدعوم من Safari 10 و 11 في nomodule .

الطريقة الأولى: تنزيل ديناميكي


يمكنك حل هذه المشكلات عن طريق تطبيق برنامج تحميل نصي صغير. يشبه كيف يعمل LoadCSS . بدلاً من الأمل في تنفيذ وحدات ES-ES nomodule في nomodule ، يمكنك محاولة تنفيذ برنامج نصي nomodule كـ "اختبار مع اختبار عبث" ، وبناءً على النتيجة ، اختر تنزيل رمز حديث أو قديم.

 <!-- use a module script to detect modern browsers: --> <script type="module"> self.modern = true </script> <!-- now use that flag to load modern VS legacy code: --> <script> addEventListener('load', function() { var s = document.createElement('script') if ('noModule' in s) { // notice the casing s.type = 'module' s.src = '/modern.js' } else { s.src = '/legacy.js' } document.head.appendChild(s) }) </script> 

ولكن مع هذا النهج ، يجب عليك الانتظار حتى يكتمل البرنامج النصي للوحدة النمطية "litmus" قبل تنفيذ البرنامج النصي الصحيح. يحدث هذا لأن <sript type="module"> يعمل دائمًا بشكل غير متزامن. ولكن هناك طريقة أفضل!

يمكنك تنفيذ الخيار المستقل عن طريق التحقق مما إذا كان nomodule في المتصفح. هذا يعني أننا سنعتبر متصفحات مثل Safari 10.1 مهملة ، حتى لو كانت تدعم الوحدات النمطية. ولكن يمكن أن يكون للأفضل . هنا هو رمز ذات الصلة:

 var s = document.createElement('script') if ('noModule' in s) { // notice the casing s.type = 'module' s.src = '/modern.js' } else s.src = '/legacy.js' } document.head.appendChild(s) 

يمكن تحويل هذا بسرعة إلى وظيفة تقوم بتحميل كود حديث أو قديم ، كما توفر تحميلًا غير متزامن لها:

 <script> $loadjs("/modern.js","/legacy.js") function $loadjs(src,fallback,s) { s = document.createElement('script') if ('noModule' in s) s.type = 'module', s.src = src else s.async = true, s.src = fallback document.head.appendChild(s) } </script> 

ما هو الحل الوسط هنا؟

التحميل المسبق

نظرًا لأن الحل ديناميكي تمامًا ، فلن يتمكن المستعرض من اكتشاف موارد جافا سكريبت الخاصة بنا إلى أن يبدأ تشغيل رمز الإقلاع الذي كتبناه لإدراج نصوص حديثة أو قديمة. عادةً ما يقوم المستعرض بمسح HTML بحثًا عن الموارد التي يمكنه تنزيلها مسبقًا. تم حل هذه المشكلة ، ولكن ليس بشكل مثالي: يمكنك تحميل الإصدار الحديث من الحزمة مسبقًا في المتصفحات الحديثة باستخدام <link rl=modulpreload> .

لسوء الحظ ، لا يدعم Chrome سوى modulepreload حتى الآن.

 <link rel="modulepreload" href="/modern.js"> <script type="module">self.modern=1</script> <!-- etc --> 

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

إليك ما قد يبدو هذا الحل قيد الاستخدام:

 <link rel="modulepreload" href="/modern.js"> <script type="module">self.modern=1</script> <script> $loadjs("/modern.js","/legacy.js") function $loadjs(e,d,c){c=document.createElement("script"),self.modern?(c.src=e,c.type="module"):c.src=d,document.head.appendChild(c)} </script> 

تجدر الإشارة أيضًا إلى أن قائمة المستعرضات التي تدعم وحدات JS هي نفسها كتلك التي تدعم <link rl=preload> . بالنسبة لبعض المواقع ، قد يكون من المناسب استخدام <link rl=preload as=script crossorigin> بدلاً من modulepreload . قد يتدهور الأداء نظرًا لأن التحميل البرمجي للنص الكلاسيكي لا يعني modulepreload مع مرور الوقت ، كما هو الحال مع modulepreload .

الطريقة الثانية: تعقب وكيل المستخدم


ليس لدي مثال رمز مناسب لأن تتبع "وكيل المستخدم" مهمة غير تافهة. ولكن بعد ذلك يمكنك قراءة المقال الممتاز في مجلة Smashing.

في الواقع ، كل شيء يبدأ بنفس <scrit src=bundle.js> في HTML لجميع المتصفحات. عند طلب bundle.js ، يقوم الخادم بتوزيع سلسلة User Agent على المتصفح الطالب وتحديد JavaScript الذي سيعود - الحديث أو القديم ، اعتمادًا على كيفية التعرف على المتصفح.

النهج عالمي ، ولكنه ينطوي على عواقب وخيمة:

  • نظرًا لأن الخوادم الذكية مطلوبة ، فلن تعمل هذه الطريقة في ظل ظروف النشر الثابتة (مولدات المواقع الثابتة ، Netlify ، وما إلى ذلك).
  • يعتمد التخزين المؤقت لعناوين URL JavaScript هذه الآن على وكيل المستخدم ، وهو متقلب للغاية.
  • تعريف UA صعب وقد يؤدي إلى تصنيف خاطئ.
  • من السهل محاكاة خط وكيل المستخدم ، وتظهر UAs جديدة كل يوم.

طريقة واحدة لحل هذه القيود هي الجمع بين نمط الوحدة النمطية / الترميز مع تمايز "وكيل المستخدم" لتجنب إرسال إصدارات متعددة من الحزمة إلى نفس العنوان. يقلل هذا النهج من إمكانية التخزين المؤقت للصفحة ، ولكنه يوفر تحميلًا فعالًا: يعرف خادم توليد HTML متى يستخدمون modulepreload ومتى preload .

 function renderPage(request, response) { let html = `<html><head>...`; const agent = request.headers.userAgent; const isModern = userAgent.isModern(agent); if (isModern) { html += ` <link rel="modulepreload" href="modern.mjs"> <script type="module" src="modern.mjs"></script> `; } else { html += ` <link rel="preload" as="script" href="legacy.js"> <script src="legacy.js"></script> `; } response.end(html); } 

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

الطريقة الثالثة: غرامة المتصفحات القديمة


يكون التأثير السلبي لنمط الوحدة / الترميز واضحًا في الإصدارات القديمة من Chrome و Firefox و Safari - عددهم صغير جدًا ، لأن المستعرضات يتم تحديثها تلقائيًا. مع Edge 16-18 ، يكون الموقف مختلفًا ، ولكن هناك أمل: الإصدارات الجديدة من Edge ستستخدم محرك العرض القائم على Chromium ، والذي لا يواجه مثل هذه المشاكل.

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

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

إذا كنت تقوم بإنشاء موقع يتم الوصول إليه بشكل أساسي عن طريق الجوال أو المتصفحات الجديدة ، فبالنسبة لمعظم هؤلاء المستخدمين ، يكون أبسط نوع من نمط الوحدة / الترميز مناسب. فقط تأكد من إضافة إصلاح Safari 10.1 إذا كانت أجهزة iOS الأقدم تأتي إليك.

    iOS-. <!-- polyfill `nomodule` in Safari 10.1: --> <script type="module"> !function(e,t,n){!("noModule"in(t=e.createElement("script")))&&"onbeforeload"in t&&(n=!1,e.addEventListener("beforeload",function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()},!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove())}(document) </script> <!-- 90+% of browsers: --> <script src="modern.js" type="module'></script> <!-- IE, Edge <16, Safari <10.1, old desktop: --> <script src="legacy.js" nomodule async defer></script> 

الطريقة الرابعة: تطبيق شروط الحزمة


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

 <!-- newer browsers will not load this bundle: --> <script nomodule src="polyfills.js"></script> <!-- all browsers load this one: --> <script src="/bundle.js"></script> 

يمكنك تكوين Angular CLI لاستخدام هذا النهج مع polyfills ، كما أوضح Minko Gachev. بعد أن تعلمت عن هذا النهج ، أدركت أنه يمكنك تمكين حقن polyfill التلقائي في preact-cli - يوضح هذا PR مدى سهولة تطبيق هذه التقنية.

وإذا كنت تستخدم WebPack ، فهناك مكون إضافي مناسب لـ html-webpack-plugin ، مما يسهل إضافة nomodule إلى الحزم مع polyfills.

إذن ماذا تختار؟


الجواب يعتمد على موقفك. إذا كنت تقوم بإنشاء تطبيق عميل ، وكان HTML الخاص بك يحتوي على ما يزيد قليلاً عن <sript> ، فقد تحتاج إلى الطريقة الأولى .

إذا كنت تقوم بإنشاء موقع يتم تقديمه على الخادم ، ويمكنك تحمل تكلفة التخزين المؤقت ، فقد تكون الطريقة الثانية مناسبة لك .

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

أنا شخصياً أختار ذلك ، مع التركيز على مدة التحليل على الأجهزة المحمولة ، وليس على تكلفة التنزيل في إصدارات سطح المكتب. يعتبر مستخدمو الأجهزة المحمولة تكاليف التحليل ونقل البيانات بمثابة مصروفات فعلية (استهلاك البطارية ورسوم نقل البيانات) ، في حين أن مستخدمي سطح المكتب ليس لديهم مثل هذه القيود. أستمر أيضًا في تحسين نسبة 90٪ من المستخدمين - يستخدم الجمهور الرئيسي لمشاريعي المتصفحات الحديثة و / أو المحمولة.

ماذا تقرأ


هل تريد معرفة المزيد حول هذا الموضوع؟ يمكنك أن تبدأ من هنا:

Source: https://habr.com/ru/post/ar473994/


All Articles