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

لديك فكرة
روجت أنت وفريقك بحماس لفكرة الإصلاح الشامل لموقع الويب الخاص بالشيخوخة في الشركة. وصلت طلباتك إلى القيادة ، حتى جاء في نظر أولئك في القمة. لقد أعطيت ضوء أخضر. بدأ فريقك في العمل بحماس وجذب المصممين والمؤلفين وغيرهم من المتخصصين. قريبا انتشرت مدونة جديدة.
بدأ العمل على ذلك ببراءة.
npm install
أمر
npm install
هنا ،
npm install
أمر
npm install
. ما إن نظرت من حولك ، فقد تم بالفعل اعتماد تبعيات الإنتاج كما لو أن تطوير المشروع كان عبارة عن هراء ، وكنت أنت الذي لم يهتم مطلقًا بما سيكون صباح الغد.
ثم بدأت.
ولكن ، على عكس عواقب حفل جنون الشرب ، لم يبدأ الشيء الرهيب في صباح اليوم التالي. لسوء الحظ - ليس في صباح اليوم التالي. جاء الحساب في أشهر. أخذت شكل غير سارة من الغثيان والصداع المعتدل لأصحاب الشركات والمديرين المتوسطين الذين تساءلوا لماذا ، بعد إطلاق الموقع الجديد ، انخفضت التحويلات والإيرادات. ثم اكتسبت الكارثة زخما. حدث هذا عندما عاد المدير الفني من عطلة نهاية الأسبوع ، والذي قضى في مكان ما خارج المدينة. تساءل عن سبب تحميل موقع الشركة ببطء شديد (إن وجد) على هاتفه.
اعتادت أن تكون جيدة للجميع. الآن قد حان الأوقات المظلمة الأخرى. قابل صداع الكحول الأول بعد تناول جرعة كبيرة من جافا سكريبت.
هذا ليس خطأك.
بينما كنت تحاول التغلب على مخلفات جهنم ، فإن كلمات مثل "لقد أخبرتك بذلك" قد تبدو بمثابة توبيخ مستحق لك. وإذا كنت ستتمكن من القتال في ذلك الوقت ، فيمكن أن تكون مناسبة للقتال.
عندما يتعلق الأمر بعواقب الاستخدام المتهور لجافا سكريبت ، يمكنك إلقاء اللوم على كل شيء والجميع. لكن البحث عن المذنب مضيعة للوقت. يتطلب جهاز الويب الحديث نفسه من الشركات حل المشكلات بشكل أسرع من منافسيها. مثل هذا الضغط يعني أننا ، في سعينا لزيادة إنتاجيتنا إلى أقصى حد ممكن ، من المحتمل أن نستحوذ على أي شيء. هذا يعني أننا ، مع وجود درجة عالية من الاحتمالية (على الرغم من أنه لا يمكن اعتبار ذلك أمرًا لا مفر منه) ، فإننا سننشئ تطبيقات سيكون فيها الكثير من التجاوزات ، وعلى الأرجح ، سوف نستخدم أنماطًا تضر بأداء التطبيقات وتوافرها.
تطوير الويب ليست مهمة سهلة. هذه وظيفة طويلة نادرا ما يتم تنفيذها بشكل جيد في المحاولة الأولى. ومع ذلك ، فإن أفضل شيء في هذا العمل هو أنه لا يتعين علينا القيام بكل شيء بشكل مثالي في البداية. يمكننا إجراء تحسينات على المشاريع بعد إطلاقها ، وفي الواقع ، هذه المادة مخصصة لهذا ، والثانية في سلسلة من المقالات حول نهج مسؤول لتطوير JS. الكمال هو هدف بعيد المنال. في غضون ذلك ، دعونا نتعامل مع مخلفات JavaScript عن طريق تحسين
البرمجة النصية إذا جاز التعبير
على الموقع في المستقبل القريب.
نحن نتعامل مع المشاكل الشائعة
قد يبدو هذا كأنه أسلوب ميكانيكي لحل المشكلات ، لكن أولاً استعرض قائمة من المشاكل النموذجية وطرق التعامل معها. في فرق التطوير الكبيرة ، غالباً ما يتم نسيان هذه الأشياء. ينطبق هذا بشكل خاص على تلك الفرق التي تعمل مع مستودعات متعددة أو لا تستخدم قالبًا محسّنًا لمشاريعها.
▍ تطبيق خوارزمية تهتز الأشجار
أولاً ، تحقق مما إذا كانت أدواتك قد تم تكوينها لتطبيق خوارزمية
اهتزاز الشجرة . إذا لم تصادفك هذا المفهوم من قبل ، فقم بإلقاء نظرة على هذه المادة الخاصة بي المكتوبة العام الماضي. إذا شرحنا تشغيل هذه الخوارزمية باختصار ، فيمكننا القول أنه نظرًا لاستخدامها في مجموعات الإنتاج الخاصة بالتطبيق ، لا تتضمن تلك الحزم التي ، على الرغم من استيرادها إلى المشروع ، لا تستخدم فيها.
يعد تطبيق خوارزمية اهتزاز الأشجار ميزة قياسية في
حزم حديثة مثل
webpack أو
Rollup أو
Parcel .
نخر أو
بلع مديري المهام. إنهم لا يفعلون هذا. لا يقوم مدير المهام ، على عكس المجمع ، بإنشاء
رسم بياني تبعية . يعمل مدير المهام ، باستخدام المكونات الإضافية الضرورية ، ويقوم بمعالجة منفصلة على الملفات المنقولة إليه. يمكن توسيع وظيفة مديري المهام باستخدام المكونات الإضافية ، مما يتيح لهم القدرة على معالجة جافا سكريبت باستخدام حزم. إذا كان توسيع قدرات مدير المهام في هذا الاتجاه يمثل مشكلة ، فربما تحتاج إلى التحقق من قاعدة الشفرة يدويًا وإزالة الكود غير المستخدم منها.
لكي تعمل خوارزمية اهتزاز الأشجار بكفاءة ، يجب استيفاء الشروط التالية:
- يجب تقديم رمز التطبيق والحزم المثبتة كوحدات ES6 . من المستحيل تقريبًا استخدام خوارزمية اهتزاز الشجرة لوحدات CommonJS .
- يجب ألا يحول المجمّع الخاص بك وحدات ES6 إلى وحدات بنسق آخر أثناء بناء المشروع. إذا حدث هذا في سلسلة الأدوات التي تستخدم Babel ، فيجب أن يكون لدى @ Babel / present-env الوحدات النمطية: إعداد خاطئ . سيؤدي ذلك إلى عدم تحويل رمز ES6 إلى رمز يستخدم CommonJS.
إذا فجأة ، عند إنشاء مشروعك ، لا يتم تطبيق خوارزمية اهتزاز الأشجار ، يمكن أن يؤدي تضمين هذه الآلية إلى تحسين الموقف. بالطبع ، تختلف فعالية هذه الخوارزمية من مشروع لآخر. بالإضافة إلى ذلك ، تعتمد إمكانية استخدامه على ما إذا كانت الوحدات المستوردة لها
آثار جانبية . قد يؤثر ذلك على قدرة المجمّع على التخلص من تضمين الوحدات النمطية المستوردة غير الضرورية في التجميع.
iv تقسيم الرمز إلى أجزاء
من المحتمل جدًا أنك تستخدم بالفعل شكلًا من أشكال
فصل الشفرة . ومع ذلك ، يجب عليك التحقق من كيفية القيام بذلك. بغض النظر عن كيفية الفصل بين الشفرة ، أريد أن أقدم لك السؤالين التاليين:
- هل تزيل الشفرة المكررة من نقاط الإدخال ؟
- هل تحميل بتكاسل كل ما يمكن تحميله بهذه الطريقة مع الواردات الحيوية ؟
تعد هذه المشكلات مهمة لأن تقليل مقدار التعليمات البرمجية المكررة عنصر أداء أساسي. كسول تحميل رمز يحسن أيضا الأداء عن طريق تقليل كمية شفرة جافا سكريبت التي هي جزء من الصفحة وتحميل عندما يتم تحميله. إذا تحدثنا عن تحليل المشروع لوجود شفرة زائدة في ذلك ، فيمكنك لذلك استخدام نوع من الأدوات مثل Bundle Buddy. إذا واجه مشروعك مشكلة في هذا ، فستخبرك هذه الأداة عن ذلك.
يمكن لأداة Bundle Buddy التحقق من معلومات تجميع حزمة الويب ومعرفة مقدار الشفرة المستخدمة في حزمكإذا تحدثنا عن التحميل البطيء للمواد ، فإن معرفة مكان البحث عن فرص لتطبيق هذا التحسين يمكن أن يكون بعض الصعوبة. عندما أقوم بالبحث في مشروع قائم عن إمكانية استخدام التحميل البطيء ، فإنني أبحث في قاعدة الشفرة عن تلك الأماكن التي تتضمن تفاعلات المستخدم مع الكود. يمكن أن يكون ، على سبيل المثال ، معالجات أحداث الماوس أو لوحة المفاتيح ، وكذلك أشياء أخرى مماثلة. أي تعليمة برمجية تتطلب إجراء من المستخدم لتشغيله هو مرشح جيد لتطبيق أمر
import()
الحيوي
import()
عليه.
بالطبع ، ينطوي تحميل البرامج النصية عند الطلب على مخاطر التأخير الملحوظ في نقل النظام إلى الوضع التفاعلي. في الواقع ، قبل أن يتمكن البرنامج من التفاعل مع المستخدم بشكل تفاعلي ، تحتاج إلى تنزيل البرنامج النصي المناسب. إذا لم تزعجك كمية البيانات المنقولة ، ففكر في استخدام تلميح مورد
الجلب المسبق لتحميل مثل هذه البرامج النصية ذات الأولوية المنخفضة. هذه الموارد لن تنافس على عرض النطاق الترددي مع الموارد الحرجة. إذا كان متصفح المستخدم
يدعم rel=prefetch
، فإن استخدام تلميح الأدوات هذا سيكون مفيدًا فقط. إذا لم يكن الأمر كذلك ، فلن يحدث شيء سيئ ، حيث تتجاهل المتصفحات العلامات التي لا يفهمونها.
▍ استخدم خيار webpack externals لتحديد الموارد الموجودة على الخوادم الأجنبية
من الناحية المثالية ، يجب أن تستضيف أكبر عدد ممكن من تبعيات موقعك على خوادمك الخاصة. إذا اضطررت ، دون خيارات ، إلى تنزيل التبعيات من خوادم الأشخاص الآخرين - فضعها في كتلة
externals في إعدادات webpack. إذا لم يتم ذلك ، فقد يعني هذا أن زوار موقعك سوف يقومون بتنزيل كل من الكود الذي تستضيفه والرمز نفسه من خوادم الأشخاص الآخرين.
ألقِ نظرة على الموقف الافتراضي الذي قد يؤدي فيه شيء مثل هذا إلى الإضرار بمواردك. افترض أن موقعك يقوم بتنزيل مكتبة Lodash من مورد CDN عام. قمت أيضًا بتثبيت Lodash في المشروع لأغراض التنمية المحلية. ومع ذلك ، إذا لم تحدد في إعدادات حزمة الويب أن Lodash هو تبعية خارجية ، فسيقوم رمز الإنتاج بتحميل المكتبة من CDN ، ولكن في الوقت نفسه سيتم تضمينه في الحزمة المستضافة على الخادم الخاص بك.
إذا كنت على دراية تامة بموظفي الحزم ، فقد يبدو كل هذا حقيقة شائعة. لكنني رأيت كيف أن هذه الأشياء لا تولي اهتماما. لذلك ، لا تأخذ الوقت الكافي للتحقق من المشروع الخاص بك عن المشاكل المذكورة أعلاه.
إذا كنت لا ترى أنه من الضروري استضافة
تبعياتك التي أنشأها مطورو الطرف الثالث بمفردك ، ففكر في استخدام
dn -prefetch أو
preconnect أو ربما حتى تلميحات
التحميل المسبق معهم. يمكن أن يؤدي ذلك إلى تقليل درجة
TTI (Time To Interactive ، وقت الموقع إلى التفاعل الأول). وإذا كانت قدرات JavaScript مطلوبة لعرض محتويات الموقع ، فستكون هناك حاجة
أيضًا إلى
فهرس سرعة الموقع.
مكتبات بديلة أصغر وتقليل النفقات العامة على أنظمة المستخدم
ما يسمى "
Userland JavaScript " (مكتبات JS التي طورها المستخدم) يبدو أنه متجر فاحش للحلوى الفاحش. كل هذا الروعة والتنوع المفتوح المصدر يلهمنا ، المطورين ، برهبة. تتيح لنا الأطر والمكتبات توسيع تطبيقاتنا وتزويدها بسرعة بميزات تساعد في حل مجموعة متنوعة من المشكلات. إذا اضطررنا إلى تنفيذ نفس الوظيفة لوحدنا ، فسوف يستغرق ذلك الكثير من الوقت والطاقة.
على الرغم من أنني شخصياً أدافع عن التقليل إلى الحد الأدنى من استخدام أطر عمل العميل والمكتبات في مشاريعي ، إلا أنه لا يسعني إلا إدراك قيمتها وفائدتها الهائلة. ولكن على الرغم من ذلك ، عندما يتعلق الأمر بتثبيت تبعيات جديدة في المشروع ، يجب علينا التعامل مع كل واحد منهم بقدر لا بأس به من الشك. إذا سبق وأن أنشأنا شيئًا ما ، يعتمد تشغيله على العديد من التبعيات المثبتة ، فهذا يعني أننا نضع العبء الإضافي على النظام الذي ينشئه كل هذا. من المحتمل أن مطوري الحزم فقط يمكنهم التعامل مع هذه المشكلة عن طريق تحسين تنميتهم. هل هذا صحيح؟
ربما هذا هو الحال ، ولكن ربما لا. ذلك يعتمد على التبعيات المستخدمة. على سبيل المثال ، React هي مكتبة شائعة للغاية. لكن
Preact بديل
صغير جدًا لـ React ، والذي يمنح المطور نفس واجهات برمجة التطبيقات تقريبًا ويظل متوافقًا مع العديد من الوظائف الإضافية React.
Luxon و
fns التاريخ بدائل
للحظة .
js ، أكثر إحكاما من هذه المكتبة ، التي
ليست صغيرة جدا .
في المكتبات مثل
Lodash ، يمكنك العثور على العديد من الطرق المفيدة. لكن من السهل استبدال بعضها بطرق ES6 قياسية. على سبيل المثال ، يمكن استبدال أسلوب Lodash
المضغوط بطريقة صفائف
الفلتر القياسية. يمكن أيضًا استبدال
العديد من أساليب Lodash بأمان بالطرق القياسية. ميزة هذا الاستبدال هي أننا نحصل على نفس ميزات استخدام المكتبة ، لكن نتخلص من التبعية الكبيرة إلى حد ما.
أيا كان ما تستخدمه ، تظل الفكرة العامة كما هي: اسأل ما إذا كان اختيارك يتضمن بدائل أكثر إحكاما. معرفة ما إذا كان يمكنك حل نفس المشاكل مع أدوات اللغة القياسية. ربما ستفاجأ بسرور من قلة الجهود التي تبذلها لتقليل حجم التطبيق ومقدار الحمل غير الضروري الذي يمارسه على أنظمة المستخدمين.
استخدام تقنيات التحميل التفاضلية للبرنامج النصي
هي احتمالات بابل في toolchain الخاص بك. تُستخدم هذه الأداة لتحويل شفرة المصدر المتوافقة مع ES6 إلى رمز يمكن للمتصفحات القديمة تشغيله. هل هذا يعني أننا محكوم علينا بتقديم حزم ضخمة حتى للمتصفحات التي لا تحتاج إليها ، حتى تختفي جميع المتصفحات القديمة؟
بالطبع لا ! يساعد تحميل الموارد التفاضلي في التغلب على هذه المشكلة عن طريق إنشاء تجميعين مختلفين يستندان إلى رمز ES6:
- يتضمن التجميع الأول جميع تحويلات الشفرة والملفات المتعددة اللازمة لموقعك للعمل في المتصفحات القديمة. ربما أنت الآن تمنح العملاء هذا التجميع المعين.
- يحتوي التجميع الثاني إما على الحد الأدنى من تحويلات الشفرة وملفات polyfill ، أو بدونها على الإطلاق. إنه مصمم للمتصفحات الحديثة. هذا هو التجمع قد لا يكون لديك. على الأقل ليس بعد.
من أجل استخدام تكنولوجيا التحميل التفاضلي للتجميعات ، عليك أن تعمل قليلاً. لن أخوض في التفاصيل هنا - سأقدم
رابطًا أفضل
لمادتي التي تناقش إحدى الطرق لتطبيق هذه التكنولوجيا. جوهر كل هذا هو أنه يمكنك تعديل تكوين البناء الخاص بك بحيث يتم إنشاء نسخة إضافية من حزمة JS لموقعك أثناء إنشاء المشروع. ستكون هذه الحزمة الإضافية أصغر من الحزمة الرئيسية. سيتم تصميمه فقط للمتصفحات الحديثة. أفضل جزء هو أن هذا النهج يسمح لك بتحسين حجم الحزمة وفي نفس الوقت عدم التضحية بأي شيء من قدرات المشروع. اعتمادًا على رمز التطبيق ، يمكن أن يكون التوفير في حجم الحزمة كبيرًا.
تحليل الحزم للمتصفحات القديمة (يسار) وحزم المتصفحات الجديدة (يمين). تم إجراء دراسة الحزمة باستخدام webpack-bundle-analys. هذه هي النسخة الكاملة الحجم لهذه الصورة.من الأسهل تقديم حزم مختلفة لمتصفحات مختلفة باستخدام الخدعة التالية. يعمل بشكل جيد في المتصفحات الحديثة:
<!-- : --> <script type="module" src="/js/app.mjs"></script> <!-- : --> <script defer nomodule src="/js/app.js"></script>
لسوء الحظ ، هذا النهج له عيوب. المتصفحات القديمة مثل IE11 ، وحتى المستعرضات الحديثة نسبيًا مثل Edge Edge 15-18 ، ستقوم بتحميل كلا الحزمتين. إذا كنت مستعدًا لتحملها - فاستعمل هذه التقنية ولا تقلق بشأن أي شيء.
من ناحية أخرى ، تحتاج إلى التوصل إلى شيء ما في حالة ما إذا كنت قلقًا بشأن
تأثير تطبيقك على حقيقة أن المتصفحات القديمة عليها تنزيل كلتا الحزمتين. إليك أحد الحلول المحتملة لهذه المشكلة التي تستخدم حقن البرنامج النصي (بدلاً من العلامة
<script>
التي استخدمناها أعلاه). إنه يتجنب التحميل المزدوج للحزم بواسطة المتصفحات المناسبة. هنا ما نتحدث عنه:
var scriptEl = document.createElement("script"); if ("noModule" in scriptEl) {
يفترض هذا البرنامج النصي أنه إذا كان المستعرض يدعم سمة
nomodule في عنصر
script
، فإنه يفهم بنية
type="module"
. وهذا يضمن أن المتصفحات القديمة لن تتلقى سوى البرامج النصية المصممة لهم ، بينما ستتلقى المتصفحات الحديثة نصوصًا مصممة لهم. ومع ذلك ، ضع في اعتبارك أن البرامج النصية المضمنة ديناميكيًا يتم تحميلها بشكل غير متزامن افتراضيًا. لذلك ، إذا كان ترتيب تبعيات التحميل مهمًا بالنسبة لك ،
فقم بتعيين السمة
async على
false
.
نقل أقل
لن أهاجم بابل هنا. هذه الأداة ضرورية في تطوير الويب الحديث ، لكنها كيان متقلب للغاية. بابل يضيف الكثير من الأشياء إلى الكود الذي ينشئه ، والذي قد لا يعرفه المطور. لذلك ، لن تندم إذا نظرت إلى أحشاء بابل ومعرفة ما يفعله بالضبط. على وجه الخصوص ، توضح المعرفة بآليات بابل الداخلية أن التغييرات الصغيرة في الطريقة التي يكتب بها شخص ما يمكن أن يكون لها تأثير إيجابي على ما ينتجه بابل.
نقل أقلوهي - هذا ما نتحدث عنه. على سبيل المثال ،
تعد الخيارات الافتراضية ميزة مفيدة للغاية في ES6 قد تستخدمها بالفعل:
function logger(message, level = "log") { console[level](message); }
هنا يجدر الانتباه إلى معلمة
level
، التي تكون قيمتها الافتراضية هي
log
السلسلة. هذا يعني أننا إذا أردنا استدعاء
console.log
باستخدام وظيفة مجمّع التسجيل ، فلن نحتاج إلى تمرير
level
إلى هذه الوظيفة. مريحة ، أليس كذلك؟ كل هذا جيد - باستثناء ما يحصل عليه بابل عند تحويل هذه الوظيفة:
function logger(message) { var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "log"; console[level](message); }
هذا مثال على أنه على الرغم من حقيقة أننا نقودنا بنوايا حسنة ، فإن وسائل الراحة التي يقدمها بابل يمكن أن تؤدي إلى عواقب سلبية. ما كان مجرد عدد قليل من الشخصيات في شفرة المصدر تحولت إلى بناء أطول بكثير في إصدار إنتاج البرنامج. - , ,
arguments
.
, ,
? , Babel :
, Babel
@babel/preset-env ,
. , , , , , ! — «» (
true
loose ). — , , , , , . «»
, Babel , .
, «» , , Babel :
, — JavaScript, , .
spread ,
,
.
— :
- — @babel/runtime @babel/plugin-transform-runtime , , Babel .
- , . @babel/polyfill . , babel /preset-env useBuiltins
usage
.
, , , , , . ,
JSX , , , . , , . , , . , Babel — . , Babel. , .
: —
, . , JavaScript-, , . , , . - .
. , , , , , , , .
, , , , , , , . - — . , , , . . , , , , . , , , .
أعزائي القراء! JS-?
