اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'تعلم تطوير الويب'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

  1. سنزوّدك من خلال هذا المقال بقائمة من مصادر React التي يمكنك استخدامها للمضي قدمًا في مسار تعلّمك تطوير الواجهات الأمامية وبناء تطبيقات الويب. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript ومعرفة استخدام سطر الأوامر أو الطرفية. الهدف: توفير مصادر إضافية لمعرفة المزيد عن مكتبة React. التنسيقات على مستوى المكون تُعرِّف العديد من تطبيقات React تنسيقاتها على أساس كل مكوِّن بدلًا من أن تكون في صفحة تنسيقات واحدة متجانسة، إذ تتيح الأداة create-react-app استيراد ملفات CSS إلى وحدات جافاسكربت، بحيث تُرسَل شيفرة CSS إلى المستخدِم فقط عند تصيير Render المكوِّن المقابل، وقد كان بإمكاننا في تطبيقنا مثلًا كتابة ملف Form.css مخصّص ليحتوي على تنسيقات تلك المكونات، ثم استيراد التنسيقات إلى وحداتها كما يلي: import Form from './Form'; import './Form.css' تسهِّل هذه الطريقة تحديد وإدارة شيفرة CSS المُخصَّصة لمكوِّن معيَّن، ولكنها تؤدي إلى تجزئة ملف التنسيقات عبر قاعدة شيفرتك البرمجية، كما يمكن ألّا تكون هذه التجزئة مفيدةً، إذ يُعَدّ الحدّ من مقدار الشيفرة التي ليست ذات فائدة والمُرسَلة إلى المستخدِم، أمرًا منطقيًا بالنسبة للتطبيقات الأكبر حجمًا، والتي تحتوي على مئات من العروض الفريدة والكثير من الأجزاء المتحركة، إذ يُحتمَل أن تكون لديك تنسيقات على مستوى التطبيق وتنسيقات مكونات محدَّدة مبنية فوقها. أدوات تطوير React استخدمنا التابع console.log()‎ للتحقق من حالة تطبيقنا وخاصياته Props، إذ سترى بعض التحذيرات المفيدة ورسائل الخطأ التي تعطيها React في واجهة سطر الأوامر CLI وطرفية جافاسكربت في المتصفح. ولكن هناك المزيد الذي نستطيع إجراؤه هنا. تتيح الأداة المساعدة React DevTools فحص الأجزاء الداخلية لتطبيق React مباشرةً في المتصفح، إذ تضيف لوحةً جديدةً إلى أدوات مطور متصفحك، كما يمكنك بواسطتها فحص حالة المكوّنات المختلفة وخاصياتها، وتعديل الحالة والخاصيات لإجراء تغييرات فورية على تطبيقك، إذ تُظهر لقطة الشاشة التالية تطبيقنا النهائي كما يظهر في الأداة React DevTools: نرى على اليسار جميع المكونات التي يتألف منها تطبيقنا بما في ذلك بعض المفاتيح الفريدة للأشياء المُصيَّرة من المصفوفات، في حين نرى على اليمين الخاصيات والخطّافات Hooks التي يستخدِمها المكوِّن App، ولاحظ أنّ للمكوّنات Form وFilterButton وTodo مسافةً بادئةً إلى اليمين، وهذا يشير إلى أنّ المكوِّن App هو المكوِّن الأب لها، لذا يُعَدّ هذا العرض رائعًا في التطبيقات الأكثر تعقيدًا لفهم العلاقات بين الأبناء والآباء بسهولة، كما تتوفر الأداة React DevTools في عدد من الأشكال مثل: امتداد متصفح كروم Chrome. امتداد متصفح فايرفوكس Firefox. امتداد متصفح Chromium Edge (سيُتاح قريبًا). تطبيق مستقل يمكنك تثبيته باستخدام NPM أو Yarn. جرّب تثبيت إحدى هذه الأدوات، ثم استخدمها لفحص التطبيق الذي أنشأته للتو. واجهة برمجة تطبيقات السياق Context API استخدَم التطبيق الذي أنشأناه خاصيات المكوِّنات لتمرير البيانات من المكوِّن App إلى المكوِّنات الأبناء التي تحتاجها، إذ تُعَدّ الخاصيات طريقةً مناسبةً لمشاركة البيانات في معظم الأحيان، ولكنها ليست الأفضل دائمًا بالنسبة للتطبيقات المعقَّدة والمتداخلة كثيرًا. توفِّر React واجهة برمجة تطبيقات السياق Context API بوصفها طريقةً لتوفير البيانات للمكوّنات التي تحتاجها دون تمرير الخاصيات إلى أسفل شجرة المكونات، كما يوجد الخطّاف useContext الذي يسهِّل ذلك. أصناف المكونات يمكن بناء مكونات React باستخدام أصناف ES6 التي تُسمَّى بأصناف المكونات Class Components، إذ كانت أصناف ES6 قبل ظهور الخطّافات الطريقة الوحيدة لجلب الحالة إلى المكوّنات أو إدارة آثار التصيير الجانبية، ولا تزال الطريقة الوحيدة للتعامل مع بعض الميزات الأخرى في الحالات الطارئة، وهي شائعة جدًا في مشاريع React القديمة، كما يمكنك الاطلاع على المصادر التالية: حالة ودورة حياة المكونات في توثيق React. توثيق React على موسوعة حسوب. تعلّم لغة جافاسكربت من خلال توثيقها على موسوعة حسوب. الاختبار توفِّر create-react-app بعض الأدوات لاختبار تطبيقك، كما يغطي توثيق create-react-app بعض أساسيات الاختبار. التوجيه يُعالَج التوجيه تقليديًا باستخدام خادم وليس باستخدام تطبيق على حاسوب المستخدِم، ولكن يمكن ضبط تطبيق ويب لقراءة موقع المتصفح وتحديثه، وتصيير واجهات مستخدِم معيّنة، وهذا ما يسمى بالتوجيه من طرف العميل Client-side Routing، كما يمكن إنشاء العديد من المسارات الفريدة لتطبيقك مثل ‎/home أو ‎/dashboard أو login/‎. أنتج مجتمع React مكتبتين رئيسيتين للتوجيه من طرف العميل هما React Router وReach Router. تُعَدّ مكتبة React Router مناسبةً للتطبيقات ذات احتياجات التوجيه المعقَّدة، كما أنها تلبي بعض الحالات الطارئة بطريقة أفضل من مكتبة Reach Router، ولكن تُعَدّ React Router مكتبةً أكبر. تُعَدّ مكتبة Reach Router مناسبةً للتطبيقات الأبسط، وتدير التركيز تلقائيًا أثناء تنقل المستخدِم من صفحة إلى أخرى. تُعَدّ إدارة التركيز أمرًا ضروريًا في التوجيه من طرف العميل، إذ يمكن وقوع مستخدِمي لوحة المفاتيح في مأزق التركيز، ويمكن ألّا يكون لدى مستخدِمي قارئ الشاشة أيّ فكرة عن انتقالهم إلى صفحة جديدة، في حين تُعَدّ مكتبة Reach Router مكانًا جيدًا للبدء، لأنها أفضل من حيث إمكانية الوصول، لكن سيُدمَج هذان المشروعان في المستقبل القريب، وستكون حينها مكتبة React Router هي المشروع الباقي مع إضافة ميزات إدارة التركيز الخاصة بمكتبة Reach. أخيرًا، لا تنسى الرجوع إلى توثيق React على موسوعة حسوب فضع هذا المرجع في جعبتك (بالإضافة إلى المراجع الأخرى التي توفرها الموسوعة) والذي ستحتاج إلى الرجوع إليه بين الحين والآخر في رحلة سيرك مع مكتبة React لبناء واجهات المواقع والتطبيقات. ترجمة -وبتصرُّف- للمقال React resources. اقرأ أيضًا مكونات React الأساسية (React Components) المصطلحات المستخدمة في React تعلم البرمجة
  2. سنخطط في هذا المقال بنية تطبيق TodoMVC في إطار العمل Ember، وسنضيف توصيف HTML له، ثم سنقسّم بنية HTML إلى مكونات. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript، ومعرفة استخدام سطر الأوامر أو الطرفية. يُعَد فهم ميزات جافاسكربت الحديثة مثل الأصناف Classes والوحدات Modules وما إلى ذلك مفيدًا للغاية، لأن إطار العمل Ember يستخدِمها بكثرة. الهدف: تعلّم كيفية إنشاء بنية تطبيق Ember، ثم تقسيم هذه البنية إلى مكونات. التخطيط لتصميم تطبيق TodoMVC أنشأنا في المقال السابق مشروع Ember جديد ثم أضفنا وضبطنا أنماط CSS، وسنضيف الآن توصيف HTML وسنخطط بنية ودلالات تطبيق TodoMVC. يُعَدّ توصيف HTML لصفحة هبوط تطبيقنا مُعرَّفًا في المسار app/templates/application.hbs، وهو موجود مسبقًا وتبدو محتوياته حاليًا كما يلي: ‏{{!-- يعرض المكون التالي رسالة ترحيب‫ Ember الافتراضية. --}} <WelcomePage /> {{!-- لا تتردد في إزالتها --}} {{outlet}} المكوّن <WelcomePage /‎> هو مكون توفّره إضافة Ember التي تصيِّر Render صفحة الترحيب الافتراضية التي رأيناها في المقال السابق عندما انتقلنا لأول مرة إلى الخادم على المضيف المحلي localhost:4200، لكننا لا نريد ذلك، وإنما نريد أن يحتوي هذا المكون على بنية تطبيق TodoMVC، لذلك احذف محتويات الملف application.hbs واستبدلها بما يلي: <section class="todoapp"> <h1>todos</h1> <input class="new-todo" aria-label="What needs to be done?" placeholder="What needs to be done?" autofocus > </section> احفظ الملف application.hbs، إذ سيَعيد خادم التطوير الذي شغّلته سابقًا بناء التطبيق وتحديث المتصفح تلقائيًا، أي يجب أن يبدو الخرج المُصيَّر الآن كما يلي: لا يتطلب الأمر كثيرًا من الجهد لجعل توصيف HTML يبدو مثل تطبيق قائمة مهام كامل الميزات. عدِّل الملف application.hbs مرةً أخرى ليكون محتواه على النحو التالي: <section class="todoapp"> <h1>todos</h1> <input class="new-todo" aria-label="What needs to be done?" placeholder="What needs to be done?" autofocus > <section class="main"> <input id="mark-all-complete" class="toggle-all" type="checkbox"> <label for="mark-all-complete">Mark all as complete</label> <ul class="todo-list"> <li> <div class="view"> <input aria-label="Toggle the completion of this todo" class="toggle" type="checkbox" > <label>Buy Movie Tickets</label> <button type="button" class="destroy" title="Remove this todo" ></button> </div> <input autofocus class="edit" value="Todo Text"> </li> <li> <div class="view"> <input aria-label="Toggle the completion of this todo" class="toggle" type="checkbox" > <label>Go to Movie</label> <button type="button" class="destroy" title="Remove this todo" ></button> </div> <input autofocus class="edit" value="Todo Text"> </li> </ul> </section> <footer class="footer"> <span class="todo-count"> <strong>0</strong> todos left </span> <ul class="filters"> <li> <a href="#">All</a> <a href="#">Active</a> <a href="#">Completed</a> </li> </ul> <button type="button" class="clear-completed"> Clear Completed </button> </footer> </section> يجب أن يكون الخرج المُصيَّر الآن على النحو التالي: يبدو التطبيق مكتملًا، ولكن تذكّر أنه الآن مجرد نموذج أولي ساكن، لذلك يجب تقسيم توصيف HTML إلى مكونات آلية، إذ سنحوّله لاحقًا إلى تطبيق تفاعلي بالكامل. إذا نظرنا إلى الشيفرة الموجودة بجوار تطبيق المهام المُصيَّر في الشكل التالي، فهناك طرق متعددة يمكن من خلالها تحديد كيفية تقسيم واجهة المستخدِم UI، ولكن لنخطّط الآن لتقسيم توصيف HTML إلى المكونات التالية: مجموعات المكونات هي كما يلي: حقل الإدخال الرئيسي new-todo: باللون الأحمر في الشكل السابق. الجسم Body الذي يحتوي على قائمة المهام والزر الذي يضع علامة على جميع المهام المكتملة mark-all-complete: باللون الأرجواني في الشكل السابق. الزر mark-all-complete المميز بوضوح للأسباب التي سنوضّحها لاحقًا: باللون الأصفر في الشكل السابق. كل مهمة هي مكوّن لوحده: باللون الأخضر في الشكل السابق. التذييل Footer: باللون الأزرق في الشكل السابق. لاحظ أنّ مربع الاختيار mark-all-complete المميَّز باللون الأصفر يُصيَّر بجوار حقل الإدخال new-todo أثناء وجوده في القسم main لأن تنسيقات CSS الافتراضية تضع مربع الاختيار والعنصر label بقيم سالبة للإحداثيات العلوية واليسارية لتحريكهما بجوار حقل الإدخال، عوضًا عن وضعهما في القسم main. استخدام واجهة سطر الأوامر CLI لإنشاء المكونات نريد إنشاء أربعة مكونات لتمثيل تطبيقنا وهذه المكونات هي: الترويسة Header. القائمة List. عنصر المهمة Todo. التذييل Footer. استخدِم الأمر ember generate component متبوعًا باسم المكوّن لإنشائه، ولننشئ مكوّن الترويسة أولًا باتباع الخطوات التالية: أوقف تشغيل الخادم بالانتقال إلى الطرفية والضغط على الاختصار Ctrl + C. أدخِل الأمر التالي في الطرفية: ember generate component header سينشئ هذا الأمر بعض الملفات الجديدة، كما هو موضّح في ناتج الطرفية النهائي التالي: installing component create app/components/header.hbs skip app/components/header.js tip to add a class, run `ember generate component-class header` installing component-test create tests/integration/components/header-test.js يُعَدّ الملف header.hbs بأنه ملف القالب الذي سيتضمّن بنية HTML لمكوّن الترويسة فقط، وسنضيف لاحقًا الوظائف الآلية المطلوبة مثل روابط البيانات والاستجابة لتفاعل المستخدِم وما إلى ذلك. يُعَدّ الملف header-test.js ملفًا مخصَّصًا لكتابة الاختبارات الآلية للتأكد من أن تطبيقنا يستمر في العمل بمرور الوقت أثناء الترقية وإضافة الميزات وإعادة البناء وما إلى ذلك، ولن نتطرّق للاختبار في هذا المقال، ولكن يجب تطبيق الاختبار أثناء عملية التطوير وليس بعده، إذ يمكن أن تنساه لاحقًا. لننشئ شيفرة مساعِدة Scaffolding للمكوّنات الأخرى قبل إضافة أيّ شيفرة مكوّن، وبالتالي أدخِل الأوامر التالية في طرفيتك واحدًا تلو الآخر: ember generate component todo-list ember generate component todo ember generate component footer سترى الآن ما يلي في المجلد todomvc/app/components: بما أنه أصبح لدينا الآن جميع ملفات بنية المكونات، فيمكننا قص ولصق توصيف HTML لكل مكوّن من الملف application.hbs إلى كل مكوّن من هذه المكوّنات، ثم تعديل الملف application.hbs ليمثّل تجريداتنا الجديدة. أولًا، يجب تعديل الملف header.hbs ليحوي ما يلي: <input class="new-todo" aria-label="What needs to be done?" placeholder="What needs to be done?" autofocus > ثانيًا، كما يجب تعديل الملف todo-list.hbs ليحوي ما يلي: <section class="main"> <input id="mark-all-complete" class="toggle-all" type="checkbox"> <label for="mark-all-complete">Mark all as complete</label> <ul class="todo-list"> <Todo /> <Todo /> </ul> </section> ثالثًا، أضف ما يلي إلى الملف todo.hbs: <li> <div class="view"> <input aria-label="Toggle the completion of this todo" class="toggle" type="checkbox" > <label>Buy Movie Tickets</label> <button type="button" class="destroy" title="Remove this todo" ></button> </div> <input autofocus class="edit" value="Todo Text"> </li> رابعًا، يجب تعديل الملف footer.hbs بحيث يحوي ما يلي: <footer class="footer"> <span class="todo-count"> <strong>0</strong> todos left </span> <ul class="filters"> <li> <a href="#">All</a> <a href="#">Active</a> <a href="#">Completed</a> </li> </ul> <button type="button" class="clear-completed"> Clear Completed </button> </footer> خامسًا، أخيرًا، يجب تعديل محتويات الملف application.hbs بحيث تُستدَعى المكونات المناسبة كما يلي: <section class="todoapp"> <h1>todos</h1> <Header /> <TodoList /> <Footer /> </section> سادسًا، شغِّل الأمر npm start في الطرفية مرةً أخرى بعد إجراء هذه التعديلات، ثم توجَّه إلى المضيف المحلي http://localhost:4200 للتأكد من أنّ تطبيق المهام لا يزال يبدو كما كان قبل إعادة البناء. لاحظ كيف يعرض كلا عنصري المهام العبارة "Buy Movie Tickets" بسبب استدعاء المكوّن نفسه مرتين، ونَص المهمة ثابت فيه. الخلاصة يبدو كل شيء كما ينبغي، إذ نجحنا في إعادة بناء توصيف HTML إلى مكوّنات، وسنبدأ في المقال التالي بالتعرّف على إضافة التفاعل إلى تطبيق Ember. ترجمة -وبتصرّف- للمقال Ember app structure and componentization. اقرأ أيضًا المقال السابق: مقدمة إلى إطار العمل Ember
  3. سنلقي نظرةً في هذا المقال على إطار العمل Ember وآلية عمله وفوائده، وكيفية تثبيت سلسلة أدواته محليًا وإنشاء تطبيق نموذجي ثم إجراء إعداد أولي لتجهيز التطبيق للتطوير. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript، ومعرفة استخدام سطر الأوامر أو الطرفية، كما يُعَدّ فهم ميزات جافاسكربت الحديثة مثل الأصناف Classes والوحدات Modules وما إلى ذلك مفيدًا للغاية، لأن إطار العمل Ember يستخدِمها بكثرة. الهدف: معرفة كيفية تثبيت إطار عمل Ember وإنشاء تطبيق بسيط. يُعَدّ Ember إطار عمل من النوع خدمة-مكونات Component-service يركّز على عملية تطوير تطبيقات الويب وإضفاء تجربة مميزة على واجهاتها ويقلل من الاختلافات بين التطبيقات، ويُعَدّ طبقةً حديثةً وخفيفةً فوق طبقة جافاسكربت الأصيلة Native، كما يتمتع بتوافق كبير مع الإصدارات السابقة واللاحقة لمساعدة الشركات على مواكبة أحدث إصدارات Ember وأحدث الاتفاقيات التي يقودها المجتمع. تُعَدّ المكونات حزمًا من شيفرات السلوك والتنسيق Style والتوصيف Markup التي تشبه إلى حد كبير ما توفره أطر عمل الواجهة الأمامية الأخرى مثل React و Vue و Angular، كما يوفر جانب الخدمة حالةً مشتركةً طويلة الأمد وسلوكًا وواجهةً للتكامل مع المكتبات أو الأنظمة الأخرى، إذ يُعَدّ الموجِّه Router الذي سنشرحه لاحقًا خدمةً مثلًا، وتشكّل المكونات والخدمات القسم الأكبر من تطبيق EmberJS. هذا المقال جزء من سلسلة تقديمية حول إطار العمل Ember وإليك فهرس كامل السلسلة: مقدمة إلى إطار العمل Ember بنية تطبيق إطار العمل Ember وتقسيمها إلى مكونات تنفيذ التفاعل في تطبيق Ember: الأحداث والأصناف والحالة تنفيذ التفاعل في تطبيق Ember: وظيفة التذييل والعرض الشرطي التوجيه Routing في إطار العمل Ember حالات الاستخدام يناسب إطار عمل EmberJS بناء التطبيقات التي تستهدف إحدى السمتين التاليتين أو كلتيهما: تطبيقات الصفحة الواحدة، بما في ذلك تطبيقات الويب الشبيهة بالتطبيقات الأصيلة وتطبيقات الويب التقدمية Progressive Web Apps أو PWA اختصارًا. يعمل إطار عمل Ember بصورة أفضل عندما يشكِّل الواجهة الأمامية الكاملة لتطبيقك. زيادة التماسك بين العديد من تقنيات الفرق البرمجية. تتيح أفضل الممارسات التي يدعمها المجتمع سرعة تطوير أكبر على المدى الطويل. يملك إطار عمل Ember اصطلاحات أو اتفاقيات واضحة ومفيدة لفرض التناسق ومساعدة أعضاء الفريق على العمل بسرعة. إضافات إطار عمل Ember يمتلك إطار العمل EmberJS معمارية الإضافات Plugin، مما يعني أنه يمكن تثبيت الإضافات Add-ons وتوفير وظائف إضافية دون كثير من الإعداد إذا وُجِد، ومن هذه الإضافات: PREmber: تصيير Rendering موقع ويب ساكن للمدونات أو المحتوى التسويقي. FastBoot: تصيير من طرف الخادم، بما في ذلك تحسين محركات البحث SEO، أو تحسين أداء التصيير الأولي لصفحات الويب المعقدة عالية التفاعل. empress-blog: تأليف منشورات المدونات باستخدام لغة ماركداون Markdown مع تحسين محركات البحث SEO باستخدام الإضافة PREmber. ember-service-worker: إعداد تطبيق ويب تقدمي PWA بحيث يمكن تثبيت التطبيق على الأجهزة المحمولة مثل تثبيته من متجر تطبيقات الجهاز نفسه. تطبيقات الجوال الأصيلة يمكن استخدام إطار عمل Ember مع تطبيقات الأجهزة المحمولة الأصيلة مع جسر بين تطبيقات الهاتف المحمول الأصيلة ولغة جافاسكربت مثل الجسر الذي توفره واجهة Corber. الآراء والأعراف يُعَدّ إطار العمل EmberJS أحد أكثر أطر عمل الواجهة الأمامية تشبثًا برأيه، وتُعَدّ الآراء في Ember مجموعةً من الاتفاقيات أو الأعراف التي تساعد على زيادة كفاءة المطورين، ولكن يجب تعلّم تلك الاتفاقيات، إذ تساعد الآراء التي تدعم الاتفاقيات في تقليل الاختلافات بين التطبيقات مثل اللغات المُستخدَمة والنظام المجتمعي بفضل تعريف هذه الاتفاقيات ومشاركتها، وهو هدف مشترك بين جميع الأطر المتشبثة برأيها، ويصبح المطورون بعدها أكثر قدرةً على التبديل بين المشاريع والتطبيقات دون الحاجة إلى إعادة تعلم المعمارية والأنماط والمصطلحات وما إلى ذلك، وستلاحظ خلال هذا المقال آراء إطار العمل Ember مثل اصطلاحات تسمية ملفات المكونات. ارتباط إطار عمل Ember مع لغة جافاسكربت الصرفة Vanilla JavaScript بُني إطار عمل Ember على تقنيات جافاسكربت، ويُعَدّ طبقةً رقيقةً فوق البرمجة التقليدية كائنية التوجه، مع السماح للمطورين باستخدام تقنيات البرمجة الوظيفية. يستخدِم Ember صيغتين رئيسيتين هما: جافاسكربت أو لغة TypeScript اختياريًا. لغة قوالب Ember الخاصة التي تعتمد على لغة Handlebars. تُستخدَم لغة القوالب Templating Language لتحسين عملية البناء ووقت التشغيل، وهي مجموعة شاملة من لغة HTML، وبالتالي يمكن لأيّ شخص يعرف لغة HTML تقديم مساهمات مهمة لأيّ مشروع من مشاريع Ember، كما يمكن للمصممين وغيرهم من غير المطورين المساهمة في قوالب الصفحات دون أي معرفة بلغة جافاسكربت، ثم يمكن إضافة التفاعل لاحقًا، كما تتيح لغة القوالب هذه حمولات أصول أخف نظرًا لتصريف القوالب في شيفرة ثنائية Byte Code يمكن تحليلها بسرعة أكبر من تحليل شيفرة جافاسكربت. كل شيء آخر في Ember هو جافاسكربت وخاصةً أصناف جافاسكربت التي تُعَدّ المكان الذي تعمل فيه معظم أجزاء إطار العمل حيث توجد الأصناف الأبناء، ويكون لكل نوع من الأشياء غرض مختلف وموقع متوقع مختلف ضمن مشروعك. يوضِّح الشكل التالي تأثير Ember على جافاسكربت في المشاريع النموذجية، إذ يوضِّح كيف أن أقل من 20% من شيفرة JS المكتوبة خاصةً بإطار العمل Ember. بدء استخدام Ember سننشئ أولًا نسخةً من نموذج تطبيق TodoMVC التقليدي لتعلُّم كيفية استخدام أساسيات إطار عمل Ember، إذ يُعَدّ تطبيق TodoMVC تطبيقًا أساسيًا لتتبّع المهام، ويُستخدَم في العديد من التقنيات المختلفة، وإليك نسخةً مكتملةً منه ليكون مرجع لك. إنشاء تطبيق جديد في Ember يحتوي مشروع TodoMVC على بعض المشاكل من حيث الالتزام بممارسات الويب التي يمكن الوصول إليها افتراضيًا، وهناك نوعان من مشاكل جيت هاب GitHub المفتوحة حول هذا الموضوع في مجموعة مشاريع TodoMVC هي: إضافة وصول مستخدِمي لوحة المفاتيح إلى العروض التوضيحية. إعادة تفعيل المخطط حول العناصر القابلة للتركيز. يهتم إطار Ember بموضوع إمكانية الوصول accessibility كثيرًا وأن تكون التطبيقات المبنية فيه سهلة الوصول لكامل المستخدمين حتى بمن فيهم أي إعاقة وقد وفر دليلًا شاملًا عنه، ولكن بما أنّ هذا المقال يركِّز على جانب جافاسكربت في إنشاء تطبيق ويب صغير، فإنّ قيمة TodoMVC تأتي من توفير ملفات CSS المبنيّة مسبقًا وبنية HTML الموصَى بها، مما يلغي الاختلافات بين التطبيقات، كما يسمح بإجراء موازنة أسهل بينها، وسنركِّز لاحقًا على إضافة شيفرة إلى تطبيقنا لإصلاح بعض أكبر أخطاء تطبيق TodoMVC. تثبيت أدوات Ember يستخدِم Ember واجهة سطر الأوامر CLI لبناء أجزاء من تطبيقك وإنشاء شيفرتها المساعدة Scaffolding. أولًا، ستحتاج إلى تثبيت أداة node ومدير الحزم npm قبل تمكّنك من تثبيت أداة ember-cli، كما يمكنك الانتقال إلى مقال دليل استخدام سطر الأوامر في عملية تطوير الويب من طرف العميل لمعرفة كيفية تثبيت node و npm إذا لم تكن مثبّتةً لديك مسبقًا. ثانيًا، اكتب الأمر التالي في طرفيتك لتثبيت أداة ember-cli: npm install -g ember-cli توفِّر هذه الأداة برنامج ember في طرفيتك، وتُستخدَم لإنشاء وبناء وتطوير واختبار تطبيقك وإنشاء شيفرته المساعِدة، كما يمكنك تشغيل الأمر ember --help للحصول على قائمة كاملة بالأوامر وخياراتها. ثالثًا، يمكنك إنشاء تطبيق جديد تمامًا من خلال كتابة الأمر التالي في طرفيتك، مما يؤدي إلى إنشاء مجلد جديد له الاسم todomvc ضمن المجلد الذي أنت فيه حاليًا، بحيث يحتوي على أدوات لبناء تطبيق Ember جديد، وتأكد من الانتقال إلى مكان مناسب في الطرفية مثل مجلد "سطح المكتب" أو مجلد "المستندات" لتعثر على مجلد تطبيقك بسهولة قبل تشغيل الأمر التالي: ember new todomvc أو شغّل الأمر التالي على نظام ويندوز: npx ember-cli new todomvc يؤدي هذا الأمر إلى إنشاء بيئة تطوير تطبيقات جاهزة للإنتاج تتضمن افتراضيًا الميزات التالية: خادم التطوير مع إعادة التحميل المباشر. معمارية الإضافات التي تسمح لحزم الطرف الثالث بتحسين تطبيقك. أحدث إصدار من جافاسكربت متكامل مع Babel و Webpack. بيئة اختبار آلية تدير اختباراتك في المتصفح، مما يتيح لك الاختبار مثل المستخدِم العادي. عملية التحويل Transpilation والتصغير Minification لكل من شيفرات CSS وجافاسكربت الخاصة بعمليات البناء للإنتاج. اتباع العرف السائد في كتابة الشيفرة مما يقلل الاختلافات بين التطبيقات ويسمح بتبديل السياق بسهولة. الاستعداد لبناء مشروع Ember ستحتاج إلى محرِّر شيفرات قبل الاستمرار بمشروعك الجديد، فإذا لم يكن لديك محرّر مُعَدّ مسبقًا، فإنّ Ember Atlas لديه بعض الإرشادات حول كيفية إعداد المحرّرات المختلفة. تثبيت الأصول المشتركة لمشاريع TodoMVC لا يُعَدّ تثبيت الأصول أو ملفات المشروع المشتركة خطوةً مطلوبةً للمشاريع الجديدة، لكنه يسمح باستخدام ملفات CSS المشتركة الحالية حتى لا نخمّن ما هو ملف CSS المطلوب لإنشاء أنماط أو تنسيقات مشروع TodoMVC. أولًا، انتقل أولًا إلى المجلد todomvc في الطرفية باستخدام الأمر cd todomvc في نظامَي macOS أو لينكس Linux مثلًا. ثانيًا، شغّل الأمر التالي لوضع ملف CSS المشترك الخاص بمشروع todomvc ضمن تطبيقك: npm install --save-dev todomvc-app-css todomvc-common ثالثًا، ابحث بعد ذلك عن الملف ember-cli-build.js في المجلد todomvc الموجود في المجلد الجذر، وافتحه في محرر الشيفرة الذي اخترته، إذ يُعَدّ الملف ember-cli-build.js مسؤولًا عن إعداد التفاصيل حول كيفية بناء مشروعك بما في ذلك تجميع كل ملفاتك مع بعضها البعض وتصغير الأصول وإنشاء خرائط الشيفرة البرمجية، لذلك ليس هناك داع للقلق بشأن هذا الملف، كما سنضيف سطورًا إلى الملف ember-cli-build.js لاستيراد ملفات CSS المشتركة، بحيث تصبح جزءًا من عملية البناء دون الحاجة إلى استيرادها ‎@import صراحةً في الملف app.css، إذ سيتطلب ذلك إعادة كتابة عنوان URL في وقت البناء وبالتالي سيكون أقل كفاءة وأكثر تعقيدًا في الإعداد. رابعًا، ابحث عن الشيفرة التالية في الملف ember-cli-build.js: let app = new EmberApp(defaults, { // أضف خيارات هنا }); خامسًا، أضف الأسطر التالية بعد ذلك قبل حفظ الملف: app.import('node_modules/todomvc-common/base.css'); app.import('node_modules/todomvc-app-css/index.css'); سادسًا، أخيرًا، ابحث عن الملف app.css الموجود في المسار app/styles/app.css، والصق ما يلي فيه: :focus, .view label:focus, .todo-list li .toggle:focus + label, .toggle-all:focus + label { outline: #d86f95 solid !important; } يعدِّل ملف CSS بعض الأنماط التي توفرها حزمة npm والتي هي todomvc-app-css، مما يسمح بظهور تركيز لوحة المفاتيح ويؤدي إلى حد ما إلى إصلاح أحد عيوب الشمولية الرئيسية لتطبيق TodoMVC الافتراضي. بدء تشغيل خادم التطوير يمكنك بدء تشغيل التطبيق في وضع التطوير عن طريق كتابة الأمر التالي في الطرفية أثناء وجودك ضمن المجلد todomvc: ember server ويجب أن يظهر لديك خرج مشابه لما يلي: Build successful (190ms) – Serving on http://localhost:4200/ Slowest Nodes (totalTime >= 5%) | Total (avg) -----------------------------------------+----------- BroccoliMergeTrees (17) | 35ms (2 ms) Package /assets/vendor.js (1) | 13ms Concat: Vendor Styles/assets/vend... (1) | 12ms يُشغَّل خادم التطوير على المضيف المحلي http://localhost:4200، والذي يمكنك زيارته في متصفحك للتحقق من عملك حتى الآن. إذا كان كل شيء على ما يرام، فسترى صفحةً تشبه الصفحة التالية: الخلاصة وصلنا إلى النقطة التي يمكننا فيها البدء في بناء نموذجنا لتطبيق TodoMVC في إطار عمل Ember، وسنتعرّف في المقال التالي على بناء بنية شيفرة توصيف تطبيقنا بوصفها مجموعة من المكونات المنطقية. ترجمة -وبتصرّف- للمقال Getting started with Ember. اقرأ أيضًا مقارنة بين أطر الواجهات الأمامية: Angular و React و Vue
  4. يمكن أن تكون أدوات تطوير الويب من طرف العميل غير مفهومة بالنسبة لكثير من المطورين، لذلك سنوضّح من خلال سلسلة من المقالات الغرض من بعض أدوات تطوير الويب الأكثر شيوعًا من طرف العميل، وسنشرح أيضًا الأدوات التي يمكنك ربطها مع بعضها بعضًا، وكيفية تثبيتها باستخدام مدير الحزم، والتحكم فيها باستخدام سطر الأوامر، وسنقّدّم مثالًا كاملًا عن سلسلة أدوات يوضح كيفية زيادة الإنتاجية، ولكن في البداية وقبل محاولة استخدام هذه الأدوات يجب أن تتعلم أساسيات لغات HTML وCSS وجافاسكربت JavaScript. وفّرنا من خلال موسوعة حسوب مرجعًا لكل من لغات HTML وCSS وJavaScript يمكنك البدء منه. سنوضح في هذه السلسلة من المقالات المواضيع التالية لفهم أدوات تطوير الويب من طرف العميل: نظرة عامة على أدوات تطوير الويب من طرف العميل: نقدّم في هذا المقال نظرة عامة على أدوات الويب الحديثة، وأنواع الأدوات المتاحة وأين ستصادفها في دورة حياة تطوير تطبيقات الويب، وكيفية العثور على المساعدة باستخدام هذه الأدوات. دورة مكثفة لفهم سطر الأوامر: سيُطلب منك بلا شك تشغيل بعض الأوامر في الطرفية Terminal أو في سطر الأوامر خلال عملية التطوير، حيث يقدم هذا المقال مقدمة إلى الطرفية والأوامر الأساسية التي ستحتاج إلى إدخالها، وكيفية ربط الأوامر مع بعضها بعضًا، وكيفية إضافة أدوات واجهة سطر الأوامر Command Line Interface -أو CLI اختصارًا. أساسيات إدارة الحزم: سنلقي نظرة على مدراء الحزم بشيء من التفصيل لفهم كيفية استخدامها في مشاريعك لتثبيت اعتماديات Dependencies أدوات المشروع وتحديثها وغير ذلك. سلسلة أدوات كاملة: سنعمل في المقالين الأخيرين من السلسلة على ترسيخ معرفتك بالأدوات من خلال إرشادك خلال عملية بناء نموذج لسلسلة أدوات عن طريق إعداد بيئة تطوير ووضع أدوات التحويل في مكانها لنشر تطبيقك فعليًا على Netlify. كما سنقدم دراسة حالة مع إعداد بيئة التطوير الخاصة بنا وإعداد أدوات تحويل شيفرتنا. نشر التطبيق: سنأخذ في المقال الأخير من هذه السلسلة مثالًا عن سلسلة الأدوات التي أنشأناها في المقال السابق ونضيفها لنتمكن من نشر التطبيق، إذ سنرفع الشيفرة البرمجية للمشروع على موقع مشاركة الشيفرات جيت هب GitHub، وننشرها باستخدام نيتليفاي Netlify، وسنوضّح كيفية إضافة اختبار بسيط لهذه العملية. لنبدأ بمقالنا الأول من هذه السلسلة من خلال إلقاء نظرة سريعة على أدوات تطوير الويب من طرف العميل. المتطلبات الأساسية: الإلمام بمفاهيم لغات HTML و CSS وجافاسكربت الأساسية. الهدف: فهم أنواع الأدوات من طرف العميل وكيفية العثور عليها والحصول على المساعدة بشأنها. استخدام الأدوات الحديثة أصبحت كتابة برمجيات الويب أكثر تعقيدًا بمرور الوقت، وبالرغم من ذلك لا يزال كتابة برمجيات الويب يدويًا بلغة HTML و CSS وجافاسكربت أمرًا ممكنًا إلا أنه يوجد الآن مجموعة كبيرة من الأدوات التي يمكن للمطورين استخدامها لتسريع عملية إنشاء موقع ويب أو تطبيق. توجد بعض الأدوات المستقرة التي أصبحت أسماء مألوفة وشائعة في مجتمع تطوير الويب، بالإضافة إلى الأدوات الجديدة التي نراها تصدر كل يوم لحل مشكلات معينة، فيمكن أن تكتب برنامجًا للمساعدة في عملية التطوير الخاصة بك ولحل مشكلة معينة لا يمكن أن تحلها الأدوات الحالية. يمكنك استخدام عدد هائل من الأدوات وتضمينها في مشروع واحد، كما يمكنك استخدام أداة واحدة مثل Webpack وإعداد ملف تكوينها من مئات الأسطر، وغالبًا ما ينظر المبرمجين المبتدئين لهذه الشيفرة وكأنها تعويذات تؤدي المهمة بطريقة سحرية ولن يفهمها إلا المهندسون الخبراء. إلا أنه في الواقع غالبية الخبراء يواجهون مشاكلًا في استخدام الأدوات من وقت لآخر، إذ يمكن إضاعة ساعات في محاولة تشغيل مسار أدوات قبل لمس سطر واحد من شيفرة التطبيق. لذلك لا داعي للقلق، فأنت لست وحدك. سنزودك من خلال هذا المقال بنقطة بداية مفيدة لفهم أساسيات استخدام أدوات الويب، إذ يُفضَّل أن تبدأ على نطاق صغير، ثم تشق طريقك تدريجيًا إلى الاستخدامات أكثر تقدمًا. نظام أدوات المطورين الحديث يعدّ النظام البيئي Ecosystem الحديث لأدوات المطورين في يومنا الحالي ضخم نسبيًا، لذا يجب من الأفضل تكوين فكرة عامة عن المشاكل الرئيسية التي تحلها هذه الأدوات. إذا انتقلت إلى محرك البحث المفضل لديك وبحثت عن "أدوات مطور الواجهة الأمامية"، فستصل لمجموعة كبيرة من النتائج تتراوح من محرّرات النصوص والمتصفحات وحتى نوع الأقلام التي يمكنك استخدامها لتدوين الملاحظات. وبالرغم من أن اختيارك لمحرّر الشيفرة هو بالتأكيد جزء من خيارات الأدوات، ولكن في هذه السلسلة من المقالات سنتجاوز ذلك من خلال التركيز على أدوات المطور التي تساعدك على إنتاج شيفرة ويب ذات كفاءة عالية. يمكنك تصنيف الأدوات من طرف العميل ضمن الفئات الثلاث التالية: شبكة الأمان Safety Net: أدوات مفيدة أثناء تطوير شيفرتك. التحويل Transformation: الأدوات التي تحول الشيفرة بطريقة ما مثل تحويل لغة وسيطة إلى لغة جافاسكربت التي يمكن أن يفهمها المتصفح. أدوات ما بعد التطوير Post-development: الأدوات المفيدة بعد كتابة شيفرتك مثل أدوات الاختبار والنشر. لنلقِ نظرة على كل واحدة من هذه الفئات بمزيد من التفصيل. شبكة الأمان هي الأدوات التي تحسّن شيفرتك البرمجية التي تكتبها، ويجب أن تكون هذه الأدوات خاصةً ببيئة التطوير الخاصة بك، بالرغم من ذلك من غير المألوف أن يكون لدى الشركات سياسة أو إعدادات جاهزة للتثبيت لكي تضمن أن يستخدم جميع مطوري هذه الشركات العمليات نفسها. تتضمن هذه الأدوات أيّ شيء يسهّل عملية التطوير فيما يتعلق بإنشاء شيفرة مستقرة وموثوقة. كما يجب أن تساعدك أدوات شبكة الأمان في منع حدوث الأخطاء أو تصحيحها تلقائيًا دون الحاجة إلى إنشاء شيفرة من نقطة الصفر في كل مرة. سنوضّح فيما يلي بعض أنواع أدوات شبكة الأمان الشائعة التي يستخدمها المطورون. منقحات الصياغة Linters منقحات الصياغة هي أدوات تتحقق من شيفرتك وتخبرك بوجود أخطاء وبأنواعها وسطور الشيفرة التي توجد فيها. يمكن ضبط منقحات الصياغة للإبلاغ عن الأخطاء، وللإبلاغ عن أيّ انتهاكات لدليل النمط المحدَّد الذي يستخدمه فريقك مثل الشيفرة التي تستخدم عددًا خاطئًا من المسافات البادئة، أو استخدام صياغة قالب Template Literals بدلًا من صياغة سلسلة نصية عادية String Literals. يعدّ Eslint منقّح أخطاء معياري خاص بلغة جافاسكربت ويمكن ضبط هذه الأداة لاكتشاف أخطاء الصياغة المحتملة وتشجيعك لاستخدام أفضل الممارسات ضمن شيفرتك البرمجية. شاركت بعض الشركات والمشاريع إعدادات Eslint الخاصة بها، ويمكنك العثور على أدوات اكتشاف أخطاء للغات أخرى مثل Csslint. كما يمكنك استخدام Webhint وهو منقح أخطاء مفتوح المصدر يمكن ضبطه للويب، ويعرض أفضل الممارسات لاستخدامها بما في ذلك أساليب الوصول والأداء والتوافق مع المتصفحات باستخدام بيانات توافق متصفح MDN والأمان واختبار تطبيقات الويب ذات الصفحة الواحدة PWA وغير ذلك. كما يتوفر كأداة سطر أوامر Node.js وكامتداد VS Code. التحكم بالشيفرة البرمجية يُعرَف أيضًا باسم أنظمة التحكم بالإصدارات Version Control Systems أو VCS اختصارًا، ويُعَد التحكم بالشيفرة البرمجية ضروريًا لدعم العمل الفردي أو ضمن فريق. يتضمن نظام VCS إصدارًا محليًا من الشيفرة التي تجري تغييرات عليها، ثم ترفع التغييرات إلى إصدار رئيسي من الشيفرة داخل مستودع بعيد مُخزَّن على خادم في مكان ما. هناك عادةً طريقة للتحكم في التعديلات التي تُطبَّق على النسخة الرئيسية من الشيفرة وتنسيقها ومتى تطبَّق، وبالتالي لا يكتب فريق المطورين فوق عمل بعضهم بعضًا. يعد نظام جيت هاب Git من أشهر نظم التحكم بالشيفرة البرمجية الذي يستخدمه معظم المطورين حاليًا، إذ يمكن الوصول إليه عبر سطر الأوامر أو عبر واجهات سهلة الاستخدام. كما يمكنك رفع نسخة من المشروع إلى الخادم الخاص بك باستخدام شيفرتك في مستودع جيت، أو يمكنك استخدام موقع ويب مستضاف للتحكم بالشيفرة المصدرية مثل جيت هب GitHub أو جيت لاب GitLab أو بيت باكيت BitBucket، ولكن سنستخدم GitHub في مثالنا. مُنسِّقات الشيفرة Code Formatters ترتبط مُنسِّقات الشيفرة إلى حد ما بمنقّحات الصياغة، باستثناء أنه بدلًا من الإشارة إلى الأخطاء في شيفرتك، فإنها عادةً ما تميل إلى التأكد من تنسيق شيفرتك الصحيح وفقًا لقواعد النمط الخاصة بك، وتعمل تلقائيًا على إصلاح الأخطاء التي تعثر عليها. أحد الأمثلة الشائعة لمنسقات الشيفرة البرمجية هو Prettier، والذي سنستخدمه لاحقًا في مثالنا. الحزم Bundlers أو Packagers هي الأدوات التي تجعل شيفرتك جاهزةً لعملية الإنتاج عن طريق تقنية هز الشجرة Tree-Shaking مثلًا للتأكّد من إدراج أجزاء مكتبات الشيفرة التي تستخدمها فعليًا فقط في شيفرة الإنتاج النهائي، أو التصغير Minifying لإزالة كل مسافة فارغة في شيفرة الإنتاج، مما يصغّرها قدر الإمكان قبل رفعها إلى الخادم. تعد أداة Parcel من الأدوات الذكية التي يمكنها تطبيق المهام المذكورة السابقة، كما أنها تساعد أيضًا في حزم الملفات أو الملحقات assets مثل HTML وCSS وملفات الصور ضمن حزم ملائمة يمكنك نشرها لاحقًا، وتضيف اعتماديات تلقائيًا كلما حاولت استخدامها. كما يمكن لهذه الأداة التعامل مع بعض مهام تحويل الشيفرة نيابة عنك. يذكر أن أداة ويب باك Webpack هي أشهر أداة حزم تطبّق مهامًا مماثلة. التحويل Transformation تتيح هذه المرحلة من دورة حياة تطبيق الويب كتابة شيفرة برمجية إما في شيفرة مستقبلية مثل تحويل الشيفرة البرمجية لأحدث ميزات لغة CSS أو جافاسكربت التي يمكن ألّا تدعمها بعض المتصفحات حتى الآن، أو إنشاء شيفرة برمجية مكافئة لشيفرة برمجية مكتوبة بلغة معينة مثل لغة TypeScript، لتتوافق الشيفرة التي أنشأتها الأداة المطلوبة مع المتصفح لاستخدامها في عملية الإنتاج. يُنظَر إلى تطوير الويب على أنه مؤلَّف من ثلاث لغات هي HTML وCSS وجافاسكربت، وهناك أدوات تحويل لجميع هذه اللغات. يقدّم التحويل فائدتين رئيسيتين هما: القدرة على كتابة شيفرة برمجية باستخدام أحدث ميزات اللغة وتحويلها إلى شيفرة تعمل على جميع الأجهزة، فيمكن أن ترغب مثلًا في كتابة شيفرة بلغة جافاسكربت باستخدام ميزات لغة جديدة متطورة، ولكن لا يزال لديك شيفرة الإنتاج النهائي التي تعمل على المتصفحات القديمة التي لا تدعم هذه الميزات. تشمل الأمثلة التالية: Babel: مصرّف جافاسكربت JavaScript الذي يسمح للمطورين بكتابة شيفرة باستخدام أحدث إصدارات جافاسكربت، والتي يأخذها Babel ويحوّلها إلى إصدار جافاسكربت قديم يمكن لمزيد من المتصفحات فهمه. كما يمكن للمطورين كتابة ونشر إضافات Babel. PostCSS: تطبّق هذه الأداة الشيء نفسه الذي يطبّقه Babel، ولكن مع ميزات CSS المتطورة. إذا لم تكن هناك طريقة مكافئة لتطبيق شيء ما باستخدام ميزات CSS القديمة، فسيثبّت PostCSS تعويض نقص دعم المتصفحات Polyfill بلغة جافاسكربت لمحاكاة تأثير CSS الذي تريده. خيار كتابة الشيفرة بلغة مختلفة تمامًا وتحويلها إلى لغة متوافقة مع الويب مثل: Sass / SCSS: يتيح لك هذا الامتداد من لغة CSS استخدام المتغيرات والقواعد المتداخلة والمزج والدوال والعديد من الميزات الأخرى، إذ يُعَد بعضها متاحًا في لغة CSS الأصلية مثل المتغيرات، وبعضها ليس كذلك. TypeScript: هي مجموعة شاملة من لغة جافاسكربت التي تقدم مجموعة من الميزات الإضافية. يحوّل مصرّف TypeScript شيفرة TypeScript إلى جافاسكربت عند البناء بهدف الإنتاج. توفّر أطر العمل مثل React وEmber وVue الكثير من الوظائف مجانًا وتسمح لك باستخدامها عبر صيغة مخصَّصة مبنية على لغة جافاسكربت الصرفة Vanilla JavaScript. تعمل شيفرة جافاسكربت الخاصة بإطار العمل في الخلفية لتفسير هذه البنية المخصَّصة وتقديمها بوصفها تطبيق ويب نهائي. أدوات ما بعد التطوير Post Development تضمن أدوات ما بعد التطوير أن يصل برنامجك إلى الويب ويستمر في عمله، إذ تتضمن مرحلة ما بعد التطوير عمليات النشر وأطر عمل الاختبار وأدوات التدقيق وغير ذلك. تُعَد هذه المرحلة بأنها المرحلة التي تحتاج أقل قدر من التفاعل النشط بحيث تُشغَّل تلقائيًا بمجرد تهيئتها، وتخبرك بحدوث خطأ ما. أدوات الاختبار Testing Tools تأخذ هذه الأدوات شكل أداة تختبر تلقائيًا شيفرت البرمجية للتأكد من صحتها قبل المضي قدمًا مثل رفع تعديلات إلى مستودع جيب هب GitHub Repo. يمكن أن يشمل ذلك الكشف عن الأخطاء Linting، ويشمل إجراءات أكثر تعقيدًا مثل اختبارات الوحدة إذ تشغّل جزءًا من شيفرتك، مع التأكد من أنها تتصرف كما ينبغي. تشمل أطر عمل اختبارات الكتابة Jest وMocha وJasmine. تتضمن أنظمة التشغيل والاختبارات الآلية Travis CI وJenkins وCircle CI وغيرها. أدوات النشر Deployment Tools تسمح أنظمة النشر بنشر موقع الويب الخاص بك، وهي متاحة لكل من المواقع الثابتة والديناميكية، وتميل للعمل جنبًا إلى جنب مع أنظمة الاختبار، إذ ستنتظرك سلسلة الأدوات إلى أن ترفع التغييرات إلى المستودع البعيد، وتجري بعض الاختبارات لمعرفة ما إذا كانت التغييرات مناسبة، وإذا نجحت الاختبارات، فستنشر تطبيقك تلقائيًا على موقع إنتاج. تعد Netlify واحدة من أكثر أدوات النشر شيوعًا في الوقت الحالي، ولكن هناك أدوات أخرى مثل Vercel وGithub Pages. أدوات ما بعد التطوير الأخرى هناك عدد من أنواع الأدوات الأخرى المتاحة للاستخدام في مرحلة ما بعد التطوير، بما في ذلك Code Climate لجمع مقاييس جودة الشيفرة، وامتداد متصفح Webhint لإجراء تحليل وقت التشغيل للتوافق مع المتصفحات وعمليات التحقق الأخرى، وGithub bots لتوفير المزيد من ميزات GitHub القوية، وUpdown لتوفير مراقبة وقت تشغيل التطبيق وغير ذلك الكثير. أنواع الأدوات تُطبَّق أنواع الأدوات المختلفة في دورة حياة التطوير وفق ترتيب معين، ولكن كن مطمئنًا أنك لست مضطرًا إلى أن يكون لديك كل هذه الأدوات لإصدار موقع ويب، فلن لا تحتاج لأيٍّ منها. لكن سيؤدي تضمين بعض هذه الأدوات في عملياتك إلى تحسين تجربة التطوير، ويُحتمَل أن يؤدي إلى تحسين جودة شيفرتك الإجمالية. يستغرق استقرار أدوات المطور الجديدة بعض الوقت حسب تعقيدها. تشتهر إحدى أشهر الأدوات وهي Webpack بكونها معقدة للغاية للتعامل معها، ولكن كان هناك ضغط كبير لتبسيط الاستخدام في أحدث إصدار رئيسي، لذا قُلٍّل الإعداد المطلوب إلى الحد الأدنى. ليس هناك حل سحري يضمن النجاح باستخدام الأدوات، ولكن ستجد تدفقات عمل تناسبك أو تناسب فريقك ومشاريعك مع زيادة خبرتك، ويجب أن تكون سلسلة الأدوات شيئًا يمكنك نسيانه وأن تركّز على العمل فقط، بمجرد تسوية جميع مكامن الخلل في العملية. كيفية اختيار أداة معينة والحصول عليها تميل معظم الأدوات إلى كتابتها وإصدارها بصورة منفصلة، لذلك لا تتوفر أبدًا في المكان أو التنسيق نفسه، على الرغم من وجود مساعدة شبه مؤكدة، فيمكن أن يكون العثور على مساعدة في استخدام أداة أو حتى اختيار الأداة التي تريد استخدامها أمرًا صعبًا. المعرفة حول أفضل الأدوات لاستخدامها هي معرفة مجتمعية إلى حد ما، مما يعني أنه إن لم تكن بالفعل في مجتمع الويب، فستكون معرفة الأدوات التي تريدها بالضبط أمرًا صعبًا، وهذا هو أحد الأسباب التي دفعتنا إلى كتابة هذه السلسلة من المقالات، ونأمل أن نقدم تلك الخطوة الأولى التي يصعب إيجادها بطريقة أخرى. ستحتاج على الأرجح إلى مجموعة الأشياء التالية: نجح المدرسون أو الموجهون أو الزملاء الطلاب ذوو الخبرة أو الزملاء الذين لديهم بعض الخبرة في حل هذه المشكلات من قبل، ويمكنهم تقديم المشورة. مكان محدد مفيد للبحث، إذ تكون عمليات البحث العامة على الويب عن أدوات مطور الواجهة الأمامية عديمة الفائدة إلا إن عرفتَ اسم الأداة التي تبحث عنها. إذا استخدمتَ مدير الحزم NPM لإدارة اعتمادياتك على سبيل المثال، فيُفضَّل الانتقال إلى صفحة npm الرئيسية والبحث عن نوع الأداة التي تبحث عنها. حاول مثلًا البحث عن "التاريخ date" إن أردتَ أداة تنسيق التاريخ، أو "المُنسِّق formatter" إذا كنت تبحث عن مُنسق شيفرة عام. انتبه إلى درجات الشعبية والجودة والصيانة، وآخر تحديث للحزمة. انقر على صفحات الأداة لمعرفة عدد تنزيلات الحزمة الشهرية، واحتوائها على توثيق جيد يمكنك استخدامه للتأكد من أنها تطبّق ما تريده، وبالتالي تُعَد مكتبة date-fns أداة تنسيق تاريخ جيدة لاستخدامها. إذا أردت البحث عن إضافة Plugin لدمج وظائف الأدوات في محرّر الشيفرة، فألقِ نظرة على صفحة الإضافات / الامتدادات لمحرر الشيفرة، وراجع حزم Atom وامتدادات VSCode على سبيل المثال. ألقِ نظرة على الامتدادات المميزة في الصفحة الأولى، وحاول مرة أخرى البحث عن نوع الامتداد الذي تريده (أو اسم الأداة مثل البحث عن "eslint" في صفحة امتدادات VSCode). إذا حصلتَ على نتائج، فألقِ نظرة على معلومات عدد النجوم أو التنزيلات التي يحتويها الامتداد، إذ يُعَد ذلك مؤشرًا على جودته. المنتديات المتعلقة بالتنمية لطرح أسئلة حول الأدوات التي يجب استخدامها مثل قسم الأسئلة والأجوبة البرمجية في أكاديمية حسوب. إذا اخترت أداة لاستخدامها، فيجب أن يكون طريقة فهم الأداة هو الصفحة الرئيسية لمشروع الأداة والذي يكون غالبًا موقع ويب كامل أو يكون مستندًا تمهيديًا واحدًا في مستودع الشيفرة. يمكن أن ترغب في العثور على بعض البرامج التعليمية المخصصة لبدء استخدام أنواع معينة من الأدوات، لذا تعد المقالات البرمجية ومقالات DevOps في أكاديمية حسوب أماكن الانطلاق الرائعة للبحث. يُحتمَل أن تمر عبر العديد من الأدوات المختلفة أثناء البحث عن الأدوات المناسبة لك، وأن تجربها لمعرفة ما إذا كانت منطقية ومدعومة جيدًا وتطبّق ما تريده منها. يُعَد كل ذلك مناسبًا للتعلم، وسيصبح الطريق أسلس كلما اكتسبت مزيدًا من الخبرة. الخلاصة قدّمنا من خلال هذا المقال مقدمة بسيطة عن أدوات الويب من طرف العميل، وسنقدّم في المقال القادم دورة مكثفة عن سطر الأوامر، إذ تُستدعَى كثير من الأدوات منه، وسنلقي نظرة على ما يمكن أن يفعله سطر الأوامر ثم نحاول تثبيت الأداة الأولى ونستخدمها. هذا المقال جزء من سلسلة مقالات بعنوان تعلم تطوير الويب والتي تشرح كامل عملية تطوير الويب من واجهات أمامية وخلفية بالكامل. ترجمة -وبتصرُّف- للمقالين Understanding client-side web development tools وClient-side tooling overview. اقرأ أيضًا مدخل إلى أدوات التطوير في متصفح الويب DevTools كيف تستخدم أدوات المطوِّر في المتصفحات الحديثة الفرق بين مصمم الويب ومطور الويب وكيفية معرفة الأنسب بينهما الأدوات المستخدمة في بناء مواقع ويب
  5. سنأخذ مثال سلسلة الأدوات الذي أنشأناه في المقال السابق، ثم نضيفه لنتمكن من نشر تطبيقنا، إذ سنرفع الشيفرة على GitHub، وننشر التطبيق باستخدام Netlify، وسنوضح كيفية تطبيق اختبار بسيط عليه. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت. الهدف: إنهاء العمل من خلال دراسة الحالة الكاملة لسلسلة الأدوات مع التركيز على مرحلة نشر التطبيق. مرحلة ما بعد التطوير يُحتَمل أن تواجه العديد من المشاكل التي يجب حلها في هذه المرحلة من دورة حياة المشروع، لذلك يجب إنشاء سلسلة أدوات تعالج هذه المشاكل بطريقة تتطلب أقل قدر ممكن من التدخل اليدوي. إليك بعض الأشياء التي يجب مراعاتها في المشروع: إنشاء بنية إنتاج: ضمان تصغير الملفات وتقسيمها وتطبيق تقنية هز الشجرة وتعطيل ذاكرة الإصدارات المخبئية Cache Busted للمتصفح. تشغيل الاختبارات: يمكن أن تتراوح من "هل نُسِّقت هذه الشيفرة تنسيقًا صحيحًا؟" إلى "هل يطبّق هذا الشيء ما هو متوقع منه؟"، والتأكد من أن الاختبارات الفاشلة تمنع النشر. نشر الشيفرة المحدثة فعليًا إلى عنوان URL مباشر: أو عنوان URL مرحلي لمراجعتها أولًا. كما تنقسم المهام السابقة إلى مهام أخرى، لأن معظم فرق تطوير الويب لها شروطها وعملياتها الخاصة لجزء من مرحلة ما بعد التطوير على الأقل. سنستخدم في مشروعنا عرض الاستضافة الثابت من Netlify لاستضافة مشروعنا. تمنحنا Netlify استضافة أو عنوان URL لعرض المشروع عبر الإنترنت ومشاركته مع أصدقائك وعائلتك وزملائك. يكون النشر على الاستضافة في نهاية دورة حياة المشروع، ولكن تعمل خدمات مثل Netlify على خفض تكلفة النشر من الناحية المالية والوقت المطلوب للنشر الفعلي، إذ يمكن النشر أثناء التطوير إلى مشاركة العمل أو الحصول على إصدار تجريبي لأغراض الأخرى. تتيح خدمة Netlify تشغيل مهام ما قبل النشر، وهو ما يعني في حالتنا أنه يمكن تنفيذ جميع عمليات إنشاء شيفرة الإنتاج ضمن Netlify وإذا نجح الإصدار، فستُنشَر تغييرات موقع الويب. تقدّم Netlify خدمة النشر بالسحب والإفلات Drag and Drop Deployment Service، لكننا نعتزم بدء نشر جديد إلى Netlify في كل مرة نرفع فيها الشيفرة على مستودع GitHub. إنها بالضبط أنواع الخدمات المتصلة التي نشجعك على البحث عنها عند اتخاذ قرار بشأن سلسلة أدوات البناء الخاصة بك. يمكننا رفع شيفرتنا على GitHub، وستشغّل الشيفرة المحدثة تلقائيًا منهج البناء الكامل. إذا كان كل شيء على ما يرام، فسيُنشَر التغيير المباشر تلقائيًا، لكن الإجراء الوحيد الذي نحتاجه هو الرفع الأولي. عملية البناء بما أننا نستخدم Parcel للتطوير، فإن خيار البناء سهل الإضافة. يمكننا تشغيل الخادم باستخدام الأمر npx parcel build src/index.html بدلًا من الأمر npx parcel src/index.html، وستبني Parcel كل شيء جاهزًا للإنتاج بدلًا من تشغيله لأغراض التطوير والاختبار فقط، ويتضمن ذلك تصغير الشيفرة وتطبيق تقنية هز الشجرة عليها، وتعطيل الذاكرة المخبئية على أسماء الملفات. تُوضَع شيفرة الإنتاج التي أنشأناها في دليل جديد يسمى dist يحتوي على جميع الملفات المطلوبة لتشغيل موقع الويب، ويكون جاهزًا للتحميل على الخادم. ليس تطبيق هذه الخطوة يدويًا هدفنا النهائي، بل نريد أن يحدث البناء تلقائيًا وأن تُنشَر نتيجة الدليل dist مباشرة على موقعنا على الإنترنت. يجب إعداد شيفرتنا وGitHub وNetlify للتواصل مع بعضها بعضًا، ليكتشف Netlify التغييرات تلقائيًا ويشغّل مهام البناء ويصدر تحديثًا جديدًا في كل مرة نحدّث فيها مستودع شيفرة GitHub. سنضيف أمر البناء إلى الملف package.json بوصفه سكربت npm، ليشغّل الأمر npm run build عملية البناء. ليست هذه الخطوة ضرورية، لكنها أفضل ممارسة جيدة لعادة الإعداد في جميع المشاريع، ثم يمكننا الاعتماد على الأمر npm run build لتطبيق خطوة البناء الكاملة، دون الحاجة إلى تذكر وسطاء أمر البناء المحدَّدة لكل مشروع. افتح الملف package.json في الدليل الجذر لمشروعك، وابحث عن الخاصية scripts. سنضيف الأمر build الذي يمكننا تشغيله لبناء شيفرتنا. أضف السطر التالي إلى مشروعك: "scripts": { ... "build": "parcel build src/index.html" } ملاحظة: إذا احتوت الخاصية scripts على أمر ضمنها، فضع فاصلة في نهايتها حسب صيغة JSON. يجب أن تكون الآن قادرًا على تشغيل الأمر التالي في جذر دليل مشروعك لتشغيل خطوة بناء الإنتاج، ولكن أنهِ أولًا عملية التشغيل باستخدام الاختصار Ctrl + C: npm run build يكون خرج الأمر السابق كما يلي، ويوضح هذا الخرج ملفات الإنتاج المُنشَأة، وحجمها، والمدة التي استغرقتها للبناء: dist/src.99d8a31a.js.map 446.15 KB 63ms dist/src.99d8a31a.js 172.51 KB 5.55s dist/stars.7f1dd035.svg 6.31 KB 145ms dist/asteroid2.3ead4904.svg 3.51 KB 155ms dist/asteroid1.698d75e9.svg 2.9 KB 153ms dist/src.84f2edd1.css.map 2.57 KB 3ms dist/src.84f2edd1.css 1.25 KB 1.53s dist/bg.084d3fd3.svg 795 B 147ms dist/index.html 354 B 944ms يجب استضافة شيفرة المشروع في مستودع git الخاص بك لتتمكّن من إنشاء نسخة منه. خطوتنا التالية هي رفع المشروع على GitHub. تنفيذ التغييرات على GitHub سيساعدك هذا القسم على تجاوز حدود تخزين شيفرتك في مستودع git، ولكنك لن تتعلّم git بالتفصيل. هيّأنا دليل العمل بوصفه دليل عمل git سابقًا، وهناك طريقة سريعة للتحقق من ذلك وهي تشغيل الأمر التالي: git status يجب أن تحصل على تقرير بحالة الملفات المُتتبَّعة والملفات المُنظَّمة وما إلى ذلك، وتُعَد هذه المصطلحات جزءًا من قواعد git. إذا حصلتَ على الخطأ fatal: not a git repository، فهذا يدل على أن دليل العمل ليس دليل عمل git، وبالتالي يجب تهيئة git باستخدام الأمر git init. أمامنا الآن ثلاث مهام وهي: إضافة التغييرات التي أجريناها إلى مكان يدعَى stage، وهو اسم خاص بالمكان الذي يودع git الملفات فيه. تنفيذ التغييرات على المستودع. رفع التغييرات على GitHub. أولًا، يمكنك إضافة التغييرات من خلال تشغيل الأمر التالي: git add . لاحظ النقطة في النهاية التي تعني "كل شيء في هذا الدليل". يشبه الأمر git add .‎ إلى حدٍ ما نهج المطرقة، إذ سيضيف جميع التغييرات المحلية التي عملت عليها دفعة واحدة. إن أردت تحكمًا أفضل فيما تضيفه، فاستخدم الأمر git add -p للعمليات التفاعلية، أو أضف ملفات باستخدام الأمر git add path/to/file. ثانيًا، أصبحت الآن الشيفرة منظمة، ويمكننا تثبيت التغيير من خلال تشغيل الأمر التالي: git commit -m ’committing initial code’ ثالثًا، أخيرًا، يجب رفع الشيفرة على مستودع GitHub المستضاف. يمكنك زيارة الصفحة new في موقع github لإنشاء مستودعك لاستضافة هذه الشيفرة. رابعًا، امنح مستودعك اسمًا قصيرًا يسهل تذكره بدون مسافات (استخدم الشرطات لفصل الكلمات)، واكتب وصفًا مناسبًا، ثم انقر على زر إنشاء مستودع Create Repository في أسفل الصفحة. يجب أن يكون لديك الآن عنوان URL بعيد يؤشّر إلى مستودع GitHub الجديد الخاص بك. خامسًا، يجب إضافة هذا الموقع البعيد إلى مستودع git المحلي قبل أن نتمكن من رفعه هناك، وإلا فلن يتمكن من العثور عليه. يجب تشغيل أمر له البنية التالية (استخدم خيار HTTPS المُقدَّم حاليًا وليس خيار SSH، خاصة إذا كنت جديدًا على GitHub): git remote add github https://github.com/yourname/repo-name.git لذلك إذا كان عنوان URL البعيد الخاص بك هو https://github.com/remy/super-website.git -كما في لقطة الشاشة أعلاه- فسيكون الأمر كما يلي: git remote add github https://github.com/remy/super-website.git غيّر عنوان URL إلى المستودع الخاص بك، وشغّل الأمر. سادسًا، أصبحنا الآن جاهزين لرفع شيفرتنا على GitHub، ويمكنك الآن تشغيل الأمر التالي: git push github main سيُطلب منك الآن إدخال اسم مستخدم وكلمة مرور قبل أن يسمح Git بإرسال الرفع، لأننا استخدمنا خيار HTTPS بدلًا من خيار SSH كما رأينا سابقًا. لذلك تحتاج إلى اسم مستخدم Github الخاص بك وكلمة مرور -إن لم تكن المصادقة الثنائية Two-Factor Authentication -أو 2FA اختصارًا- مفعّلة فإننا نشجعك دائمًا على تفعيلها، ولكن ضع في بالك أنك إذا فعلتها فستحتاج لاستخدام رمز وصول شخصي بجانب كلمة السر الخاصة بالحساب. تحتوي صفحات المساعدة على Github على إرشادات بسيطة وممتازة تغطي كيفية الحصول على هذا الرمز. ملاحظة: إذا كنت مهتمًا باستخدام خيار SSH، وبالتالي تجنب الحاجة إلى إدخال اسم المستخدم وكلمة المرور في كل مرة ترفع فيها شيفرة على GitHub، فيمكنك الاطلاع على فيديو الاتصال بخدمة GitHub دون كلمة سر. يوجّه الأمر السابق git لرفع الشيفرة -أو ما يسمى بالنشر- على الموقع البعيد الذي أطلقنا عليه اسم github -وهو المستودع المستضاف على github.com ويمكننا أن نطلق عليه أي اسم نريده- باستخدام الفرع main. لم ننشئ أي فروع إضافية على هذا المشروع الإطلاق، ولكن الفرع main هو الفرع الافتراضي لعملنا وهو ما يؤسسه git بصورة افتراضية، وهو أيضًا الفرع الافتراضي الذي سيبحث عنه Netlify. الخطوة التالية في سلسلة الأدوات هي توصيل GitHub مع Netlify لنشر مشروعنا مباشرة على الويب. استخدام Netlify للنشر يُعَد النشر من GitHub إلى Netlify أمرًا بسيطًا بمجرد معرفة الخطوات، خاصة مع مواقع الويب الثابتة Static Websites مثل مشروعنا. انتقل إلى صفحة البداية في Netlify. اضغط على زر Github أسفل عنوان النشر المستمر Continuous Deployment الذي يعني أنه كلما تغير مستودع الشيفرة، فسيحاول Netlify نشرها، وبالتالي فهي مستمرة. يمكن أن تحتاج إلى ترخيص Netlify مع GitHub اعتمادًا على ما إذا أعطيت Netlify ترخيصًا من قبل، واختيار الحساب الذي تريد إعطاءه ترخيصًا، إذا كان لديك عدة حسابات أو مؤسسات على GitHub. اختر الحساب الذي رفعت مشروعك عليه. سيطالبك Netlify بقائمة من مستودعات GitHub التي يمكنك العثور عليها. حدد مستودع مشروعك وانتقل إلى الخطوة التالية. بما أننا ربطنا Netlify بحساب Github وأعطيناه إذن الوصول لنشر مستودع المشروع، فسيسأل Netlify عن كيفية إعداد المشروع للنشر وما الذي يجب نشره. يجب إدخال الأمر npm run build وتحديد الدليل dist لدليل النشر الذي يحتوي على الشيفرة التي نريد جعلها عامة. انقر على نشر الموقع Deploy site في النهاية. يجب أن تحصل بعد انتظار قصير لحدوث النشر على عنوان URL يمكنك الانتقال إليه لرؤية موقعك المنشور. إذا أجريت تغييرًا ورفعت التغيير إلى مستودع git البعيد على GitHub، فسيؤدي ذلك إلى إرسال إشعار إلى Netlify الذي سيشغّل مهمة البناء ثم ينشر دليل dist الناتج على موقعنا المنشور. جرّب إجراء تغيير بسيط على تطبيقك، ثم ارفعه إلى GitHub باستخدام الأوامر التالية: git add . git commit -m ‘simple netlify test’ git push github main يجب أن ترى تحديث موقعك المنشور بالتغيير. يستغرق ذلك بضع دقائق للنشر، لذا تحلى بالصبر. يمكننا اختياريًا تغيير اسم مشروع Netlify أو تحديد استخدام اسم نطاقنا الذي يقدّم Netlify بعض الوثائق الممتازة عنه. الاختبار يُعَد الاختبار بحد ذاته موضوعًا واسعًا حتى في مجال تطوير الواجهة الأمامية. سنوضح كيفية إضافة اختبار أولي إلى مشروعك وكيفية استخدام الاختبار للسماح بنشر المشروع أو منعه في حال وجود مشاكل. هناك طرق متعددة للتعامل مع مشاكل الاختبارات هي: الاختبار الشامل End-to-end Testing: يتضمن نقر الزائر على شيء ما مع حدوث بعض الأمور الأخرى. اختبار التكامل Integration Testing: يتضمن هذا الاختبار السؤال: "هل ستستمر بالعمل إحدى كتل الشيفرة بطريقة صحيحة عند اتصالها بكتلة أخرى؟" اختبار الوحدة Unit Testing: تُختبَر أجزاء صغيرة ومحددة من الوظائف لمعرفة ما إذا كانت تفعل ما يفترض منها فعله. تذكّر أيضًا أن الاختبارات لا تقتصر على شيفرة جافاسكربت، إذ يمكن تشغيل الاختبارات على DOM المُصيَّر، وتفاعلات المستخدم، وCSS، وحتى على مظهر الصفحة. سننشئ اختبارًا صغيرًا في مشروعنا يتحقق من بيانات وكالة ناسا التابعة لجهة خارجية والتأكد من أنها في التنسيق الصحيح. إذا لم يكن الأمر كذلك، فسيفشل الاختبار وسيمنع المشروع من العمل. الاختبار موضوع ضخم يتطلب مقالات منفصلة خاصة به، ولكننا نأمل أن يجعلك هذا القسم على الأقل مدركًا لأهمية الاختبار. لا يتضمن اختبار هذا المشروع إطارًا اختباريًا، إلا أن هناك عددًا كبيرًا من خيارات إطار العمل. ليس الاختبار مهمًا بحد ذاته، فالمهم هو كيفية التعامل مع فشل أو نجاح الاختبار. ستتضمن بعض منصات النشر طريقة محددة للاختبار كجزء من سير العمل الخاصة بها. تدعم جميع المنتجات مثل GitHub وGitLab إجراء الاختبارات على التنفيذات. بما أننا ننشر مشروعنا على Netlify الذي لا يسأل إلا عن أمر البناء، فسيتعين علينا جعل الاختبارات جزءًا من عملية البناء. إذا فشل الاختبار، فسيفشل البناء، ولن ينشر Netlify. أولًا، انتقل إلى الملف package.json وافتحه. ثانيًا، ابحث عن الخاصية scripts وحدّثها لتحتوي على أوامر البناء والاختبار التالية: "scripts": { … "test": "node tests/*.js", "build": "npm run test && parcel build src/index.html" } ثالثًا، يجب الآن إضافة الاختبار إلى قاعدة شيفرتنا. أنشئ دليلًا جديدًا في الدليل الجذر الخاص بك وسمِّه tests: mkdir tests رابعًا، أنشئ ملف اختبار ضمن الدليل الجديد: cd tests touch nasa-feed.test.js خامسًا، افتح هذا الملف وأضف محتويات الملف nasa-feed.test.js إليه. سادسًا، يستخدم هذا الاختبار حزمة axios لجلب البيانات التي نريد اختبارها. شغّل الأمر التالي لتثبيت هذه الاعتمادية: npm install --save-dev axios يجب تثبيت axios يدويًا لأن Parcel لن تساعدنا فيها. تقع اختباراتنا خارج نطاق رؤية Parcel في نظامنا، نظرًا لأن Parcel لا ترى أو تدير أيًا من شيفرة الاختبار، لذلك يجب تثبيت الاعتمادية بأنفسنا. سابعًا، يمكننا تشغيل الأمر التالي في سطر الأوامر لإجراء الاختبار يدويًا: npm run test إذا نجحت عملية الاختبار، فالنتيجة هي لا شيء، وهذا يُعَد نجاحًا بحد ذاته. كما جرى الخروج من الاختبار بإشارة خاصة تخبر سطر الأوامر بأن الاختبار ناجح، وتكون قيمة إشارة الخروج 0، وإذا كان هناك فشل، فسيفشل الاختبار مع رمز الخروج 1، وهي قيمة على مستوى النظام تدل على حدوث فشل شيء ما. يستخدم الأمر npm run test لغة Node.Js لتشغيل جميع الملفات الموجودة في دليل الاختبارات التي تنتهي بالامتداد ‎.js. يُستدعَى الأمر npm run test في سكربت البناء، ثم سترى السلسلة && التي تعني أنه "إذا نجح الشيء الموجود على اليسار (الخرج صفر)، فافعل الشيء الموجود على اليمين"، أي إذا نجحت الاختبارات، فطبّق بناء الشيفرة. ثامنًا، يجب رفع الشيفرة الجديدة إلى GitHub باستخدام أوامر التالية المماثلة لما استخدمته سابقًا: git add . git commit -m ‘adding test’ git push github main يمكن أن ترغب في بعض الأحيان في اختبار نتيجة شيفرة البناء، لأنها ليست الشيفرة الأصلية التي كتبناها، لذلك يجب تشغيل الاختبار بعد أمر البناء. أخيرًا، سينشر Netlify تحديث المشروع بعد دقيقة أو نحو ذلك من الرفع، إذا اجتاز الاختبار فقط. الخلاصة لا يزال هناك طريق طويل لقطعه قبل أن تتمكن من عَدّ نفسك ممتازًا في استخدام الأدوات من طرف العميل، لكن نأمل أن تكون هذه السلسلة من المقالات منحتك أول خطوة مهمة نحو فهم هذه الأدوات. لنلخص جميع أجزاء سلسلة الأدوات: تُطبَّق جودة الشيفرة وصيانتها بواسطة الأداتين Eslint وPrettier، وتُضاف هذه الأدوات بوصفها اعتماديات تطوير devDependencies إلى المشروع عبر الأمر npm install --dev eslint prettier eslint-plugin-react. كما يجب استخدام إضافة Eslint لأن المشروع يستخدم React. هناك نوعان من ملفات الإعداد التي تقرأها أدوات جودة الشيفرة هما: ‎.eslintrc و‎.prettierrc. نستخدم أداة Parcel أثناء التطوير للتعامل مع الاعتماديات. يعمل parcel src/index.html في الخلفية لمراقبة التغييرات وبناء الشيفرة المصدرية تلقائيًا. يُعالَج النشر عن طريق رفع التغييرات إلى Github في الفرع main، مما يؤدي إلى البناء والنشر على Netlify لنشر المشروع. يكون عنوان URL في مثالنا هو near-misses.netlify.com، وسيكون لديك عنوان URL الفريد الخاص بك. كما يوجد اختبار بسيط يمنع بناء ونشر الموقع إذا لم تعطنا NASA API تنسيق البيانات الصحيح. هذا المقال جزء من سلسلة مقالات بعنوان تعلم تطوير الويب والتي تشرح كامل عملية تطوير الويب من واجهات أمامية وخلفية بالكامل. ترجمة -وبتصرُّف- للمقال Deploying our app. اقرأ أيضًا المقال السابق: بناء نموذج كامل لسلسلة أدوات تطوير الويب من طرف العميل أساسيات بناء تطبيقات الويب كيفية نشر تطبيق Rails باستخدام AZK
  6. سنعمل في هذا المقال على ترسيخ معرفتك بأدوات تطوير الويب من طرف العميل من خلال إرشادك خلال عملية بناء نموذج لسلسلة أدوات toolchain، وسنقطع شوطًا طويلًا في إعداد بيئة تطوير ووضع أدوات التحويل في مكانها لنشر تطبيقك فعليًا على Netlify. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت. الهدف: ترسيخ ما تعلمناه حتى الآن من خلال العمل على دراسة حالة كاملة لسلسلة أدوات. هناك فعليًا مجموعات غير محدودة من الأدوات مع طرق استخدامها المختلفة، وما تراه في هذا المقال هو طريقة واحدة فقط يمكن من خلالها استخدام الأدوات المميزة لمشروع ما. وصف دراسة الحالة سنستخدم سلسلة الأدوات التي سننشئها في هذا المقال لبناء ونشر موقع صغير يعطي قائمة بيانات مأخوذة من إحدى واجهات برمجة التطبيقات المفتوحة التابعة لوكالة ناسا فيما يتعلق بالأجسام الفضائية التي يحُتمَل أن تكون خطرة وتهدد وجودنا على الأرض: كما يمكنك مشاهدة نسخة حية من الموقع على near-misses.netlify.com. الأدوات المستخدمة في سلسلة أدواتنا سنستخدم الأدوات والميزات التالية: JSX: مجموعة من امتدادات الصياغة ذات الصلة بإطار العمل React والتي تتيح تطبيق أشياء مثل تحديد بنى المكونات ضمن جافاسكربت. لن تحتاج إلى معرفة إطار عمل React لاتباع هذا المقال، لكننا ضمّنا هذه الأداة لإعطائك فكرة عن كيفية دمج لغة ويب غير أصيلة non-native ضمن سلسلة أدوات. أحدث ميزات جافاسكربت المبنية مسبقًا (في وقت كتابة النسخة الأجنبية من هذا المقال) مثل import. أدوات تطوير مفيدة مثل Prettier للتنسيق وEslint لكشف الأخطاء في الصياغة. PostCSS لتوفير إمكانات تداخل CSS. Parcel: لبناء وتقليل حجم شيفرتنا وكتابة محتوى ملف الإعداد تلقائيًا. GitHub: لإدارة التحكم في الشيفرة المصدرية. Netlify: لأتمتة عملية النشر. يمكن ألّا تكون على دراية بجميع الميزات والأدوات السابقة أو ما تفعله، ولكن لا داعي للقلق، لأننا سنشرحها لاحقًا. سلاسل الأدوات وتعقيدها المتوارث كلما زاد عدد الروابط الموجودة في سلسلة أدواتك، كانت أكثر تعقيدًا ويُحتمَل أن يكون ترابطها هشًا، وإعدادها أكثر تعقيدًا وسهلة الكسر. بينما كلما قل عدد الروابط، زادت مرونة سلسلة الأدوات. تختلف مشاريع الويب عن بعضها بعضًا، لذلك يجب التفكير في الأجزاء الضرورية من سلسلة أدواتك والنظر في كل جزء بعناية. أصغر سلسلة أدوات هي سلسلة لا تحتوي على روابط على الإطلاق. يمكنك كتابة شيفرة HTML يدويًا، واستخدام لغة جافاسكربت الصرفة Vanilla JavaScript دون وجود أطر عمل أو لغات وسيطة، وتحميلها يدويًا إلى خادم للاستضافة. لكن يُرجَّح أن تستفيد متطلبات البرامج الأكثر تعقيدًا من استخدام الأدوات للمساعدة في تبسيط عملية التطوير. كما يجب تضمين الاختبارات قبل النشر إلى خادم الإنتاج الخاص بك للتأكد من أن برنامجك يعمل على النحو المنشود، وهذا يبدو بالفعل وكأنه سلسلة أدوات ضرورية. سنستخدم في مشروعنا سلسلة أدوات مصممة خصيصًا للمساعدة في تطوير برامجنا ودعم الخيارات التقنية التي تُجرَى أثناء مرحلة تصميم البرنامج، ولكن سنتجنب أيّ أدوات غير ضرورية، بهدف تقليل التعقيد إلى الحد الأدنى. كان بإمكاننا تضمين أداة لتقليل أحجام ملفات SVG أثناء الإنشاء مثلًا، ولكن يحتوي هذا المشروع على 4 صور SVG فقط التي صغّرناها يدويًا باستخدام الأداة SVGO قبل إضافتها إلى المشروع. المتطلبات الأساسية ذكرنا خدمتين على الويب في قائمة الأدوات أعلاه إلى جانب الأدوات التي سنثبّتها وستساهم في سلسلة الأدوات الخاصة بنا. إذًا لننتهز الفرصة للتأكد من إعدادها قبل المواصلة، وستحتاج إلى إنشاء حسابات في GitHub وNetlify. GitHub هي خدمة مستودع لشيفرة مصدرية تضيف مجموعة ميزات خاصة بمجتمع المطورين مثل تتبع المشكلات وإصدارات المشروع التالية وغير ذلك. سنرفع لاحقًا الشيفرة إلى مستودع شيفرة GitHub الذي سيضيف تأثيرًا تسلسليًا لنشر البرامج على الصفحة الرئيسية في الويب. Netlify هي خدمة استضافة لمواقع الويب الثابتة، أي مواقع الويب التي تتكون بالكامل من ملفات لا تتغير في الوقت الحقيقي، والتي تتيح النشر عدة مرات في اليوم واستضافة المواقع الثابتة من جميع الأنواع بحريّة. توفر Netlify الصفحة الرئيسية على الويب، وبالتالي توفّر استضافة مجانية لنشر تطبيق الاختبار الخاص بنا عليه. يمكنك التسجيل في GitHub (من خلال النقر على رابط التسجيل Sign Up في الصفحة الرئيسية إن لم يكن لديك حساب سابقًا، واتبع تعليمات استخدام حساب GitHub في عملية الاستيثاق على Netlify (انقر على تسجيل Sign Up، ثم اختر GitHub من قائمة "التسجيل باستخدام إحدى القوائم التالية Sign up with one of the following")، لذلك ما عليك سوى إنشاء حساب جديد. ستحتاج لاحقًا إلى ربط حساب Netlify بمستودع GitHub لنشر هذا المشروع، وسنرى كيفية تطبيق ذلك في المقال التالي. مراحل الأدوات تُنظَّم سلسلة الأدوات ضمن المراحل التالية: شبكة الأمان Safety Net: تجعل تجربة تطوير البرمجيات مستقرة وأكثر كفاءة، ونشير إليها بوصفها بيئة التطوير. التحويل Transformation: هي الأدوات التي تتيح استخدام أحدث ميزات لغة مثل لغة جافاسكربت أو لغة أخرى مثل JSX أو TypeScript في عملية التطوير، ثم تحوّل شيفرتنا لكي يستمر إصدار الإنتاج يعمل على مجموعة متنوعة من المتصفحات الحديثة والقديمة. ما بعد التطوير Post Development: هي الأدوات التي تُشغَّل بعد الانتهاء من التطوير لضمان وصول برنامجك إلى الويب واستمراره في العمل. سننظر في إضافة اختبارات إلى شيفرتنا، ونشر التطبيق باستخدام Netlify لتكون متاحةً على الويب ويمكن للمستخدمين مشاهدته. لنبدأ العمل بدءًا من بيئة التطوير خاصتنا. إنشاء بيئة تطوير يُنظَر أحيانًا إلى هذا الجزء من سلسلة الأدوات على أنه يؤخر العمل الفعلي، إذ ستقضي كثيرًا من الوقت في محاولة جعل البيئة مناسبة تمامًا. لكن يمكنك النظر إلى هذا الجزء بالطريقة نفسها التي تُعِد بها بيئة عملك المادية، إذ يجب أن يكون الكرسي مريحًا ومجهزًا في وضع جيد، ويجب استخدام منافذ طاقة وواي فاي وUSB، ويمكن لبعض الزخارف أو الموسيقى أن تحسين مزاجك، إذ تُعَد هذه الأشياء مهمة لتنفّذ أفضل عمل ممكن، وتجهز لمرة واحدة فقط إن احسنت تجهيزها. وكذلك يجب إعداد بيئة التطوير الخاصة بك بالطريقة نفسها، ويمكن إعدادها مرة واحدة فقط وإعادة استخدامها في العديد من المشاريع المستقبلية، إذا أُعِدت بطريقة صحيحة. يمكن أن ترغب في مراجعة هذا الجزء من سلسلة الأدوات بطريقة شبه منتظمة والتفكير فيما إذا كان هناك أيّ ترقيات أو تغييرات يجب إدخالها، ولكن لا ينبغي أن يكون هذا مطلوبًا كثيرًا. ستعتمد سلسلة أدواتك على احتياجاتك الخاصة، ولكن بالنسبة لمثالنا الخاص بسلسلة أدوات كاملة، فإن الأدوات التي يجب تثبيتها مقدمًا هي: أدوات تثبيت المكتبة Library Installation Tools لإضافة الاعتماديات. التحكم في مراجعة الشيفرة Code Revision Control. أدوات ترتيب الشيفرة Code Tidying Tools لتنظيم شيفرات جافاسكربت وCSS وHTML. أدوات كشف أخطاء الشيفرة Code Linting Tools. أدوات تثبيت المكتبة سنستخدم مدير الحزم npm لتثبيت أدواتنا، ويجب أن يكون لديك Node.js وnpm مثبتين مسبقًا. سنستخدم npm لتثبيت الأجزاء اللاحقة من سلسلة أدواتنا، وسنثبّت git للمساعدة في التحكم في المراجعة. التحكم في مراجعة الشيفرة يُحتمَل أنك سمعت عن أداة Git من قبل، إذ تعد أداة Git حاليًا من أكثر أدوات التحكم في مراجعة الشيفرة المصدرية شيوعًا والمتاحة للمطورين، إذ توفر التحكم في مراجعة الشيفرة البرمجية بالإضافة للعديد من المزايا مثل طريقة نسخ عملك الاحتياطي في مكان بعيد وآلية العمل ضمن فريق في المشروع نفسه دون الخوف من الكتابة فوق شيفرة بعضنا بعضًا. Git وGitHub مختلفان، إذ تُعَد أداة Git أداةً التحكم في المراجعة، بينما يُعَد موقع GitHub مخزنًا على الإنترنت لمستودعات Git بالإضافة إلى عدد من الأدوات المفيدة للعمل معها. لاحظ أنه بالرغم من أننا نستخدم GitHub في هذا المقال، إلا أن هناك العديد من البدائل بما في ذلك GitLab وBitbucket، كما يمكنك استضافة مستودعات git الخاصة بك على جهازك الشخصي ببساطة. سيساعد استخدام التحكم في المراجعة في مشاريعك وإدراجه بوصفه جزءًا من سلسلة الأدوات في إدارة تطور شيفرتك، وتوفر طريقة للالتزام بكتل العمل أثناء تقدمك، جنبًا إلى جنب مع تعليقات مثل "تنفيذ ميزة جديدة معينة"، أو "إصلاح خطأ معين بسبب تغييرات محددة". كما يمكن أن يتيح لك التحكم في المراجعة تفريع شيفرة مشروعك، وإنشاء إصدار منفصل وتجربة وظائف جديدة، دون أن تؤثر تلك التغييرات على الشيفرة الأصلية. أخيرًا، يمكن أن يساعدك على التراجع عن تغييرات أو إعادة الشيفرة إلى الوقت الذي كانت تعمل فيه عند إدخال خطأ في مكان ما دون إصلاحه، وسيحتاجُ جميع المطورين ذلك من حين لآخر. يمكن تنزيل Git وتثبيته عبر موقع git-scm على الويب. نزّل برنامج التثبيت المناسب لنظامك، وشغّله، واتبع التعليمات التي تظهر على الشاشة. يمكنك التفاعل مع git بعدة طرق مختلفة، من استخدام سطر الأوامر إلى استخدام تطبيق git GUI لإصدار الأوامر من خلال الضغط على الأزرار، أو حتى من داخل محرر الشيفرة الخاص بك مباشرةً، كما هو موضح في المثال التالي في Visual Studio Code: لكن تثبيت git هو كل ما نحتاجه حاليًا. أدوات ترتيب الشيفرة سنستخدم الأداة Prettier لترتيب شيفرتنا في المشروع. ثبّت Prettier، أو يمكنك تثبيتها بوصفها أداة مساعدة عالمية باستخدام الطرفية Terminal. يمكنك التحقق مما إذا كانت Prettier مثبّتة عالميًا باستخدام الأمر التالي: prettier -v ستحصل في حالة التثبيت على رقم إصدار مُعاد مثل رقم الإصدار 2.0.2، وإن لم تكن مثبّتة، فسيعيد شيئًا مثل "الأمر غير موجود command not found". إن لم تكن مثبّتة، فثبتها باستخدام الأمر التالي: npm install prettier -g يمكن بعد ذلك تشغيل الشيفرة وترتيبها في سطر الأوامر على أساس ملف من أي مكان على حاسوبك كما يلي: prettier --write ./src/index.html استخدمنا في الأمر السابق الأداة Prettier مع الراية ‎--write، إذ ستفهمُ Prettier أن هذا يعني أنه "إذا كانت هناك مشاكل في تنسيق شيفرتك، فتابع وأصلحها، ثم احفظ الملف"، وهذا يناسب عملية التطوير الخاصة بنا، ولكن يمكننا استخدام Prettier بدون الراية وستتحقق من الملف فقط. يُعد التحقق من الملف -دون حفظه- مفيدًا لأغراض مثل عمليات التحقق المُشغَّلة قبل الإصدار، أي "لا تصدر أيّ شيفرة ليس تنسيقها صحيحًا". ولكن تشغيل الأمر على كل ملف أمرًا صعبًا، وبالتالي سيكون استخدام أمر واحد لذلك مفيدًا جدًا، وينطبق الشيء نفسه على أدوات كشف الأخطاء Linting الخاصة بنا. هناك العديد من الطرق المختلفة لحل هذه المشكلة ومنها: استخدام سكربتات npm لتشغيل أوامر متعددة من سطر الأوامر دفعة واحدة مثل الأمر npm run tidy-code. استخدام خطّافات جيت git hooks الخاصة لاختبار ما إذا كانت الشيفرة منسَّقة قبل الالتزام. استخدام إضافات plugins محرر الشيفرة لتشغيل أوامر prettier في كل مرة يُحفَظ الملف فيها. أحد الإضافات المفيدة في VS Code هو Prettier Code Formatter من Esben Petersen التي تتيح تنسيق الشيفرة تلقائيًا عند الحفظ، وهذا يعني تنسيق أي ملف في المشروع الذي نعمل عليه، بما في ذلك ملفات HTML وCSS وجافاسكربت وJSON وmarkdown وغير ذلك. كل ما يحتاجه محرّر الشيفرة هو تفعيل "التنسيق عند الحفظ Format On Save". كما يمكن استخدام Prettier دون الحاجة إلى تهيئة أي شيء، إن كنت راضيًا عن الإعدادات الافتراضية. أدوات كشف أخطاء الشيفرة يساعد كشف الأخطاء Linting في تحسين جودة الشيفرة، وهو وسيلة لكشف الأخطاء المحتمَلة في وقت مبكر أثناء عملية التطوير. كما أنه مكون رئيسي في سلسلة الأدوات وتتضمنه العديد من مشاريع التطوير افتراضيًا. تكون أدوات كشف أخطاء تطوير الويب خاصة بلغة جافاسكربت في أغلب الأحيان، بالرغم من توفر القليل منها للغتي HTML وCSS. إذا اُستخدِم عنصر HTML غير معروف أو خاصية CSS غير صالحة، فيُحتمَل ألّا ينكسر أيّ شيء نظرًا للطبيعة المرنة لهاتين اللغتين. بينما تُعَد لغة جافاسكربت أكثر هشاشة، فمثلًا يؤدي استدعاء دالة غير موجودة عن طريق الخطأ إلى تعطّل الشيفرة، لذلك يُعَد كشف أخطاء شيفرة جافاسكربت مهمًا جدًا خاصةً في المشاريع الكبيرة. أداة الانتقال إلى كشف أخطاء شيفرة جافاسكربت هي Eslint التي تُعَد أداة قوية ومتعددة الاستخدامات، ولكن يمكن أن تكون صعبة نوعًا ما لإعدادها بطريقة صحيحة، إذ يستغرق الأمر غالبًا عدة ساعات في محاولة الحصول على الإعداد الصحيح تمامًا. ستظهر الأداة Eslint خطأ بأنه لا يمكنها العثور على ملف الإعداد إذا شغّلته. يدعم ملف الإعداد تنسيقات متعددة ولكننا سنستخدم في مشروعنا ملف ‎.eslintrc.json، وتعني النقطة في بداية اسم الملف أن الملف مخفي افتراضيًا. تُثبَّت الأداة Eslint باستخدام npm، لذلك لديك خيار تثبيت هذه الأداة تثبيتًا محليًا أو عامًا، إذ يوصَى باستخدام كليهما: يجب دائمًا تضمين Eslint كاعتمادية محلية في المشاريع التي تنوي مشاركتها ليتمكن أي شخص من إنشاء نسخته الخاصة اتباع القواعد التي طبقتها على المشروع. يجب أن تفكر في تثبيت الأداة Eslint تثبيتًا عامًا لتمكنك من استخدامها بسرعة للتحقق من أي ملف تريده. لن نشرح جميع ميزات Eslint في هذا المقال، لكننا سنضع إعدادًا مناسبًا لمشروعنا الخاص ومتطلباته. ضع في بالك أنه إذا رغبتَ في تحسين وفرض قاعدة حول كيفية ظهور شيفرتك أو التحقق من صحتها، فيُحتمَل أن يُطبَّق ذلك باستخدام إعداد Eslint الصحيح. سنقدم لاحقًا ملف إعداد Eslint، إذ يمكن أن يؤدي تشغيل الأمر إلى إنشاء بعض المعلومات المفيدة. فيما يلي مثال لخرج Eslint: ./my-project/src/index.js 2:8 error 'React' is defined but never used no-unused-vars 22:20 error 'body' is defined but never used no-unused-vars 96:19 error 'b' is defined but never used no-unused-vars ✖ 3 problems (3 errors, 0 warnings) يُعَد دعم تكامل محرر الشيفرة مفيدًا لأداة Eslint، ويُحتمَل أن يكون أكثر فائدة لأنه يمكن أن يقدم لنا ملاحظات في الوقت الحقيقي عند ظهور المشاكل: إعداد المشروع الأولي يمكن إعداد مشروع جديد بأمان باستخدام هذه الأدوات مع العلم أن العديد من المشاكل الأساسية ستُكتشَف مبكرًا. يمكننا إنشاء المشروع وتثبيت الأدوات الأولية وإنشاء ملفات إعداد أولية باستخدام سطر الأوامر، ثم ستشعر بما يجب أن يكون عليه الإعداد الافتراضي بمجرد تكرار هذه العملية عدة مرات. حسنًا لنبدأ الإعداد المشروع الأولي. ابدأ بفتح الطرفية، وانتقل إلى مكان يمكنك العثور عليه والوصول إليه بسهولة مثل سطح المكتب Desktop أو المجلد الرئيسي أو مجلد المستندات. ثم شغّل الأوامر التالية لإنشاء مجلد لتحتفظ بمشروعك فيه، وانتقل إلى المجلد: mkdir will-it-miss cd will-it-miss سننشئ الآن دليلًا جديدًا لجميع شيفرات تطوير موقع الويب لنضعها فيه. شغّل الأمر التالي: mkdir src يختلف تنظيم الشيفرة من فريق لآخر. سنضع شيفرة مشروعنا المصدرية في الدليل src. تأكد من أنك داخل جذر الدليل will-it-miss، ثم أدخِل الأمر التالي لبدء وظيفة التحكم في شيفرة git المصدرية التي تعمل في الدليل: git init هذا يعني أنك ستتمكن من بدء تخزين مراجعات محتويات المجلد، وحفظها في مستودع بعيد وغير ذلك. أدخل الأمر التالي بعد ذلك لتحويل دليلك إلى حزمة npm مع المزايا التي ناقشناها في المقال السابق: npm init --force سيؤدي الأمر السابق إلى إنشاء ملف package.json افتراضي يمكننا تهيئته لاحقًا إذا رغبنا في ذلك. كما يؤدي استخدام الراية ‎--force في أن ينشئ الأمر ملف package.json افتراضي على الفور دون طرح جميع الأسئلة المعتادة حول المحتويات التي تريدها كما رأينا سابقًا، وسنحتاج فقط الإعدادات الافتراضية حاليًا، وبالتالي نوفر قليلًا من الوقت. الحصول على ملفات شيفرة المشروع سنحصل في هذه المرحلة على ملفات شيفرة المشروع (HTML وCSS وجافاسكربت وغيرها)، وسنضعها في الدليل src. لن نعلّمك كيفية عملها، لأنه ليس هدف هذا المقال، بل هدفنا تشغيل الأدوات لتتعلّم كيفية عملها. يمكنك الحصول على ملفات الشيفرة من خلال زيارة هذه الصفحة وتنزيل وفك ضغط محتويات هذا المستودع على قرصك الصلب المحلي في مكانٍ ما. يمكنك تنزيل المشروع بأكمله كملف مضغوط عن طريق تحديد خيار نسخ Clone أو تنزيل Download ثم Download ZIP. انسخ محتويات الدليل src الخاص بالمشروع إلى دليل src فارغ حاليًا. أصبحت ملفات مشروعنا في مكانها الصحيح، هذا كل ما نحتاجه حاليًا. تثبيت أدواتنا حان الوقت الآن لتثبيت المجموعة الأولية من الأدوات التي سنستخدمها في بيئة التطوير الخاصة بنا. شغّل الأمر ضمن الدليل الجذر لمشروعك: npm install --save-dev eslint prettier babel-eslint هناك شيئان مهمان يجب ملاحظتهما حول الأمر الذي شغّلته للتو. الأول هو أننا نثبّت الاعتماديات محليًا على المشروع، إذ يُعَد تثبيت الأدوات محليًا أفضل لمشروع معين. يتيح التثبيت محليًا إعادة إنشاء الإعداد بسهولة على أجهزة أخرى، ولكن لا يشمل ذلك الخيار ‎--global. الجزء الثاني المهم من أمر التثبيت هو الخيار ‎--save-dev الذي يخبر npm أن هذه الاعتماديات المعينة مطلوبة فقط من أجل التطوير، لذلك يسردها npm في الملف package.json ضمن اعتماديات التطوير devDependencies وليس ضمن الاعتماديات devDependencies، وبالتالي إذا ثُبِّتَ هذا المشروع في وضع الإنتاج، فلن تُثبَّت هذه الاعتماديات. يمكن أن يحتوي المشروع النموذجي على العديد من اعتماديات التطوير التي لا حاجة إليها لتشغيل الشيفرة فعليًا في الإنتاج، ويؤدي إبقائها كاعتماديات منفصلة إلى تقليل العمل غير الضروري عند النشر في عملية الإنتاج، وهو ما سنلقي نظرة عليه في المقال التالي. يجب تطبيق إعداد قبل البدء في تطوير شيفرة التطبيق الفعلية لتعمل أدواتنا بطريقة صحيحة، وهو ليس شرطًا أساسيًا في تطوير الويب، ولكن يُعَد إعداد الأدوات بطريقة صحيحة أمرًا مفيدًا، إذا ساعد في اكتشاف الأخطاء أثناء عملية التطوير، وهو أمر مفيد خاصة لأداة Eslint. إعداد أدواتنا سنضيف ملفات الإعداد في جذر المشروع -وليس في الدليل src- لإعداد الأدوات Prettier وEslint. يمكنك العثور على ملفات الإعداد في جذر المشروع، والتي لا تحتوي غالبًا على خيارات الإعداد المعبَّر عنها ببنية JSON، بالرغم من أن أدواتنا والعديد من الأدوات الأخرى تدعم بنية YAML أيضًا، والتي يمكنك التبديل إليها إذا كانت المفضلة لديك. أنشئ أولًا ملفًا في جذر الدليل will-it-Miss وسمّه ‎.prettierrc.json. يمكنك إعداد Prettier من خلال وضع المحتويات التالية في الملف ‎.prettierrc.json: { "singleQuote": true, "trailingComma": "es5" } إذا استخدمت الإعدادات السابقة مع تنسيقات Prettier الخاصة بلغة جافاسكربت، فستستخدم علامات اقتباس فردية لجميع القيم المقتبسة، ولن تستخدم فواصل لاحقة، وهي ميزة أحدث في ECMAScript ستتسبب في حدوث أخطاء في المتصفحات القديمة. يجب إعداد Eslint بعد ذلك من خلال إنشاء ملف آخر في جذر الدليل will-it-miss بالاسم ‎.eslintrc.json، وضع فيه المحتويات التالية: { "env": { "es6": true, "browser": true }, "extends": "eslint:recommended", "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, "rules": { "no-console": 0 } } يشير إعداد Eslint السابق إلى أننا نريد استخدام إعدادات Eslint الموصَى بها، وأننا سنسمح باستخدام ميزات ES6 مثل map()‎ أو Set()‎، ويمكننا استخدام عبارات استيراد الوحدة import، وأن استخدام التابع console.log()‎ مسموح. لكننا نستخدم صيغة JSX الخاصة بإطار عمل React في ملفات المشروع المصدرية، ويمكن أن تستخدم في مشاريعك الحقيقية إطار العمل React أو Vue أو أي إطار عمل آخر، ويمكن ألّا تستخدم إطار عمل على الإطلاق. سيؤدي وضع صيغة JSX في شيفرة جافاسكربت إلى أن تظهر أخطاء Eslint بسرعة كبيرة من الإعداد الحالي، لذلك سنحتاج إلى إضافة مزيد من إعدادات Eslint لقبول ميزات JSX. يجب أن يبدو ملف الإعداد config file النهائي على النحو التالي: { "env": { "es6": true, "browser": true }, "extends": ["eslint:recommended", "plugin:react/recommended"], "parserOptions": { "ecmaVersion": 6, "sourceType": "module", "ecmaFeatures": { "jsx": true } }, "plugins": ["react"], "rules": { "semi": "error", "no-console": 0, "react/jsx-uses-vars": "error" } } بما أن الإعداد يستخدم مكونًا إضافيًا يسمى React، فيجب تثبيت اعتمادية التطوير هذه حتى تكون الشيفرة موجودة لتشغيل هذا الجزء من عملية كشف الأخطاء. شغّل الأمر التالي في الطرفية ضمن جذر مجلد مشروعك: npm install --save-dev eslint-plugin-react هناك قائمة كاملة بقواعد Eslint التي يمكنك تعديلها وإعدادها وفقًا لمحتواك، بالإضافة إلى ذلك نشرت العديد من الشركات والفرق إعدادات Eslint الخاصة بها، والتي يمكن أن تكون مفيدة في بعض الأحيان إما للاستلهام منها أو لتحديد إحداها التي تناسب معاييرك الخاصة. وبذلك اكتمل إعداد بيئة التطوير، وأصبحنا جاهزين لكتابة الشيفرة تقريبًا. أدوات البناء والتحويل سنستخدم إطار العمل React في مشروعنا، مما يعني استخدام صيغة JSX في الشيفرة المصدرية. كما سنستخدم في مشروعنا أحدث ميزات لغة جافاسكربت. هناك مشكلة وهي أنه لا يوجد متصفح يدعم JSX بطريقة أصيلة، لإنها لغة وسيطة ويُفترَض أن تُصرَّف إلى لغات يفهمها المتصفح في شيفرة الإنتاج. إذا حاول المتصفح تشغيل شيفرة جافاسكربت المصدرية، فستظهر أخطاء على الفور، إذ يحتاج المشروع إلى أداة بناء لتحويل الشيفرة المصدرية إلى شيء يمكن أن يفهمه المتصفح دون مشاكل. هناك عدد من الخيارات لأدوات التحويل، وبالرغم من أن WebPack هو خيار شائع، لكننا سنستخدم Parcel في مشروعنا لأنها تتطلب إعدادًا أقل بكثير. تعمل Parcel على أساس أنها ستحاول إعداد متطلبات التطوير الخاصة بك على الفور، إذ ستراقب الشيفرة وتشغّل خادم ويب لإعادة التحميل مباشرةً أثناء التطوير، وبالتالي ستثبّت Parcel اعتماديات برامجنا تلقائيًا كما هو مشار إليها في الشيفرة المصدرية. كما ستهتم Parcel بتثبيت أي أدوات تحويل وإعداد المطلوب دون الحاجة إلى التدخل في معظم الحالات، ويمكن أن تُجمّع Parcel الشيفرة البرمجية وتجهّزها للنشر في وضع الإنتاج مع الاهتمام بالتصغير ومتطلبات توافق المتصفح. لذلك يجب تثبيت اعتمادية parcel في مشروعنا من خلال تشغيل الأمر التالي في طرفيتك: npm install --save-dev parcel-bundler استخدام الميزات المستقبلية تستخدم شيفرة مشروعنا بعض ميزات الويب الجديدة بما في ذلك الميزات الجديدة جدًا التي لم يكتمل توحيدها بعد مثل استخدام اقتراح W3C لتداخل CSS بدلًا من الوصول إلى أداة مثل الأداة Sass. يسمح تداخل nesting لغة CSS بأن تتداخل محدّدات وخصائص CSS ضمن بعضها بعضًا، وبالتالي ينشأ مجال محدِّد selector أكثر تحديدًا. كانت Sass من أوائل المعالجات المسبقة التي تدعم التداخل -إن لم تكن الأولى فعلًا- ولكن يبدو أن التداخل سيُوحَّد قريبًا، مما يعني أننا سنوفره في متصفحاتنا دون الحاجة إلى أدوات البناء. حتى ذلك الحين، ستحوّل Parcel بين CSS المتداخلة وCSS المدعومة بطريقة أصيلة بمساعدة PostCSS. بما أننا قررنا أن مشروعنا يجب أن يستخدم تداخل CSS بدلًا من Sass، فسيحتاج المشروع إلى تضمين إضافة PostCSS. لنستخدم postcss-preset-env التي تتيح "استخدام CSS المستقبلية اليوم use tomorrow's CSS today" من خلال اتباع الخطوات التالية: أضف ملفًا بالاسم ‎.postcssrc إلى جذر دليل المشروع. أضف المحتويات التالية إلى الملف الجديد التي ستمنحنا تلقائيًا وصولًا كاملًا إلى أحدث ميزات CSS: { "plugins": { "postcss-preset-env": { "stage": 0 } } } وهذا كل ما نحتاجه. تذكر أن Parcel تثبّت الاعتماديات افتراضيًا. يمكن أن تكون هذه المرحلة من سلسلة أدواتنا صعبة، لأننا اخترنا أداة تحاول تقليل الإعداد والتعقيد، لكن لا يوجد شيء يجب تطبيقه أثناء مرحلة التطوير، إذ تُستورَد الوحدات استيرادًا صحيحًا، وتحوَّل CSS المتداخلة إلى CSS عادية تحويلًا صحيحًا، ولا تعوق عمليةُ البناء عمليةَ التطوير. تشغيل التحويل سنشغّل خادم Parcel في سطر الأوامر لبدء العمل في مشروعنا الذي سيراقب التغييرات في شيفرتنا ويثبّت الاعتماديات تلقائيًا، وبالتالي لن نضطر إلى الانتقال ذهابًا وإيابًا بين الشيفرة وسطر الأوامر. انتقل إلى الطرفية وشغّل الأمر التالي لبدء تشغيل Parcel في الخلفية: npx parcel src/index.html يجب أن ترى الخرج التالي بمجرد تثبيت الاعتماديات: Server running at http://localhost:1234 ✨ Built in 129ms. كما يثبّت Parcel الاعتماديات التي سنستخدمها في شيفرتنا، بما في ذلك react وreact-dom وreact-async-hook وdate-fns وformat-number، وبالتالي سيكون تشغيل Parcel الأول أطول من التشغيل المعتاد. يعمل الخادم الآن على عنوان URL المطبوع، وهو localhost: 1234 في حالتنا. انتقل إلى عنوان URL السابق في متصفحك وسترى التطبيق قيد التشغيل. يمتلك Parcel خدعة ذكية أخرى وهي أن أي تغييرات تطرأ على شيفرتك المصدرية ستؤدي إلى تحديث في المتصفح. يمكنك تجربة هذه الميزة من خلال ما يلي: حمّل الملف src/components/App.js في محرر النصوص المفضل لديك. ابحث عن النص "near misses"، واستبدله بشيء مثل "flying fish". احفظ الملف، ثم ارجع مباشرة إلى التطبيق المُشغَّل في متصفحك. ستلاحظ تحديث المتصفح تلقائيًا، وتغيير السطر "‎ there will be near misses" في أعلى الصفحة. كما يمكنك تجربة استخدام Eslint وPrettier. حاول إزالة المسافة البيضاء من أحد ملفاتك وحاول وضع ‎‎‎Prettier عليه لتنظيفه، أو أدخل خطأً صياغيًا في أحد ملفات جافاسكربت الخاصة بك وشاهد الأخطاء التي يعطيها Eslint عندما تحاول استخدام Parcel لبنائه مرة أخرى. الخلاصة أنشأنا في هذا المقال بيئة تطوير محلية بسيطة إلى حد ما لإنشاء تطبيق فيها، إذ يمكنك في هذه المرحلة أثناء تطوير برامج الويب صياغة شيفرة برنامجك الذي تنوي إنشائه. بما أننا نتكلم عن تعلم الأدوات المتعلقة بتطوير الويب، وليس شيفرة تطوير الويب نفسها، فلن نعلمك كتابة شيفرة فعلية. لذلك كتبنا مثالًا لمشروع لتستخدم فيه أدواتك. نقترح عليك العمل في المقال التالي باستخدام شيفرة مثالنا، ثم يمكنك محاولة تغيير محتويات دليل src في مشروعك ونشره على Netlify، وسنناقش مرحلة النشر على Netlify في المقال التالي. هذا المقال جزء من سلسلة مقالات بعنوان تعلم تطوير الويب والتي تشرح كامل عملية تطوير الويب من واجهات أمامية وخلفية بالكامل. ترجمة -وبتصرُّف- للمقال Introducing a complete toolchain. اقرأ أيضًا المقال السابق: أساسيات إدارة الحزم في تطوير الويب من طرف العميل دليل استخدام سطر الأوامر في عملية تطوير الويب من طرف العميل فهم أدوات تطوير الويب من طرف العميل
  7. قد ترغب في نشر صفحة الويب البسيطة التي بنيتها ليزورها الجميع وذلك برفعها إلى خادم ويب، لهذا سنناقش في هذا المقال طريقة تنفيذ الأمر مستخدِمين عدة خيارات وأدوات متاحة، مثل عميل SFTP وRSync وGitHub وGoogle App Engine، كما يُنصح قبل متابعة القراءة بالاطلاع على مفهوم خادم الويب وآلية عمل أسماء النطاقات، كما ينبغي أن تكون على دراية بطريقة إعداد بيئة عمل بسيطة من أجل تطوير الويب وكيفية كتابة صفحات ويب. بروتوكول نقل الملفات الآمن SFTP ستجد العديد من الأدوات التي تخدم مثل عميل لبروتوكول SFTP، وسنغطي في مقالنا FileZilla كونها أداةً مجانيةً متاحةً للاستخدام في ويندوز وماك ولينوكس، ولتثبيت الأداة FileZilla، انتقل إلى صفحة التنزيل الرئيسية الخاصة بها، ثم انقر الزر الكبير الذي يحمل العنوان تنزيل Download، بعد ذلك ثبتها من خلال ملف التثبيت الذي نزّلته بالطريقة الاعتيادية. افتح تطبيق FileZilla وسترى نافذةً تشبه النافذة في الصورة التالية: تسجيل الدخول سنفترض في مثالنا أنّ مزود خدمة الاستضافة -أي الخدمة التي تستضيف خادم ويب HTTP الخاص بنا- هي الشركة الوهمية "Example Hosting Provider" التي تعطي لمواردها عناوين URL. لنفترض أننا أنشأنا حسابًا وتلقينا المعلومات التالية: لنلق نظرةً في البداية على العنوان /http://demozilla.examplehostingprovider.net، وبالطبع لن تجد شيئًا فيه. اتبع الآن الخطوات التالية لتتصل بالخادم البعيد عبر عميل SFTP الذي ثبتّه: اختر من القائمة الرئيسية: ملف File > مدير الموقع Site Manager. انقر على الزر "موقع جديد New Site" في نافذة "مدير الموقع Site Manager" وسمِّ الموقع "demozilla" ضمن المساحة المخصصة لذلك. ضع في حقل "المضيف Host" اسم خادم SFTP الذي زودتك به الشركة. اختر "عادي Normal" من قائمة نمط تسجيل الدخول Logon Type المنسدلة، ثم اكتب اسم المستخدِم وكلمة المرور في الأماكن المخصصة لذلك. ضع رقم المنفذ وبقية المعلومات المطلوبة في أماكنها المخصصة. انقر الآن على زر "اتصال Connect" لتتصل بالخادم. العرض المشترك للموارد على حاسوبك وعلى الخادم ستبدو نافذة التطبيق كما يلي عند نجاح الاتصال بالخادم، والصورة التالية ما هي إلا مثال خاص لإيضاح الفكرة فقط: لنلق نظرةً على ما تعرضه الصورة: ستجد في القسم اليساري نافذة الملفات المحلية، حيث تستطيع من خلالها الانتقال إلى المجلد الذي تخزّن فيه موقعك. ستجد في القسم اليميني اليساري نافذة الملفات الموجودة على الخادم -أي البعيدة- بعد دخولنا إلى المجلد الخاص بالموقع على الخادم عبر بروتوكول SFTP. يمكنك تجاهل الأقسام العليا والسفلى حاليًا، وهي بالترتيب نافذة سجل الرسائل التي تعرض حالة الاتصال بين حاسوبك وخادم SFTP، وتمثل الأخرى سجلًا آنيًا لمعلومات التفاعل بين عميل SFTP الخاص بك والخادم. رفع الموارد إلى الخادم تخبرنا إرشادات خادم الاستضافة في مثالنا أنه علينا وضع الموارد في المجلد البعيد Public/htdocs حتى تُنشَر على الويب، إذًا لا بدّ من الانتقال إلى المجلد المحدَّد ضمن القسم اليساري من نافذة العميل. ويمثِّل هذا المجلد جذر موقع الويب، إذ ينبغي وجود الملف الرئيسي للموقع index.html، وبقية الموارد الداعمة له ضمن هذا المجلد، وعندما تصل إلى المجلد الجذر، اسحب الملفات والموارد التي تريد رفعها إلى الخادم من القسم الأيسر لنافذة العميل إلى القسم الأيمن من النافذة. التحقق من وجود الملفات على ويب بعد رفعها يمكنك التحقق من وجود الملفات التي رفعتها بطلب عنوان موقعك عبر المتصفح في مثالنا. /http://demozilla.examplehostingprovider.net وبهذا تكون العملية قد اكتملت بنجاح. استخدام الأداة Rsync تُستخدَم الأداة Rsync لمزامنة الملفات المحلية والبعيدة، وتتوفر الأداة عمومًا في معظم الأنظمة المبنية على يونكس مثل ماك ولينوكس، كما تتواجد نسخ تعمل على ويندوز أيضًا، ويُنظَر إلى هذه الأداة على أنها أكثر تقدمًا من عملاء SFTP لأنها تستخدَم في سطر الأوامر افتراضيًا، إذ يبدو الأمر الأساسي كما يلي: rsync [-options] SOURCE user@x.x.x.x:DESTINATION ‎-options‎‎‎: وهي عبارة عن شرطة - يتبعها حرف أو أكثر مثل v- لإظهار رسائل الخطأ الطويلة وb- لإجراء نسخ احتياطي، كما يمكنك الاطلاع على قائمة الخيارات ضمن الصفحة الرئيسية للأداة Rsync. SOURCE: يمثِّل المسار إلى الملف أو المجلد المحلي الذي تريد نسخ الملفات منه. @user: يمثِّل ثبوتيات المستخدِم على الخادم البعيد الذي تريد نسخ الملفات إليه. x.x.x.x: عنوان IP الخاص بالخادم البعيد. DESTINATION: يمثِّل المسار أو الموقع على الخادم البعيد الذي تريد نسخ الملفات أو المجلدات إليه. عليك الحصول على المعلومات السابقة من مزود خدمة الاستضافة، كما من الجيد دائمًا استخدام اتصال آمن كما هو الحال مع FTP، لذلك يمكن تحديد التفاصيل الخاصة ببروتوكول SSH لتنفيذ الاتصال باستخدامه من خلال الخيار e-، وإليك المثال التالي: rsync [-options] -e "ssh [هنا SSH ضع تفاصيل]" SOURCE user@x.x.x.x:DESTINATION واجهات رسومية للأداة Rsync أدوات الواجهات الرسومية متوفرة لهؤلاء الذين لا يرتاحون بالتعامل مع سطر الأوامر مثل الأداة Acrosync المتاحة في نظامَي التشغيل ويندوز وماك، وتذكَّر أنه عليك الحصول على الثبوتيات اللازمة للوصول إلى موقعك من خادم الاستضافة، إذ سيكون دور الواجهة الرسومية تسهيل إدخال هذه الثبوتيات. نشر موقع ويب على جيت هاب GitHub يُعَدّ جيت هاب GitHub موقع ويب لكتابة الشيفرة بصورة جماعية، حيث يمكّنك من رفع الشيفرة وتخزينها في مستودعات منظومة إدارة النُسخ الخاصة بالموقع، كما يتيح لك هذا الموقع المفتوح المصدر لاحقًا إمكانية التعاون مع أطراف أخرى للعمل على شيفرتك، حيث يمكن لأيّ كان الوصول إلى شيفرتك واستخدامها والتعلم منها وتطويرها أيضًا، كما يمكنك بالطبع فعل الأمر ذاته مع أيّ شيفرة ضمن الموقع، وسيرشدك هذا المقال إلى كيفية نشر المحتوى على الويب باستخدام الميّزات التي تقدمها صفحات جيت هاب GitHub-pages. نشر المحتوى يُعَدّ جيت هاب مجتمعًا غايةً في الأهمية والفائدة، كما تُعَدّ جيت Git منظومة إدارة نُسخ شعبيةً جدًا تستخدِمها معظم الشركات حاليًا أثناء تطوير برمجياتها، كما يمتلك جيت هاب ميزةً هامةً تُدعى صفحات جيت هاب تتيح لك نشر شيفرة المواقع على الويب مباشرةً. الإعدادات الأساسية ثبّت جيت أولًا على حاسوبك، فهي البنية البرمجية التحتية لمنظومة إدارة النسخ ويعمل جيت هاب اعتمادًا عليها. سجّل بعد ذلك حسابًا جديدًا على جيت هاب، وهذه العملية بسيطة وسهلة. سجّل دخولك إلى الموقع عندما يكتمل حسابك الجديد باستخدام كلمة السر واسم المستخدِم الخاصَّين بك. تحضير الشيفرة لرفعها يمكنك تخزين الشيفرة التي تريد في مستودعات جيت هاب، لكن لا بدّ من تنظيم شيفرتك على أساس شيفرة موقع ويب تقليدي حتى تستفيد من ميزة صفحات جيت هاب بكامل طاقتها، إذ ينبغي مثلًا وجود نقطة دخول إلى موقعك على شكل ملف HTML يحمل الاسم index.html، كما يجب أيضًا تهيئة مجلد الشيفرة ليصبح مستودع جيت قبل متابعة العمل، واتبع الخطوات التالية لتنفيذ الأمر: انتقل من خلال سطر الأوامر إلى المجلد الذي يحتوي على موقعك مستخدِمًا تعليمة تغيير المجلد cd وسنفترض أنه يحمل الاسم test-site وموجود على سطح المكتب: cd Desktop/test-site نفّذ التعليمة التالية عندما تصل إلى داخل المجلد المطلوب، والتي تطلب من الأداة git تحويل مجلدك إلى مستودع جيت: git init واجهات سطر الأوامر ستكون أفضل الطرق لرفع الشيفرة إلى جيت هاب هي عبر سطر الأوامر، إذ سيظهر على صورة نافذة تكتب فيها التعليمات التي تنفذ وظائف معينة مثل إنشاء ملفات أو تنفيذ برنامج معيّن، بدلًا من النقر على أزرار واجهة مستخدِم ما، وستبدو نافذة سطر الأوامر مشابهةً للصورة التالية: يحتوي أي نظام تشغيل على أداة سطر الأوامر: ويندوز: وتُدعى موجِّه الأوامر Command Prompt، ويمكن الوصول إلى الأداة بالضغط على المفتاح Windows ثم كتابة Command Prompt واختياره من القائمة التي ستظهر، وتذكّر أنّ سطر أوامر ويندوز مختلف عن مثلائه في لينوكس وماك، لذلك قد تختلف التعليمات التي ستراها تاليًا وفقًا لنظام التشغيل. OS X: وتُدعى طرفية Terminal، وللوصول إليها انتقل إلى تطبيقات Applications، ثم مرافق Utilities. لينوكس: وتُدعى طرفية Terminal أيضًا، ويمكن الوصول إليها عادةً بضغط المفاتيح Ctrl + Alt + T، وإذا تعذر ذلك، فابحث عنها ضمن شريط التطبيقات أو في القائمة. قد يخيفك الأمر في البداية، لكنك ستعتاد على هذه المهام الروتينية، فكل ما عليك فعله هو إخبار الحاسوب بما تحتاج من خلال كتابة الأمر المناسب ثم الضغط على المفتاح Enter. بناء مستودع خاص بشيفرتك لا بدّ من إنشاء مستودع جديد لملفاتك على أساس خطوة ثانية. انقر على زر الإضافة (+) في أعلى يمين صفحة جيت هاب الرئيسية، بعد ذلك اختر مستودعًا جديدًا. اكتب اسم المستودع المناسب لشيفرتك في صندوق اسم المستودع Repository name عندما تنتقل إلى صفحة إنشاء مستودع جديد، ليكون مثلًا my-repository. اكتب معلومات عن المحتويات التي ستضعها في المستودع في حقل الوصف Description كما في الصورة التالية: انقر على زر إنشاء مستودع Create repository وستظهر لك الصفحة التالية: رفع الملفات إلى جيت هاب سترى سطرين من الشيفرة في صفحة المستودع الجديد وفق القسم الذي يحمل العنوان "… أو ادفع مستودعًا موجودًا عبر سطر الأوامر or push an existing repository from the command line…"؛ انسخ السطر الأول بالكامل والصقه في نافذة سطر الأوامر، ثم اضغط المفتاح Enter، إذ سيبدو الأمر مشابهًا للصورة التالية: git remote add origin https://github.com/chrisdavidmills/my-repository.git اكتب سطرَي الأوامر التاليين ثم اضغط المفتاح Enter بعد كل سطر، حيث تحضِّر التعليمات السابقة الشيفرة للرفع إلى جيت هاب وتطلب من جيت إدارة ملفات الشيفرة. git add --all git commit -m 'adding my files to my repository' ادفع الشيفرة في النهاية إلى جيت هاب بالانتقال إلى صفحة الويب جيت هاب التي كنا فيها، ثم أدخل السطر الثاني من الشيفرة الموجودة في القسم "… أو ادفع مستودعًا موجودًا عبر سطر الأوامر or push an existing repository from the command line…" إلى الطرفية أو موجِّه سطر الأوامر واضغط المفتاح Enter. git push -u origin main علينا الآن تفعيل ميزة صفحات جيت هاب في المستودع الجديد الذي رفعت إليه ملفات الشيفرة، لذلك اختر الأمر "إعدادات Setting" في صفحة الويب الخاصة بمستودعك ثم الأمر "صفحات Pages" من الشريط الجانبي على يسار الصفحة، واختر بعد ذلك الفرع "رئيسي Main" أسفل الأمر مصدر Source، إذ سيسبب ذلك تحديث الصفحة. انتقل إلى قسم صفحات جيت هاب مجددًا، وسترى سطرًا جديدًا مفاده أنّ موقعك أصبح جاهزًا ليُنشر على العنوان https://xxxxxx. إذا نقرت على عنوان URL السابق، فستنتقل إلى نسخة حية لموقعك تُعرض فيها محتويات الصفحة التي تُدعى index.html، والتي تُعرض افتراضيًا؛ لكن إذا كانت الصفحة الرئيسية لموقعك لا تحمل الاسم السابق وإنما myPage.html مثلًا، فعليك حينها الانتقال إليها بنفسك بطلب عنوانها https://xxxxxx/myPage.html. معلومات أكثر عن جيت هاب إذا أردت إجراء تعديلات أكثر على موقعك ومن ثم رفع الشيفرة المعدَّلة إلى جيت هاب، فعليك تطبيق التغييرات التي أجريتها سابقًا على ملفات موقعك كما فعلت سابقًا، ثم إدخال الأوامر التالية مع الضغط على المفتاح"Enter" بعد كل أمر لدفع التعديلات إلى جيت هاب: git add --all git commit -m 'another commit' git push ضع مكان العبارة another commit أيّ رسالة أخرى تراها مناسبة لوصف التعديلات التي أجريتها. يُعَدّ ما قدمناه عن جيت بدايةً متواضعةً جدًا، ولتتعمق أكثر عليك الاطلاع على المزيد من المقالات والدورات التعليمية حول جيت وجيت هاب. استضافة مواقع ويب على محرك جوجل آب يُعَدّ محرك جوجل آب Google App Engine منصةً فعالةً لبناء وتشغيل التطبيقات بالاستفادة من البنية التحتية لجوجل، سواءً أردت بناء موقع ويب من الصفر، أو استضافة موقع ويب ساكن static website، كما سنرشدك فيما يأتي إلى طريقة استضافة موقعك على جوجل آب خطوةً بخطوة. إنشاء مشروع على منصة جوجل السحابية لا بدّ من إنشاء مشروع جديد على منصة جوجل السحابية Google Cloud Platform قبل البدء باستخدام أدوات جوجل، ويتطلب ذلك بالطبع امتلاك حساب على جوجل. انتقل إلى لوحة محرك التطبيقات App Engine dashboard ضمن طرفية منصة جوجل السحابية، وانقر زر إنشاء Create. إذا لم تنشئ مشروعًا من قبل، فعليك تحديد ما إذا أردت استقبال بريد إلكتروني عن آخر التحديثات أم لا، ثم الموافقة على شروط الخدمة. ويفترض بعدها أن تكون قادرًا على متابعة العمل. اختر اسمًا للمشروع ثم اعدل على معرفه ID، واحفظه، إذ سنستخدِم القيم التالية في هذه الإرشادات: اسم المشروع: GAE Sample Site. معرّف المشروع: gaesamplesite. انقر على زر إنشاء Create لإنشاء مشروعك. إنشاء تطبيق يمكن أن يضم كل مشروع سحابي تطبيقًا واحدًا وهذا ما سنفعله الآن: نحتاج إلى تطبيق تجريبي لننشره، فإذا لم يكن لديك تطبيق لاستخدامه، فنزِّل التطبيق التجريبي الذي أعددناه لهذا الغرض وفك ضغطه. لنلق نظرةً على هيكلية التطبيق التجريبي، حيث يحتوي المجلد website على ملفات المشروع بالإضافة إلى ملف الإعدادات app.ymal. لا بدّ أن تكون محتويات موقعك ضمن المجلد website وأن تحمل الصفحة الرئيسية للموقع الاسم index.html وما عدا ذلك يمكنها أخذ الشكل الذي تريد. يخبر ملف الإعدادات app.yaml محرك التطبيقات عن كيفية ربط العناوين التي تقود إلى ملفاتك الساكنة، ولا حاجة لتعديل أيّ شيء فيه. نشر التطبيق سنحاول نشر التطبيق التجريبي الآن بعد تجهيزنا إياه: افتح مفسّر أوامر أو صدفة سحابة جوجل Google Cloud Shell. اسحب المجلد sample-app وافلته في لوحة محرر الشيفرة. نفِّذ الأمر التالي لاختيار مشروعك: gcloud config set project gaesamplesite نفّذ الأمر التالي للانتقال إلى مجلد تطبيقك: cd sample-app بهذا تكون قد أصبحتَ جاهزًا الآن لنشر التطبيق، أي لرفع تطبيقك إلى محرك تطبيقات جوجل: gcloud app deploy أدخِل رقمًا ليدل على المنطقة التي ستضع فيها تطبيقك. اضغط المفتاح Y لتأكيد ما اخترته. انتقل إلى تطبيقك عبر المتصفح بكتابة عنوانه على الصورة your-project-id.appspot.com لمشاهدة موقعك على الإنترنت، وسنستبدل your-project-id في تطبيقنا التجريبي بمعرِّف التطبيقgaesamplesite ليصبح العنوان على الصورة gaesamplesite.appspot.com. طرق أخرى لرفع الملفات يّعَدّ بروتوكول FTP من البروتوكولات المعروفة جيدًا لنشر مواقع الويب، لكنه بالطبع ليس الوحيد في هذا المضمار، وإليك بعض الخيارات الأخرى: واجهات ويب Web interfaces: وهي واجهة مكتوبة بلغة HTML تعمل على أساس واجهة أمامية لخدمة رفع ملفات عن بعد موجودة على خادم الاستضافة. WebDAV: وهو توسعة لبروتوكول HTTP يسمح بإدارة أوسع للملفات. اقرأ أيضا اطلع على توثيق محرك تطبيقات جوجل عبر صفحته الرسمية. لابد من الإشارة إلى إمكانية استضافة المواقع على جيت هاب مستخدمًا اسم النطاق الذي تريده، كما يمكنك الاطلاع على معلومات أكثر عن الموضوع من خلال ملفات المساعدة الإلكترونية الخاصة بموقع جيت هاب. ترجمة -وبتصرف- للمقالات التالية: ?How do you upload your files to a web server. ?How do I use GitHub Pages. ?How do you host your website on Google App Engine. اقرأ أيضًا مساعدة المبتدئين في فهم كيفية رفع الموقع على الإنترنت مدخل إلى أسماء النطاقات في شبكة الإنترنت الفرق بين صفحة الويب وموقع الويب وخادم الويب ومحرك البحث المدخل الشامل لتعلم تطوير الويب
  8. إن كنت نشرت موقعك الإلكتروني وأصبح متاحًا للجميع، فهل أنت على ثقة بأن كل شيء يجري على ما يرام؟ سنناقش في هذا المقال عدة خطوات قد تتبعها لتتأكد من عمل موقعك، وبعض الإجراءات التي يمكن اتخاذها لتلافي بعض المشاكل التي تظهر. يملك خادم الويب البعيد سلوكًا قد يختلف أحيانًا عن سلوك الخادم المحلي الذي اختبرت عليه موقعك، لهذا من الأفضل اختبار الموقع عند نشره على الويب مباشرةً، وقد تفاجئك كمية المشاكل التي قد تظهر مثل الصور الغير معروضة أو الصفحات الغير محمَّلة أو التي تعاني بطأً في التحميل وغيرها، لكنك ستجد لحسن الحظ أنّ معظم هذه المشاكل ليست بتلك الخطورة، وإنما هي أخطاء بسيطة أو بعض الإشكالات في إعدادات الخادم الذي يستضيف الموقع. سنطّلع إذًا في مقالنا على الطريقة التي نُشخِّص بها المشاكل التي تعترض موقع الويب إبان نشره وآلية معالجتها، وإليك بعض الاختبارات البسيطة التي لابد من إجرائها فور نشر موقعك على الويب. اختبر الموقع على متصفحك لا بدّ من تشغيل موقعك الإلكتروني من خلال متصفحك فور نشره على الويب كي تتفحص عمله وتتأكد من سير كل شيء كما هو متوقع له. صورة مفقودة لنلق نظرةً على هذه الصفحة الاختبارية من موزيلّا، حيث ستلاحظ عدم وجود صورة يونيكورن unicorn. لنلق نظرةً على الأداة "شبكة الاتصال Network" والتي نصل إليها من خلال: أدوات Tools> أدوات مطوري ويب Web Developers> شبكة الاتصال Network. سنجد أنّ المشكلة في الخطأ "404" والذي يشير إلى أنّ المورد المطلوب غير موجود، وبالتالي لن يعرِض المتصفح الصورة. حالات HTTP يستجيب الخادم برسالة توضِّح حالة الطلب في كل مرة يتلقى طلبًا، وإليك أكثر الرسائل شيوعًا: 200 (موجود OK): المورد الذي تطلبه قد أرسل إليك بنجاح. 301 (نُقل نهائيًا Moved Permanently): بمعنى أنه قد نُقل المورد إلى مكان آخر، ولن يعرِض متصفحك هذه الرسالة كثيرًا، لكن من المفيد الاطلاع على فحواها، وذلك لأنّ محركات البحث تستخدمها بكثرة من أجل تحديث فهارسها. 304 (لم يتغير Not modified): لم تطرأ أية تعديلات على المورد منذ آخر مرة طلبته فيها، وبالتالي سيتمكن المتصفح من عرض النسخة التي يحتفظ بها في ذاكرته المؤقتة cache، مما يزيد من سرعة الاستجابة وفعالية استخدام نطاق حزمة التراسل المخصصة. 403 (ممنوع Forbidden): لا يُسمح لك بالوصول إلى المَورد، وعادةً ما يكون السبب خطأً في الإعدادات، إذ يغفل المضيف عن إعطائك أذونات الوصول إلى المَورد على سبيل المثال. 404 (غير موجود Not found): تشرح الحالة نفسها بنفسها وسنناقش حلها لاحقًا. 500 (خطأ داخلي في الخادم Internal Server Error): قد تتوقف أحد لغات البرمجة التي تعمل على الخادم مثل PHP أو Net. عن العمل عندما تحدث مشكلة في الخادم، كما قد يعاني خادم ويب من أخطاء في الإعدادات، وبالتالي عليك العودة في هذه الحالة إلى فريق العمل الذي يُشرف على الاستضافة. 503 (الخدمة غير متوفرة Service unavailable): ينتج هذا الخطأ عادةً عن زيادة حمولة مؤقتة على النظام أو حدوث خطأ على الخادم، وبالتالي عليك التجريب مجددًا بعد فترة. سيواجه المبتدئون الذين يتحققون من مواقعهم البسيطة الحالات 200 و304 و403 و404 غالبًا. حل المشكلة 404 ما الخطأ الذي حدث؟ تبدو الصورة التي نبحث عنها في مكانها للوهلة الأولى، لكن مع ذلك يعيد الخادم الخطأ 404، فإذا دققنا الآن في شيفرة HTML، فسنلاحظ الخطأ المطبعي الذي ارتكبناه في كتابة اسم الصورة unicorn_pics.png بدلًا من unicorn_pic.png، لذا صحِّح الخطأ ضمن السمة src لوسم الصورة لتحل المشكلة. احفظ التغييرات وادفع بالملف المصحَّح إلى الخادم، ثم حمِّل الصفحة من جديد على متصفحك: لنلق نظرةً مجددًا على حالتَي HTTP المعروضتين أسفل الصورة السابقة: 200: الموافقة لطلبَي الإحضار / وunicorn_pic.png، وهذا يعني أننا نجحنا في إعادة تحميل الصفحة وفي عرض الصورة. 304: الموافقة لطلب إحضار الملف basic.css، وتعني عدم تغيّر الملف منذ آخر مرة طُلِب فيها، وبالتالي يمكن للمتصفح استخدام النسخة المحفوظة منه ضمن ذاكرته المؤقتة عوض تلقي نسخة جديدة وهكذا نكون قد أوجدنا حلًا لمشكلتنا وتعلمنا في الوقت نفسه شيئًا مفيدًا عن بعض حالات HTTP. أخطاء تحدث باستمرار إليك أكثر الأخطاء التي نواجهها تكرارًا: أخطاء مطبعية في العناوين إذا أردنا مثلًا كتابة العنوان Http://academy.hsoub.com ونسينا كتابة الحرف "u" بسبب السرعة، فلن يتمكن المتصفح من إيجاد العنوان. الخطأ 404 قد ينتج الخطأ أحيانًا عن خطأ مطبعي، لكنه ينتج في أحيان أخرى عن نسيان رفع المورد إلى الخادم أو عن فقدان الاتصال أثناء رفعه، لذا تحقق من كتابة وصحة مسار الملف، وارفع الملفات مجددًا إذا استمرت المشكلة، إذ ستحل هذه الإجراءات البسيطة المشكلة غالبًا. أخطاء جافاسكربت أضاف أحد ما -أنت مثلًا- سكربت إلى صفحة ويب وارتكب خطأً، إذ لن يمنع ذلك الخطأ تحميل الصفحة، لكنك ستدرك وجود خطب ما. تعرض لك الصورة السابقة الخطأ الذي ارتكبته بدقة، وبالتالي سيسهل علينا إصلاحه. أمور إضافية ينبغي التحقق منها لقد زودناك ببعض الوسائل البسيطة التي ستساعدك في التحقق من صحة عمل موقعك والأخطاء التي تقع باستمرار وكيفية التعامل معها، كما يمكنك التأكد من تحقيق صفحتك للمعايير التالية: أداء الصفحة هل تُحمّل الصفحة بسرعة كافية؟ قد تساعدك بعض الموارد مثل WebPageTest.org أو بعض إضافات المتصفح مثل YSlow في فهم بعض أمور مهمة مثل هذه. لاحظ كيف تصنِّف YSlow الصفحات من الدرجة A حتى F، فقد حققت الصفحة البسيطة في مثالنا معظم معايير الأداء، لذلك صنفت على أنها من الدرجة A مع ملاحظة ضرورة استخدام شبكة تسليم محتوى Content Deleivery Ntework -أو CDN اختصارًا-، كما لن يكون استخدامها أمرًا ملحًا في صفحتنا التي تعرض صورةً واحدةً، وإنما عندما يقدِّم موقع الويب آلاف الصور ويعاني من زيادة مستمرة في عرض حزمة الاستهلاك. استجابة الخادم يمكن استخدام الأداة ping الموجودة في مفسِّرات الأوامر في اختبار اسم النطاق والتأكد من استجابة الخادم الذي يستضيفه: $ ping mozilla.org PING mozilla.org (63.245.215.20): 56 data bytes 64 bytes from 63.245.215.20: icmp_seq=0 ttl=44 time=148.741 ms 64 bytes from 63.245.215.20: icmp_seq=1 ttl=44 time=148.541 ms 64 bytes from 63.245.215.20: icmp_seq=2 ttl=44 time=148.734 ms 64 bytes from 63.245.215.20: icmp_seq=3 ttl=44 time=147.857 ms ^C --- mozilla.org ping statistics --- 4 packets transmitted, 4 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 147.857/148.468/148.741/0.362 ms انتبه إلى إمكانية استخدام الاختصار Ctrl+C من خلال لوحة المفاتيح في مقاطعة عمل الأداة عندما تحصل على المعلومات التي تريد، إذ ستعمل الأداة إلى ما لانهاية إذا لم توقفها. قائمة تحقق بسيطة تحقق من الأخطاء التي تحمل الرقم 404. تحقق من عمل كل صفحات موقعك كما هو مطلوب. استعرض موقعك ضمن عدة متصفحات لتتأكد من تصييره بالصورة الصحيحة. لقد رفعت موقعك وأصبح جاهزًا ليستقبل الزوار، إذ أنجزت خطوةً متقدمةً وعليك الآن التعمق في تفاصيل أكثر. قد يزور موقعك أشخاص من مختلف أرجاء العالم، لذلك عليك التفكير في جعله متاحًا للجميع. هل تعتقد أنّ موقعك يحتاج إلى المزيد من الصقل؟ إذًا عليك تعلم المزيد حول لغة أوراق الأنماط المتتالية CSS. ترجمة -وبتصرف- للمقال ?How do you make sure your website works properly. اقرأ أيضًا كيف تنشر صفحة أو موقع ويب قمت بتصميمه على الإنترنت الفرق بين صفحة الويب وموقع الويب وخادم الويب ومحرك البحث كيفية التعامل مع الويب المدخل الشامل لتعلم تطوير الويب
  9. يختلف أثر رسوم CSS المتحركة الناتجة عن التغيير التدريجي لخصائص التنسيق من خاصية إلى أخرى، وقد تؤدي عمليات التحريك المكثفة إلى جمود المتصفح وهو يحاول تحقيق معدل إطارات مناسب لعرض أكثر نعومةً، كما تساعدك الأداتين Frame rate وWaterfall في الإضاءة على عمل المتصفح أثناء تنفيذه هذه الرسوميات لتشخيص مشاكل الأداء التي تنتج عنها. تحدِّد عدد الإطارات عندما تستخدِم رسوم CSS المتحركة، إذ تستخدِم CSS كل إطار لتعريف مظهر العنصر خلال مراحل الحركة، ويعرض المتصفح الحركة بالانتقال من إطار إلى آخر، كما تُعَدّ رسوميات CSS المتحركة أسهل تنفيذًا من رسوميات جافاسكربت، كما تقدم أداءً أفضل أيضًا، وذلك لأنها تمنح المتصفح قدرًا أكبر من التحكم بتوقيت تصيير الإطارات، وإمكانية تجاوز بعضها إذا كان ذلك ضروريًا. يختلف تأثير التعديلات التي نُحدثها على خصائص CSS تبعًا للخاصية المستهدفة. فتحريك العناصر باستخدام خصائص تستهلك موارد أعلى، سيفضي إلى عرقلة أداء المتصفح الذي يحاول جاهدًا بلوغ معدل إطارات مرتفع. تسلسل تصيير رسوميات CSS يُحدِّث المتصفح صفحة ويب عندما تتغير خاصية CSS عبر عملية متسلسلة تتضمن الخطوات التالية: إعادة ضبط التنسيق Recalculating Style: لا بدّ للمتصفح من إعادة ضبط التنسيق في كل مرة تتغير فيها خاصية CSS لعنصر. تخطيط الصفحة Layout: يستخدِم المتصفح التنسيق الجديد في الخطوة الثانية لضبط موقع وهندسة العنصر، وتدعى هذه المرحلة تخطيط الصفحة أو إعادة الانسياب reflow. رسم الصفحة Paint: لا بدّ للمتصفح في النهاية من إعادة رسم العناصر على الشاشة، ولم نعرض في التسلسل السابق خطوةً أخيرةً تقتضي بتقسيم الصفحة أحيانًا إلى طبقات يُعاد رسمها بصورة مستقلة، ثم تُدمج مجددًا بعملية تدعى التركيب Composition. لا بدّ من اتساع إطار واحد للتسلسل السابق طالما أنّ الشاشة لن تُحدِّث حتى اكتمال سلسلة التنفيذ السابقة، ومن المعروف أنّ معدل 60 إطار في الثانية كافٍ لإظهار الرسوميات المتحركة بصورة سلسة وناعمة على الشاشة، ويتطلب هذا من المتصفح تنفيذ السلسلة السابقة خلال 16.7 ميلي ثانية. تأثير الخاصية على الأداء يؤثر تحريك بعض خاصيات CSS أكثر من غيرها على أداء صفحة الويب إليك بعض الأمثلة: table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } نوع الخاصية Cost مثال ستتسبب الخصائص التي تتحكم تغيراتها بموقع وهندسة العنصر في وقوع أحداث إعادة ضبط التنسيق وتخطيط الصفحة وإعادة رسمها left max-width border-width margin-left font-size لن تتسبب الخصائص التي لا تتحكم تغيراتها بموقع وهندسة العنصر ولا تُصيّر في طبقة مستقلة في وقوع حدث تخطيط الصفحة. color لا تتسبب الخصائص التي تُصيّر في طبقة مستقلة في وقوع حدث إعادة الرسم أيضًا، لأن تحديث الصفحة سيُنفَّذ في خطوة التركيب.. transform opacity يمكنك الاطلاع على تأثير كل خاصية من خاصيات CSS على ملف الأداء وفقًا للمتصفح المستخدَم عبر الموقع CSS Triggers. مثال تطبيقي: موازنة بين أثر الخاصيتين margin وtransform سترى خلال هذه الفقرة كيف تشير الأداة إلى الفرق بين تحريك الكائنات من خلال الخاصية margin والخاصية transform، كما لا يهدف هذا السيناريو إلى إقناعك بأنّ استخدام الخاصية فكرة سيئة دومًا، وإنما لتوضيح فائدة أدوات تحليل الأداء في الإضاءة على عمل المتصفح أثناء تصيير موقعك وكيفية استخدام هذه الإضاءات لتشخيص وتصحيح مشاكل الأداء، فإذا أردت تجريب الأمر، فانتقل إلى صفحة المثال التطبيقي، والتي تبدو بالشكل التالي: تملك الصفحة أداتَي تحكم: زر تشغيل/ توقف: لتشغيل وإيقاف تشغيل الحركة. مجموعة أزرار اختيار: لاختيار أسلوب الحركة باستخدام transform أو margin. ستجد في الصفحة مجموعةً من العناصر أضفنا لها خلفيةً بألوان متدرجة باستخدام الخاصية linear-gradient وظلال حولها بتطبيق الخاصية shadow لأنّ كلتا الخاصيتين السابقتين شديدتا التأثير على الأداء. تحريك العناصر باستخدام margin ابقي الخيار على Use margin وابدأ الحركة، ثم افتح الأداة Performance وأنشئ تسجيلًا جديدًا، فليس عليك في الواقع سوى تسجيل بضعة ثوان. افتح التسجيل الأول، إذ سيعتمد ما تراه على حاسوبك وحمولة نظام التشغيل، لكنه سيبدو قريبًا من الشكل التالي: تظهر في الشكل ثلاثة مشاهد مختلفة: (a): نظرة عامة للأداة Waterfall. (b): معدل الإطارات. (c): تفاصيل الشريط الزمني. نظرة عامة للأداة Waterfall تُظهِر اللوحة مشهدًا مضغوطًا للأداة Waterfall يسيطر عليه اللون الأخضر الذي يوحي بقضاء المتصفح معظم وقته في عمليات الرسم. معدل الإطارات يُظهِر الشكل السابق معدّل الإطارات الذي يشير إلى وسطي قدره 46.67 إطار في الثانية، وهو أقل من المعدل المستهدَف البالغ 60 إطار في الثانية، والأسوأ من ذلك التعرجات الشديدة والانخفاضات المتكررة التي تصل بالوسطي إلى العشرينات والعشرات، فمن غير المحتمل رؤية حركة ناعمة في هذه الحالة وخاصةً عندما تضيف إليها العمليات الناتجة عن تفاعل المستخدِم مع الصفحة. تفاصيل الأداة Waterfall يعرض باقي التسجيل تفاصيل الأداة Waterfall، فإذا تنقَّلت خلال التفاصيل، فستجد نمطًا متكررًا كما يلي: يُظهِر الشكل السابق تسلسل عملية تصير الصفحة، إذ يُعاد ضبط تنسيق كل عناصر الصفحة في كل إطار، ثم تجري عملية تخطيط واحدة للصفحة، ومن ثم إعادة رسم الصفحة، إذ تُخفّض عملية الرسم كما تلاحظ من أداء الصفحة. سلطنا الضوء في لقطة الشاشة السابقة على عملية رسم، إذ يخبرنا امتدادها أنها استغرقت 13.11 ميلي ثانية من أصل 16.7 ثانية وهو زمن تنفيذ عمليات الإطار بكاملها إذا أردنا أداءً سلسًا وناعمًا، وهذا هو السبب وراء انخفاض معدل الإطارات وعدم استقراره، كما من الممكن تعديل الاختبار بإزالة الظلال حول العناصر، ومن ثم ترى أثر ذلك على الأداء، وسنحاول تاليًا استخدام الخاصية transform بدلًا من margin وسنرى النتيجة. تحريك العناصر باستخدام الخاصية transform اختر الآن زر Use transform ضمن خيارات التشغيل في صفحة المثال التطبيقي السابقة وأنشئ تسجيلًا ثانيًا، إذ سيبدو شكل لوحة النظرة العامة للأداة Performance كما يلي: نظرة عامة للأداة Waterfall لاحظ وجود علامات خضراء أقل بكثير من المشهد نفسه عند استخدام الخاصية margin والكثير من اللون الزهري، والذي يوحي بعمليات تخطيط كثيرة أو عمليات إعادة ضبط تنسيق الصفحة كثيرة. معدل الإطارات Frame Rate يبدو الوضع أفضل موازنةً مع الحالة السابقة، فقد حققت الصفحة معدلًا قريبًا من 60 إطار في الثانية، كما يبدو معدل الإطارات في تزايد مستمر بغض النظر عن الانخفاض الملموس في معدل الإطارات في البداية. تفاصيل الأداة Waterfall تدل التفاصيل على سبب التحسن في معدل الإطارات موازنةً بالحالة السابقة، إذ لا يقضي المتصفح وقتًا طويًلا في تخطيط الصفحة أو في عملية الرسم وهي الناحية الأهم في حالتنا. حسَّن استخدام الخاصية transform أداء الموقع بصورة واضحة، وقد أرشدتنا الأداة Performance إلى طبيعة التحسن وسببه. ترجمة -وبتصرف للمقال Animating CSS Properties. اقرأ أيضًا المرجع الشامل إلى التحريك عبر CSS أساسيات التحريك: إنشاء صفحات ويب تفاعلية متجاوبة المكونات الرئيسية للأداة Performance لتحليل أداء صفحات الويب التحريك عبر CSS
  10. سنركِّز في هذا المقال على الشمولية Accessibility (أو تترجم إلى سهولة وصول أيضًا) بما في ذلك إدارة التركيز Focus Management في React التي يمكنها تحسين قابلية الاستخدام وتقليل الارتباك لكل من مستخدمي لوحة المفاتيح فقط وقارئات الشاشة. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript ومعرفة استخدام سطر الأوامر أو الطرفية. الهدف: تعلّم كيفية تنفيذ إمكانية وصول مستخدِمي لوحة المفاتيح في React. مستخدمو لوحة المفاتيح أنجزنا حتى الآن جميع الميزات التي أردنا تنفيذها، إذ أصبح بإمكان المستخدِم إضافة مهمة جديدة، وتحديد المهام، وإلغاء تحديدها، وحذف المهام، وتعديل أسماء المهام، وترشيح قائمة المهام جميعها أو المهام النشطة أو المهام المكتملة، إذ يمكن للمستخدِمين تنفيذ جميع هذه المهام باستخدام الفأرة، ولكن لا يمكن لمستخدِمي لوحة المفاتيح فقط الوصول إلى هذه الميزات بسهولة. استكشاف مشكلة قابلية استخدام لوحة المفاتيح انقر على حقل الإدخال الموجود أعلى التطبيق كما لو أنك تريد إضافة مهمة جديدة، إذ سترى خطًا سميكًا يمثل حدود العنصر outline حول حقل الإدخال، إذ تُعَدّ هذه الحدود المؤشر المرئي على تركيز المتصفح على هذا العنصر حاليًا، لذا اضغط على مفتاح Tab من لوحة المفاتيح، وسترى ظهور المخطط حول زر الإضافة Add تحت حقل الإدخال، إذ يدل ذلك على انتقال تركيز المتصفح. اضغط على مفتاح Tab عدة مرات، وسترى مؤشر التركيز المتقطع ينتقل بين أزرار الترشيح، واستمر في الضغط على مفتاح Tab إلى أن يصبح مؤشر التركيز حول زر التعديل Edit الأول، ثم اضغط على مفتاح Enter، إذ سيبدّل المكوِّن <Todo /‎> بين القوالب كما صمّمنا، وسترى نموذجًا يتيح تعديل اسم المهمة. ولكن قد تتساءل عن مكان وجود مؤشر التركيز حاليًا، إذ تُزال تمامًا العناصر التي كانت موجودةً سابقًا لاستبدال شيء آخر بها عندما نبدِّل بين القوالب في المكوِّن <Todo /‎>، وهذا يعني اختفاء العنصر الذي ركّزنا عليه سابقًا، ولا يوجد شيء نركّز عليه حاليًا، فقد يؤدي ذلك إلى إرباك مجموعة كبيرة من المستخدِمين، وخاصةً المستخدِمين الذين يعتمدون على لوحة المفاتيح أو الذين يستخدِمون قارئ الشاشة، كما يمكن تحسين تجربة مستخدِمي لوحة المفاتيح وقارئ الشاشة من خلال إدارة تركيز المتصفح بأنفسنا. التركيز بين القوالب إذا بدَّل المستخدِم قالب <Todo/‎> من قالب العرض إلى قالب التعديل، فيجب علينا التركيز على العنصر <input> المستخدَم لإعادة تسميته، ويجب علينا إعادة التركيز مرةً أخرى إلى زر التعديل Edit عند التبديل مرةً أخرى من قالب التعديل إلى قالب العرض. استهداف العناصر يجب علينا إخبار React بالعنصر الذي نريد التركيز عليه في نموذج DOM وكيفية العثور عليه، ويساعدنا في ذلك الخطّاف useRef في React الذي ينشئ كائنًا له الخاصية current، إذ يمكن أن تكون هذه الخاصية مرجعًا لأيّ شيء نريده، ثم يمكننا البحث عن هذا المرجع لاحقًا، وهذا مفيد للإشارة إلى عناصر DOM، لذا عدِّل تعليمة الاستيراد import في الجزء العلوي من الملف Todo.js لتتضمن الخطّاف useRef كما يلي: import React, { useRef, useState } from "react"; أنشئ بعد ذلك ثابتَين جديدين بعد الخطّافات في الدالة Todo()‎، إذ يجب أن يكون أحد هذين الثابتين مرجعًا لزر التعديل Edit في قالب العرض والآخر لحقل التعديل في قالب التعديل. const editFieldRef = useRef(null); const editButtonRef = useRef(null); تملك هذه المراجع قيمةً افتراضيةً هي null لأنها ستكون بلا قيمة حين ربطها بالعناصر الخاصة بها من خلال إضافة الخاصية ref لكل عنصر، وضبط قيمها على كائنات ref المسماة بأسماء مناسبة، ويجب تعديل مربع النص <input> في قالب التعديل كما يلي: <input id={props.id} className="todo-text" type="text" value={newName} onChange={handleChange} ref={editFieldRef} /> يجب أن يكون زر التعديل Edit في قالب العرض كما يلي: <button type="button" className="btn" onClick={() => setEditing(true)} ref={editButtonRef} > Edit <span className="visually-hidden">{props.name}</span> </button> التركيز على عناصر ref باستخدام الخطاف useEffect يمكننا استخدام عناصر ref للغرض المقصود منها عن طريق استيراد خطّاف React آخر هو useEffect()‎ الذي سُمِّي بهذا الاسم لأنه يعمل بعد أن تصيِّر React مكونًا معينًا، وسيشغّل هذا الخطّاف أيّ أمور إضافية نريد إضافتها إلى عملية التصيير، والتي لا يمكننا تشغيلها ضمن جسم الدالة الرئيسية، إذ يُعَدّ الخطّاف useEffect()‎ مفيدًا في الوضع الحالي لأننا لا نستطيع التركيز على عنصر إلا بعد تصيير المكوِّن <Todo /‎> ومعرفة React مكان عناصر ref، لذا عدِّل تعليمة الاستيراد import في الملف Todo.js مرةً أخرى لإضافة الخطّاف useEffect كما يلي: import React, { useEffect, useRef, useState } from "react"; يأخذ الخطّاف useEffect()‎ دالةً على أساس وسيط له، إذ تُنفَّذ هذه الدالة بعد تصيير المكوِّن، لذا ضع استدعاء الخطّاف useEffect()‎ التالي قبل تعليمة return مباشرةً في جسم الدالة Todo()‎، ومرّر إليه دالةً تسجّل العبارة "side effect" في الطرفية: useEffect(() => { console.log("side effect"); }); أضف تابع console.log()‎ آخر، لتوضيح الفرق بين عملية التصيير الرئيسية وتشغيل الشيفرة ضمن الخطّاف useEffect()‎، أي ضع التعليمة التالية بعد الإضافة السابقة: console.log("main render"); افتح الآن التطبيق في متصفحك، إذ يجب أن ترى كلتا الرسالتين في الطرفية مع تكرار كل منهما ثلاث مرات، ولاحظ ظهور عبارة التصيير الرئيسي main render أولًا، ثم عبارة "side effect" ثانيًا، على الرغم من وجود عبارة "side effect" أولًا في الشيفرة. main render (3) Todo.js:100 side effect (3) Todo.js:98 احذف تعليمة console.log("main render")‎ الآن، ولننتقل إلى تطبيق إدارة التركيز. التركيز على حقل التعديل تذكّر أننا نريد التركيز على حقل التعديل عندما ننتقل إلى قالب التعديل، لذا عدِّل الخطّاف useEffect()‎ ليصبح كما يلي: useEffect(() => { if (isEditing) { editFieldRef.current.focus(); } }, [isEditing]); إذا كانت قيمة isEditing هي true، فستقرأ React قيمة مرجع editFieldRef الحالية وتنقل تركيز المتصفح إليه، كما سنمرِّر مصفوفةً إلى الخطّاف useEffect()‎ على أساس وسيط ثان، إذ تُعَدّ هذه المصفوفة قائمةً من القيم التي يجب أن يعتمد عليها الخطّاف useEffect()‎، فإذا ضمّنا هذه القيم، فلن يُشغَّل الخطاف useEffect()‎ إلا عند تغيير إحدى هذه القيم، إذ نريد تغيير التركيز عندما تتغير قيمة isEditing فقط، لذا انتقل إلى متصفحك الآن، وسترى انتقال التركيز إلى العنصر <input> المقابل لزر التعديل Edit عند النقر عليه. نقل التركيز إلى زر التعديل مرة أخرى يمكن أن يبدو نقل React للتركيز مرةً أخرى إلى زر التعديل Edit عند حفظ التعديل أو إلغائه أمرًا سهلًا للوهلة الأولى، فلنجرّب إضافة شرط إلى الخطّاف useEffect للتركيز على زر التعديل إذا كانت قيمة الحالة isEditing هي false مثلًا، لذا عدِّل استدعاء الخطّاف useEffect()‎ كما يلي: useEffect(() => { if (isEditing) { editFieldRef.current.focus(); } else { editButtonRef.current.focus(); } }, [isEditing]); عُد إلى متصفحك وسترى انتقال تركيزك بين تعديل العنصر <input> وزر التعديل Edit مع بدء التعديل وإنهائه، ولكن يركِّز الزر Edit في المكوِّن <Todo /‎> الأخير مباشرةً على تحميل الصفحة قبل تفاعلنا مع التطبيق، وهنا يتصرّف الخطّاف useEffect()‎ كما صممناه تمامًا، فهو يعمل بمجرد تصيير المكوِّن، ويرى أن قيمة الحالة isEditing هي false، كما يركِّز على الزر Edit، وبما أنه يوجد ثلاث نسخ من المكوِّن <Todo /‎>، فإننا نرى التركيز على الزر Edit الأخير، إذ يجب إعادة التفكير ليتغير التركيز عندما تتغير قيمة الحالة isEditing فقط. إدارة التركيز الصارمة يجب معرفة قيمة الحالة isEditing ومتى تتغير هذه القيمة، لذا يجب أن نكون قادرين على قراءة قيمة isEditing السابقة، ويجب أن يكون منطق العمل كما يلي باستخدام الشيفرة الوهمية التالية: if (wasNotEditingBefore && isEditingNow) { focusOnEditField() } if (wasEditingBefore && isNotEditingNow) { focusOnEditButton() } ناقش فريق React طرقًا للحصول على حالة المكوِّن السابقة، وقدّم مثالًا عن الخطّاف المُخصَّص الذي يمكننا استخدامه لذلك. الصق الشيفرة التالية بالقرب من أعلى الملف Todo.js قبل الدالة Todo()‎: function usePrevious(value) { const ref = useRef(); useEffect(() => { ref.current = value; }); return ref.current; } سنعرِّف الآن الثابت wasEditing بعد الخطّافات في الجزء العلوي من الدالة Todo()‎، إذ نريد أن يتتبع هذا الثابت قيمة isEditing السابقة، لذلك سنستدعي الدالة usePrevious مع isEditing على أساس وسيط كما يلي: const wasEditing = usePrevious(isEditing); يمكننا باستخدام هذا الثابت تحديث الخطّاف useEffect()‎ لتطبيق الشفرة الوهمية التي ناقشناها سابقًاـ لذا عدِّل هذه الشيفرة لتصبح كما يلي: useEffect(() => { if (!wasEditing && isEditing) { editFieldRef.current.focus(); } if (wasEditing && !isEditing) { editButtonRef.current.focus(); } }, [wasEditing, isEditing]); لاحظ اعتماد منطق useEffect()‎ الآن على الثابت wasEditing، لذلك سنقدِّمه ضمن مصفوفة الاعتماديات، وحاول مرةً أخرى استخدام زرَي التعديل Edit والإلغاء Cancel للتبديل بين قوالب المكوِّن <Todo /‎>، إذ سترى مؤشر تركيز المتصفح يتحرك بطريقة مناسبة دون ظهور المشكلة التي ناقشناها سابقًا. التركيز عندما يحذف المستخدم مهمة هناك مشكلة واحدة أخيرة في تجربة لوحة المفاتيح، وهي اختفاء التركيز عندما يحذف المستخدِم مهمةً من القائمة، لذا سنتبع نمطًا مشابهًا للتعديلات السابقة، إذ سننشئ مرجعًا جديدًا، وسنستخدِم الخطّاف usePrevious()‎، لنتمكّن من التركيز على عنوان القائمة عندما يحذف المستخدِم مهمةً. يكون المكان الذي نرغب في إرسال تركيزنا إليه واضحًا في بعض الأحيان، إذ كانت لدينا نقطة أصل للرجوع إليها وهي زر التعديل Edit عند التبديل بين قوالب <Todo /‎>، لكن لا يوجد مكان للرجوع إليه في حالتنا، لأننا نزيل العناصر تمامًا من نموذج DOM، فعنوان القائمة هو أفضل خيار لأنه قريب من عنصر القائمة الذي سيحذفه المستخدِم، كما سيخبر التركيز عليه المستخدِم بعدد المهام المتبقية. إنشاء المرجع استورد الخطّافَين useRef()‎ وuseEffect()‎ إلى الملف App.js كما يلي: import React, { useState, useRef, useEffect } from "react"; صرّح بعد ذلك عن مرجع جديد ضمن الدالة App()‎ قبل تعليمة return مباشرةً كما يلي: const listHeadingRef = useRef(null); تحضير العنوان لا تكون عناصر العنوان مثل <h2> قابلةً للتركيز عادةً، ولكن لا يُعَدّ ذلك مشكلة، إذ يمكننا جعل أيّ عنصر قابلًا للتركيز برمجيًا عن طريق إضافة السمة tabindex="-1"‎ إليه، أي يمكن التركيز عليه باستخدام لغة جافاسكربت فقط، كما لا يمكنك الضغط على مفتاح Tab للتركيز على عنصر له السمة tabindex="-1"‎ باستخدام الطريقة نفسها التي يمكنك اتباعها مع عناصر <button> أو <a>، إذ يمكن تطبيق ذلك باستخدام السمة tabindex="0"‎، ولكنه لا يُعَدّ مناسبًا في هذه الحالة. لنضِف السمة tabindex المكتوبة بالصورة tabIndex في صيغة JSX إلى العنوان الموجود أعلى قائمة المهام مع المرجع headingRef كما يلي: <h2 id="list-heading" tabIndex="-1" ref={listHeadingRef}> {headingText} </h2> ملاحظة: تُعَدّ السمة tabindex رائعةً لحالات إمكانية الوصول الطارئة، ولكن يجب عليك الحرص على عدم الإفراط في استخدامها، إذ يمكنك تطبيقها على عنصر فقط عندما تكون متأكدًا تمامًا من أنّ جعله قابلًا للتركيز سيفيد المستخدِم بطريقة ما، كما يجب عليك استخدام العناصر التي يمكن التركيز عليها طبيعيًا مثل الأزرار وعناصر الروابط وحقول الإدخال، في حين يمكن أن يكون لاستخدام السمة tabindex غير المسؤول تأثيرًا سلبيًا عميقًا على مستخدِمي لوحة المفاتيح وقارئات الشاشة. الحصول على الحالة السابقة نريد التركيز على العنصر المرتبط بالمرجع باستخدام السمة ref عندما يحذف المستخدِم مهمةً من القائمة فقط، إذ سيتطلب ذلك الخطّاف usePrevious()‎ الذي استخدمناه سابقًا، لذا أضف هذا الخطّاف في أعلى الملف App.js بعد عبارات الاستيراد مباشرةً كما يلي: function usePrevious(value) { const ref = useRef(); useEffect(() => { ref.current = value; }); return ref.current; } أضف الآن ما يلي قبل تعليمة return ضمن الدالة App()‎: const prevTaskLength = usePrevious(tasks.length); استدعينا الخطّاف usePrevious()‎ لتتبع طول حالة المهام. ملاحظة: بما أننا نستخدِم الآن الخطّاف usePrevious()‎ في ملفين، فستتمثّل إعادة البناء الفعالة في نقل الدالة usePrevious()‎ إلى ملفها، وتصديرها من هذا الملف، واستيرادها حيث تريدها. استخدام الخطاف useEffect()‎ للتحكم في التركيز الرئيسي يمكننا الآن إعداد الخطّاف useEffect()‎ لتشغيله عندما يتغير عدد المهام، والذي سيركِّز على العنوان إذا كان عدد المهام أقل مما كان عليه سابقًا، أي أننا حذفنا مهمةً. أضف ما يلي إلى جسم الدالة App()‎ بعد الإضافات السابقة مباشرةً: useEffect(() => { if (tasks.length - prevTaskLength === -1) { listHeadingRef.current.focus(); } }, [tasks.length, prevTaskLength]); نحاول التركيز فقط على عنوان القائمة إذا كانت لدينا مهام أقل مما كانت عليه سابقًا، إذ تضمن الاعتماديات Dependencies االمُمرَّرة في هذا الخطّاف محاولة إعادة التشغيل فقط عندما تتغير أيّ من هذه القيم، أي عدد المهام الحالية أو عدد المهام السابقة، وسترى الآن ظهور مخطط التركيز المنقط حول العنوان أعلى القائمة عندما تحذف مهمةً في متصفحك. الخلاصة انتهينا من إنشاء تطبيق React من الألف إلى الياء، وستكون المهارات التي تعلمتها أساسًا للبناء عليها لتواصل العمل مع React، كما يمكنك أن تكون مساهمًا فعالًا في مشروع React حتى إذا كان كل ما تفعله هو التفكير مليًا في المكونات وحالتها وخاصياتها، وتذكّر أن تكتب دائمًا أفضل شيفرة HTML ممكنة. يُعَدّ الخطّافان useRef()‎ وuseEffect()‎ ميزات متقدمةً إلى حد ما، ويجب أن تفخر بنفسك لاستخدامها، وابحث عن فرص لممارستها أكثر، مما سيسمح لك بإنشاء تجارب شاملة للمستخدِمين، إذ يمكن تعذُّر الوصول إلى تطبيقنا لمستخدِمي لوحة المفاتيح بدونهما. ملاحظة: إذا كنت بحاجة إلى التحقق من شيفرتك مقابل نسختنا من الشيفرة، فيمكنك العثور على نسخة نهائية من نموذج شيفرة تطبيق React في المستودع todo-reaction. كما يمكنك الحصول على إصدار مباشر قيد التشغيل من خلال مراجعة todo-react-build. سنقدِّم في المقال التالي قائمةً بموارد React التي يمكنك استخدامها للمضي قدمًا في مسار تعلّمك. ترجمة -وبتصرُّف- للمقال Accessibility in React. اقرأ أيضًا تنفيذ التفاعل في تطبيق React: التعديل والترشيح والتصيير الشرطي إنشاء تطبيق قائمة مهام باستخدام React تقسيم تطبيق React إلى مكونات أساسيات بناء تطبيقات الويب
  11. لابد من نشر الموقع على شبكة الإنترنت حالما تنتهي من كتابة الشيفرة وتنظيم الملفات التي تكوّنه ليتمكن الجميع من الوصول إليه، إذ سنشرح في هذا المقال كيف تنشر موقعك التجريبي البسيط على الإنترنت بأقل جهد ممكن. ماهي الخيارات المتاحة يُعَدّ نشر موقع ويب موضوعًا معقّدًا لوجود طرق وأساليب كثيرة لتنفيذه، ولن نحاول في هذا المقال توثيق كل الطرق الممكنة، وإنما سنصف إيجابيات وسلبيات ثلاثة نهج مناسبة للمبتدئين، ثم نفصّل إحداها والتي تُعَدّ الأفضل للكثير من القراء. الحصول على استضافة واسم نطاق يفضِّل الكثيرون الدفع مقابل الحصول على استضافة واسم نطاق وذلك لتحكّم أفضل في محتوى ومظهر موقع الويب: الاستضافة Hosting: هي مساحة تخزين مُستأجَرة على خادم ويب تابع لشركة الاستضافة، إذ توضَع ملفات الموقع على هذا الخادم الذي يتيح محتواه للزائرين. اسم النطاق Domain name: هو العنوان الفريد الذي ستجد عليه موقع ويب محدَّد مثل ‎/hsoub أو ‎/google، ويمكنك استئجار اسم النطاق لعام أو أكثر من شركة مُسجّلة Domain Registrar. تستخدِم معظم مواقع ويب الاحترافية الطريقة السابقة لتنشر محتواها على شبكة الإنترنت، وبالإضافة إلى ذلك، سيتطلب الأمر برنامجًا يستخدِم بروتوكول نقل الملفات FTP لنقل ملفاتك إلى الخادم، ويمكنك الاطلاع على مقال أساسيات تحديد الكلفة المادية الكاملة لبناء موقع ويب، كما تتنوع برامج FTP كثيرًا، لكن وظيفتها بالمجمل هي استخدام المعلومات التي تزودك بها شركة الاستضافة وهي اسم المستخدِم وكلمة المرور واسم المضيف عادةً، وذلك لتأمين الاتصال مع خادم الاستضافة ونقل الملفات إليه، ثم يُظهِر البرنامج بعد الاتصال ملفاتك المحلية وملفات خادم الويب في نافذتين متجاورتين ليسهل نقل الملفات بين المكانين. نصائح لإيجاد استضافة ونطاق لا نرشح في هذا المقال أيّ شركة استضافة أو شركة مسجِّلة، فعليك البحث بنفسك عما يناسبك، إذ تتيح لك كل المسجلات وسيلةً للتحقق من توفر اسم نطاق معين وحجزه. تمنحك بعض مزودات الخدمة الموجودة على حاسوبك الشخصي أو ضمن شبكة مكتبك إمكانية استضافة محدودة لموقعك، إذ لن تكون الميزات المتاحة كثيرةً، لكنها ممتازة لتجربته الموقع. ستجد أيضًا خدمات استضافة مجانية مثل Neocities و Google sites و Blogger و Wordpress، وقد تجني فائدة ما تدفع، لكن موارد مجانية مثل هذه قد تكوِّن خيارًا تجريبيًا ممتازًا أيضًا. تزوّدك بعض الشركات بخدمتي الاستضافة وحجز أسماء نطاقات معًا. استخدام أدوات على شبكة الإنترنت مثل جيت-هاب و جوجل آب تساعدك بعض الأدوات في نشر موقعك مثل: جيت-هاب GitHub: وهو موقع كتابة شيفرة تشاركي يسمح لك برفع ملفات الشيفرة إلى مستودعات تخزين على منظومة التحكم بالإصدار Git، إذ تستطيع بعد ذلك مشاركة شيفرة المشاريع المختلفة والتعاون في العمل عليها، وطالما أنّ المنظومة مفتوحة المصدر، فيمكن لأيّ كان الوصول إلى الشيفرة على جيت-هاب واستخدامها والتعلم منها وتطويرها، كما تقدِّم جيت-هاب أيضًا ميزةً مفيدةً هي صفحات جيت-هاب GitHub Pages التي تسمح لك باستضافة شيفرة موقعك على ويب. محرّك جوجل آب Google App Engine: وهي منصة قوية تسمح لك ببناء وتشغيل التطبيقات بالاستفادة من البنية التحتية لجوجل، سواءً أردت بناء تطبيق ويب من الصفر أو استضافة موقع ويب ساكن، ولمزيد من المعلومات راجع مقال رفع موقع ويب إلى شبكة الإنترنت. هناك عدة خيارات أخرى مجانية، لكنك قد تتخطى بسهولة حدود الميزات المتاحة. استخدام بيئة عمل متكاملة مبنية على ويب هناك العديد من تطبيقات الويب التي تحاكي بيئة تطوير مواقع ويب وتسمح لك بإدخال شيفرة HTML و CSS وجافاسكربت واستعراض نتيجة تنفيذ الشيفرة على أساس موقع ويب حقيقي داخل نافذة المتصفح، وتُعَدّ هذه الأدوات عمومًا سهلة الاستخدام نسبيًا وممتازةً لأغراض التعلّم ومشاركة الشيفرة، إذ بإمكانك إذا أردت مشاركة أسلوب برمجي أو طلب مساعدة في تنقيح شيفرة من زميل في مكتب مجاور، كما أنها مجانية لبعض الميزات الأساسية، لكنها لا تزوّدك عادةً بمساحة لتخزين ملفات الدعم أو ملفات المساعدة والتي تدعى بالأصول assets مثل الصور، وحاول أن تجرب بعضها مثل JSFiddle و Glitch و JS Bin و CodePen g لتجد ما يناسبك. النشر باستخدام جيت-هاب سنناقش الآن كيف تنشر موقعك على صفحات جيت-هاب بسهولة: سجّل في موقع جيت-هاب أولًا وأكِّد امتلاكك عنوان البريد الإلكتروني الذي استخدمته في التسجيل. أنشئ مستودعًا لتخزين الملفات. أدخِل في الصندوق Repository name الظاهر في الصفحة الموضحة في الشكل التالي العبارة username.github.io، إذ يمثل username اسم المستخدم، فقد يُدخِل "مازن" الاسم "mazen.github.io" مثلًا، ثم فعِّل بعد ذلك الخيار Initialize this repository with a README وانقر بعدها زر أنشئ مستودعًا create repository. اسحب وأفلت محتوى الموقع ضمن المستودع واحفظ التغييرات. انتقل باستخدام متصفحك إلى الموقع "username.github.io -وفقًا لما اخترته-، وسيعرض محتوى موقعك. ترجمة -وبتصرف- للمقال Publishing your website. اقرأ أيضًا مدخل إلى خادم ويب. مدخل إلى أسماء النطاقات على شبكة الإنترنت. أساسيات تحديد الكلفة المادية الكاملة لبناء موقع ويب
  12. سنضيف في هذا المقال اللمسات الأخيرة على وظائف تطبيق قائمة المهام Todo List الرئيسية -الذي بنيناه في المقالات السابقة- من خلال السماح بتعديل المهام الحالية، وترشيح Filtering قائمة المهام جميعها والمهام المكتملة وغير المكتملة فقط، كما سنتعرّف على التصيير الشرطي Conditional Rendering لواجهة المستخدِم UI. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript ومعرفة استخدام سطر الأوامر أو الطرفية. الهدف: التعرف على التصيير الشرطي في React وتطبيق ترشيح القائمة وتعديل واجهة مستخدِم في تطبيقنا. تعديل اسم المهمة لا توجد لدينا واجهة مستخدِم لتعديل اسم المهمة حتى الآن، ولكن يمكننا على الأقل تنفيذ الدالة editTask()‎ في الملف App.js حاليًا، والتي تشبه الدالة deleteTask()‎ لأنها ستأخذ معرِّفًا id للعثور على الكائن الهدف، وستأخذ الخاصية newName التي تحتوي على الاسم الذي سنعدّل اسم المهمة إليه، في حين أننا سنستخدِم التابع Array.prototype.map()‎ بدلًا من التابع Array.prototype.filter()‎ لأننا نريد إعادة مصفوفة جديدة مع بعض التعديلات بدلًا من حذف شيء منها، لذا أضف الدالة editTask()‎ ضمن المكوِّن App مع الدوال الأخرى كما يلي: function editTask(id, newName) { const editedTaskList = tasks.map(task => { // إذا كانت هذه المهمة لها معرّف المهمة المعدّلة نفسه if (id === task.id) { // return {...task, name: newName} } return task; }); setTasks(editedTaskList); } مرِّر editTask إلى مكونات <Todo /‎> بوصفها خاصيةً Prop بالطريقة نفسها التي مررنا بها الخاصية deleteTask كما يلي: const taskList = tasks.map(task => ( <Todo id={task.id} name={task.name} completed={task.completed} key={task.id} toggleTaskCompleted={toggleTaskCompleted} deleteTask={deleteTask} editTask={editTask} /> )); افتح الآن الملف Todo.js، إذ سنُجري إعادة بناء. واجهة مستخدم التعديل يجب توفير واجهة مستخدِم للمستخدِمين للسماح لهم بتعديل مهمة، لذا استورد أولًا الخطّاف useState إلى المكوِّن Todo كما فعلنا سابقًا مع المكوِّن App عن طريق تعديل تعليمة الاستيراد الأولى إلى ما يلي: import React, { useState } from "react"; سنضبط الآن الحالة isEditing التي يجب أن تكون قيمتها الافتراضية false، لذا أضف السطر التالي في الجزء العلوي من تعريف المكون Todo(props) { … }‎: const [isEditing, setEditing] = useState(false); كما يجب إعادة التفكير في المكوِّن <Todo /‎> من الآن فصاعدًا، إذ نريد منه عرض أحد القالبَين التاليين بدلًا من القالب الوحيد المستخدَم حتى الآن: قالب العرض View عند عرض المهام فقط، وهو ما استخدمناه حتى الآن. قالب التعديل Editing عند تعديل المهام، وسننشئه بعد قليل. انسخ الشيفرة التالية في الدالة Todo()‎ بعد الخطّاف useState()‎ وقبل التعليمة return: const editingTemplate = ( <form className="stack-small"> <div className="form-group"> <label className="todo-label" htmlFor={props.id}> New name for {props.name} </label> <input id={props.id} className="todo-text" type="text" /> </div> <div className="btn-group"> <button type="button" className="btn todo-cancel"> Cancel <span className="visually-hidden">renaming {props.name}</span> </button> <button type="submit" className="btn btn__primary todo-edit"> Save <span className="visually-hidden">new name for {props.name}</span> </button> </div> </form> ); const viewTemplate = ( <div className="stack-small"> <div className="c-cb"> <input id={props.id} type="checkbox" defaultChecked={props.completed} onChange={() => props.toggleTaskCompleted(props.id)} /> <label className="todo-label" htmlFor={props.id}> {props.name} </label> </div> <div className="btn-group"> <button type="button" className="btn"> Edit <span className="visually-hidden">{props.name}</span> </button> <button type="button" className="btn btn__danger" onClick={() => props.deleteTask(props.id)} > Delete <span className="visually-hidden">{props.name}</span> </button> </div> </div> ); أصبح لدينا الآن بنيتا قوالب مختلفتين -"Edit" و"View"- معرّفتان ضمن ثابتين منفصلين، وهذا يعني أنّ التعليمة return الخاصة بالمكوِّن <Todo /‎> مكررة، فهي تحتوي على تعريف قالب العرض View أيضًا، ويمكن تنظيف هذا التكرار باستخدام التصيير الشرطي Conditional Rendering لتحديد القالب الذي يعيده المكوِّن، وبالتالي يُصيَّر في واجهة المستخدِم. التصيير الشرطي يمكننا في صيغة JSX استخدام شرط لتغيير ما يصيّره المتصفح، إذ يُكتَب الشرط في صيغة JSX باستخدام معامِل ثلاثي Ternary Operator، فالشرط في حالة المكوِّن <Todo /‎> هو "هل تُعدَّل هذه المهمة؟"، لذا عدِّل التعليمة return ضمن الدالة Todo()‎، بحيث تصبح كما يلي: return <li className="todo">{isEditing ? editingTemplate : viewTemplate}</li>; يجب أن يصيّر متصفحك جميع مهامك كما كانت سابقًا، ويمكن مشاهدة قالب التعديل من خلال تغيير القيمة الافتراضية للحالة isEditing من false إلى true في شيفرتك حاليًا، إذ سنجعل زر التعديل Edit يبدِّل هذه القيمة لاحقًا. التبديل بين قوالب سنجعل الآن ميزة التعديل تفاعليةً، إذ يجب أولًا استدعاء الدالة setEditing()‎ مع القيمة true عندما يضغط المستخدِم على زر التعديل Edit في قالب العرض viewTemplate لنتمكّن من التبديل بين القوالب، لذا عدِّل زر التعديل Edit في قالب العرض viewTemplate كما يلي: <button type="button" className="btn" onClick={() => setEditing(true)}> Edit <span className="visually-hidden">{props.name}</span> </button> سنضيف الآن السمة onClick نفسها إلى زر الإلغاء Cancel في قالب التعديل editingTemplate، ولكن سنضبط الحالة isEditing هذه المرة على القيمة false لتعيدنا إلى قالب العرض، لذا عدِّل زر الإلغاء Cancel في قالب التعديل editingTemplate كما يلي: <button type="button" className="btn todo-cancel" onClick={() => setEditing(false)} > Cancel <span className="visually-hidden">renaming {props.name}</span> </button> يجب أن تكون قادرًا الآن على الضغط على زرَي التعديل Edit والإلغاء Cancel في عناصر المهام للتبديل بين القالبين. الخطوة التالية هي جعل وظيفة التعديل تعمل فعليًا. التعديل من واجهة المستخدم ما سنعمل عليه تاليًا مماثل لما عملنا عليه مع المكون Form (انظر المقال السابق تقسيم تطبيق React إلى مكونات). إذا كتب المستخدِم شيئًا في حقل الإدخال الجديد، فيجب تتبّع النص الذي يدخله، ويجب استخدام خاصية رد النداء Callback Prop بمجرد إرسال النموذج لتحديث الحالة باسم المهمة الجديد، إذ سنبدأ بإنشاء خطّاف Hook جديد لتخزين وضبط الاسم الجديد، لذا ضع ما يلي أسفل الخطّاف الموجود مسبقًا في الملف Todo.js: const [newName, setNewName] = useState(''); أنشئ بعد ذلك الدالة handleChange()‎ التي تضبط الاسم الجديد، لذا ضع ما يلي بعد الخطافات وقبل القوالب: function handleChange(e) { setNewName(e.target.value); } سنعدِّل الآن الحقل <input /‎> الخاص بقالب التعديل editingTemplate بضبط السمة value على newName وربط الدالة handleChange()‎ مع الحدث onChange كما يلي: <input id={props.id} className="todo-text" type="text" value={newName} onChange={handleChange} /> يجب أخيرًا إنشاء دالة تعالِج الحدث onSubmit الخاصة بنموذج التعديل، لذا أضف ما يلي مباشرةً بعد الدالة السابقة التي أضفتها: function handleSubmit(e) { e.preventDefault(); props.editTask(props.id, newName); setNewName(""); setEditing(false); } تذكّر أنّ خاصية رد النداء editTask()‎ تحتاج إلى معرِّف المهمة التي نعدّلها بالإضافة إلى اسمها الجديد. اربط الدالة handleSubmit()‎ مع حدث إرسال submit النموذج عبر إضافة معالج الحدث onSubmit التالي إلى عنصر النموذج <form> الخاص بقالب التعديل editingTemplate: <form className="stack-small" onSubmit={handleSubmit}> يجب الآن أن تكون قادرًا على تعديل مهمة في متصفحك. العودة إلى أزرار الترشيح اكتملت الآن ميزاتنا الرئيسية، وبالتالي يمكننا التفكير في أزرار الترشيح التي تكرّر العنوان "All" حاليًا بدون وظائف تطبّقها، إذ سنعيد تطبيق بعض المهارات التي استخدمناها في المكوِّن <Todo /‎> من أجل ما يلي: إنشاء خطّاف لتخزين المرشِّح Filter النشط. تصيير مصفوفة من عناصر <FilterButton /‎> التي تسمح للمستخدِمِين بتغيير المرشِّح النشط بين جميع المهام والمهام المكتملة والمهام غير المكتملة. إضافة خطاف ترشيح أضف خطّافًا جديدًا إلى الدالة App()‎ التي تقرأ وتضبط المرشِّح، إذ نريد أن يكون المرشّح الافتراضي هو All لأنه يجب عرض جميع المهام في البداية. const [filter, setFilter] = useState('All'); تعريف المرشحات هدفنا الآن هو: يجب أن يكون لكل مرشِّح اسم فريد. يجب أن يكون لكل مرشِّح سلوك فريد. يُعَدّ كائن JavaScript طريقةً رائعةً لربط الأسماء بالسلوكيات، فالمفتاح هو اسم المرشِّح، والخاصية هي السلوك المرتبط بهذا الاسم. أضِف كائنًا بالاسم FILTER_MAP في الجزء العلوي من الملف App.js بعد تعليمات الاستيراد وقبل الدالة App()‎ كما يلي: const FILTER_MAP = { All: () => true, Active: task => !task.completed, Completed: task => task.completed }; تُعَدّ قيم الكائن FILTER_MAP دوالًا سنستخدِمها لترشيح مصفوفة بيانات المهام tasks كما يلي: يعرِض المرشِّح All جميع المهام، لذلك يعيد القيمة true لجميع المهام. يعرِض المرشِّح Active المهام التي يكون فيها للخاصية completed القيمة false. يعرِض المرشِّح Completed المهام التي يكون فيها للخاصية completed القيمة true. أضف ما يلي بعد الذي أضفناه منذ قليل، إذ سنستخدِم التابع Object.keys()‎ لتجميع مصفوفة FILTER_NAMES: const FILTER_NAMES = Object.keys(FILTER_MAP); ملاحظة: نعرِّف هذه الثوابت خارج الدالة App()‎ لأنها إذا عُرِّفت ضمنها، فسيُعاد حسابها في كل مرة يعاد فيها تصيير المكوِّن <App /‎>، ولا نريد حصول ذلك، فلن تتغير هذه المعلومات أبدًا بغض النظر عمّا يفعله تطبيقنا. تصيير المرشحات أصبح لدينا الآن مصفوفة FILTER_NAMES، وبالتالي يمكننا استخدامها لتصيير المرشِّحات الثلاثة، إذ يمكننا إنشاء ثابت يسمى filterList ضمن الدالة App()‎، إذ سنستخدِم هذا الثابت لربط مصفوفة الأسماء وإعادة المكوِّن <FilterButton /‎>، وتذكّر أننا هنا بحاجة إلى مفاتيح أيضًا، لذا أضف ما يلي بعد التصريح عن الثابت taskList: const filterList = FILTER_NAMES.map(name => ( <FilterButton key={name} name={name}/> )); سنستبدل الآن الثابت filterList بالمكونات <FilterButton /‎> الثلاثة المكرَّرة في الملف App.js، أي بدلًا مما يلي: <FilterButton /> <FilterButton /> <FilterButton /> ضع التالي: {filterList} لن يعمل ذلك الآن، إذ لدينا المزيد لعمله أولًا. المرشحات التفاعلية يمكنك جعل أزرار المرشِّحات تفاعليةً من خلال تحديد الخاصيات التي تحتاج إلى استخدامها. نعلم أن المكوِّن <FilterButton /‎> يجب أن يبلّغ عمّا إذا كان مضغوطًا حاليًا، ويجب الضغط عليه إذا تطابق اسمه مع القيمة الحالية لحالة المرشِّح. نعلم أن المكوِّن <FilterButton /‎> يحتاج إلى دالة رد نداء لضبط المرشِّح النشط، كما يمكننا الاستفادة مباشرةً من الخطّاف setFilter. عدِّل الثابت filterList كما يلي: const filterList = FILTER_NAMES.map(name => ( <FilterButton key={name} name={name} isPressed={name === filter} setFilter={setFilter} /> )); كما يجب الآن تعديل الملف FilterButton.js لاستخدام الخاصيات التي قدمناها له بالطريقة نفسها التي طبّقناها سابقًا مع المكوِّن <Todo /‎>، لذا طبّق ما يلي وتذكَّر استخدام الأقواس المعقوصة لقراءة هذه المتغيرات: ضع all مكان الخاصية {props.name}. اضبط قيمة السمة aria-pressed على الخاصية {props.isPressed}. أضف السمة onClick التي تستدعي الخطّاف props.setFilter()‎ مع اسم المرشِّح. يجب أن تكون الآن الدالة FilterButton()‎ كما يلي: function FilterButton(props) { return ( <button type="button" className="btn toggle-btn" aria-pressed={props.isPressed} onClick={() => props.setFilter(props.name)} > <span className="visually-hidden">Show </span> <span>{props.name}</span> <span className="visually-hidden"> tasks</span> </button> ); } انتقل إلى متصفحك مرةً أخرى، إذ يجب أن ترى تسمية الأزرار المختلفة بأسمائها الخاصة، فإذا ضغطتَ على زر الترشيح Filter، فيجب أن ترى نَص الزر يأخذ تخطيطًا جديدًا، إذ يخبرك هذا بأنه مُحدَّد، فإذا ألقيت نظرةً على فاحص صفحة أدوات التطوير DevTool’s Page Inspector أثناء النقر على الأزرار، فسترى أنّ قيم السمة aria-pressed تتغير وفقًا لذلك. لكن لا تزال الأزرار لا ترشِّح المهام في واجهة المستخدِم. ترشيح المهام في واجهة المستخدم يربط الثابت taskList في الدالة App()‎ حاليًا حالة المهام ويعيد مكوِّن <Todo /‎> جديدًا لكل منها، ولكننا لا نريد ذلك، إذ يجب تصيير المهمة فقط إذا كانت مُضمَّنةً في نتائج تطبيق المرشِّح المحدَّد، وبالتالي يجب ترشيح حالة المهام باستخدام التابع Array.prototype.filter()‎ قبل ربطها لإزالة الكائنات التي لا نريد تصييرها، لذا عدِّل الثابت taskList كما يلي: const taskList = tasks .filter(FILTER_MAP[filter]) .map(task => ( <Todo id={task.id} name={task.name} completed={task.completed} key={task.id} toggleTaskCompleted={toggleTaskCompleted} deleteTask={deleteTask} editTask={editTask} /> )); يمكن الوصول إلى قيمة في المصفوفة FILTER_MAP التي تتوافق مع مفتاح حالة المرشِّح لتحديد دالة رد النداء التي يجب استخدامها في التابع Array.prototype.filter()‎، فإذا كان المرشِّح هو All مثلًا، فسيُقيَّم العنصر FILTER_MAP[filter]‎ على ‎() => true، كما يؤدي اختيار المرشِّح في متصفحك الآن إلى إزالة المهام التي لا تفي بمعاييره، في حين سيتغير العدد الموجود في العنوان أعلى القائمة ليمثِّل القائمة. الخلاصة اكتمل تطبيقنا الآن، ولكن يمكننا إجراء بعض التحسينات لضمان إمكانية استخدام مجموعة أكبر من المستخدِمين له، كما يتناول المقال التالي تضمين إدارة التركيز Focus Management في React التي يمكنها تحسين قابلية الاستخدام وتقليل الارتباك لكل من مستخدِمي لوحة المفاتيح فقط وقارئات الشاشة. ترجمة -وبتصرُّف- للمقال React interactivity: Editing, filtering, conditional rendering. اقرأ أيضًا تنفيذ التفاعل في تطبيق React: الأحداث والحالة إنشاء تطبيق قائمة مهام باستخدام React تقسيم تطبيق React إلى مكونات أساسيات بناء تطبيقات الويب
  13. حان الوقت الآن لتعديل تطبيقنا من واجهة مستخدِم ثابتة تمامًا إلى واجهة مستخدِم تسمح لنا بالتفاعل معها وتعديلها بعد وضع خطة للمكوّنات من خلال البحث عن الأحداث Events والحالة State، إذ ينتج في النهاية تطبيق يمكننا من خلاله إضافة المهام وحذفها بنجاح، ووضع علامة على المهام المكتملة. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript ومعرفة استخدام سطر الأوامر أو الطرفية. الهدف: التعرف على كيفية التعامل مع الأحداث والحالة في React، واستخدامها لإنشاء دراسة حالة تطبيق تفاعلي. معالجة الأحداث إذا استخدَمت لغة جافاسكربت الصرفة Vanilla JavaScript سابقًا، فلا بدّ أنك معتاد على وجود ملف جافاسكربت منفصل، إذ يمكنك الاستعلام عن بعض عُقد DOM وربط المستمعين بها كما يلي: const btn = document.querySelector('button'); btn.addEventListener('click', () => { alert("hi!"); }); يمكننا في React كتابة معالِجات الأحداث مباشرةً للعناصر الموجودة في JSX كما يلي: <button type="button" onClick={() => alert("hi!")} > Say hi! </button> يمكن أن يُعَدّ ذلك أمرًا غير مناسب لنصائح أفضل الممارسات التي تميل إلى عدم استخدام معالِجات الأحداث المُضمَّنة في HTML، ولكن تذكر أنّ صيغة JSX هي جزء من لغة جافاسكربت. أضفنا في المثال السابق السمة onClick إلى عنصر الزر <button>، وقيمة هذه السمة هي دالة تشغّل تنبيهًا بسيطًا، إذ تملك السمة onClick معنًى خاصًا هنا، فهي تخبر React بتشغيل دالة معيّنة عندما ينقر المستخدِم على الزر، كما يجب ملاحظة بعض الأشياء الأخرى، وهي: تُعَدّ طبيعة اسم السمة onClick ذات حالة الجَمل Camel-cased مهمةً، إذ لن تتعرف صيغة JSX على السمة onclick، لأن هذه الكلمة محجوزة في لغة جافاسكربت، وتُستخدَم لغرض مختلف يمثل خاصيات معالِج الحدث onclick المعيارية. تتبع جميع أحداث المتصفح هذا التنسيق في صيغة JSX باستخدام الجزء on متبوعًا باسم الحدث. لنطبّق هذه الملاحظات على تطبيقنا بدءًا من المكوِّن Form.js. معالجة إرسال النموذج أنشئ دالةً بالاسم handleSubmit()‎ في الجزء العلوي من دالة المكوِّن Form()‎، إذ يجب على هذه الدالة منع سلوك الحدث submit الافتراضي، ثم يجب إطلاق تنبيه alert()‎ بالذي تريده كما يلي: function handleSubmit(e) { e.preventDefault(); alert('Hello, world!'); } يمكنك استخدام هذه الدالة من خلال إضافة السمة onSubmit إلى العنصر <form>، وضبط قيمتها على الدالة handleSubmit كما يلي: <form onSubmit={handleSubmit}> إذا عدت إلى متصفحك ونقرت على زر "الإضافة Add"، فسيعرض المتصفح مربع حوار تنبيه يحتوي على الرسالة "Hello, world!‎" أو أيّ شيء آخر اخترته. خاصيات رد النداء يندر اقتصار التفاعل على مكوِّن واحد فقط في تطبيقات React، إذ ستؤثِّر الأحداث التي تحدث في أحد المكوِّنات على أجزاء أخرى من التطبيق، فإذا كان لدينا القدرة على إنشاء مهام جديدة مثلًا، فستؤثِّر الأشياء التي تحدث في المكوِّن <Form /‎> على القائمة المُصيَّرة في المكوِّن <App /‎>. نريد أن تساعدنا الدالة handleSubmit()‎ في إنشاء مهمة جديدة، لذلك سنحتاج إلى طريقة لتمرير المعلومات من المكوِّن <Form /‎> إلى المكوِّن <App /‎>، فلا يمكننا تمرير البيانات من الابن إلى الأب بالطريقة نفسها التي نمرر بها البيانات من الأب إلى الابن باستخدام الخاصيات Props المعيارية، إذ يمكننا بدلًا من ذلك كتابة دالة في المكوِّن <App /‎> تتوقع بعض البيانات من نموذجنا بوصفها دخلًا لها، ثم تمرير هذه الدالة إلى المكوِّن <Form /‎> بوصفها خاصيةً، وتُسمَّى هذه الدالة التي تُعامَل على أنها خاصية بخاصية رد النداء Callback Prop، إذ يمكننا استدعاء خاصية رد النداء ضمن المكوِّن <Form /‎> لإرسال البيانات الصحيحة إلى المكوِّن <App /‎>. معالجة إرسال النموذج باستخدام خاصيات رد النداء أنشئ دالةً بالاسم addTask()‎ في الجزء العلوي من دالة المكوِّن App()‎، بحيث تحتوي هذه الدالة على معامِل واحد هو name: function addTask(name) { alert(name); } سنمرِّر بعد ذلك الدالة addTask()‎ إلى المكوِّن <Form /‎> بوصفها خاصيةً، إذ يمكن أن تحمل الخاصية أيّ اسم تريده، ولكن اختر اسمًا تفهمه لاحقًا مثل الاسم addTask الذي يتطابق مع اسم الدالة ومع ما ستفعله، وهنا يجب تعديل استدعاء المكوِّن <Form /‎> كما يلي: <Form addTask={addTask} /> أخيرًا، يمكنك استخدام هذه الخاصية ضمن الدالة handleSubmit()‎ في المكوِّن <Form /‎> كما يلي: function handleSubmit(e) { e.preventDefault(); props.addTask("Say hello!"); } سيؤدي النقر على زر "الإضافة Add" في متصفحك إلى إثبات عمل دالة رد النداء addTask()‎، لكن سيكون جيدًا أن نحصل على تنبيه لإظهار ما نكتبه في حقل الإدخال، وهذا ما سنفعله لاحقًا. ملاحظة: سمّينا خاصية رد النداء بالاسم addTask لتسهيل فهم ما ستفعله هذه الخاصية، ومن الاصطلاحات الشائعة الأخرى التي قد تصادفها في شيفرة React هي أن تُسبَق أسماء خاصيات رد النداء بالكلمة on متبوعةً باسم الحدث الذي سيؤدي إلى تشغيلها، إذ يمكننا مثلًا تسمية خاصية بالاسم onSubmit مع القيمة addTask. الحالة والخطاف useState استخدَمنا حتى الآن الخاصيات لتمرير البيانات عبر المكوّنات، ولكننا نحتاج إلى شيء آخر عند تعاملنا مع دخل المستخدِم وتحديثات البيانات. تأتي الخاصيات من أب المكوِّن، فلن يرث المكوِّن <Form /‎> مثلًا اسمًا جديدًا للمهمة، إذ يتواجد العنصر <input /‎> مباشرةً ضمن المكوِّن <Form /‎>، لذا سيكون هذا المكوِّن مسؤولًا مباشرةً عن إنشاء هذا الاسم الجديد، كما لا يمكننا الطلب من المكوِّن <Form /‎> إنشاء خاصياته تلقائيًا، ولكن يمكننا أن نطلب منه تتبع بعض بياناته، إذ تسمى هذه البيانات التي يمتلكها المكوِّن نفسه بالحالة State، والتي تُعَدّ أداةً قويةً أخرى من React، لأن المكوّنات لا تمتلك الحالة فحسب، وإنما يمكنها تحديثها لاحقًا، في حين لا يمكن تحديث الخاصيات التي يتلقاها المكوِّن، فهي للقراءة فقط. توفِّر React مجموعةً متنوعةً من الدوال الخاصة التي تسمح لنا بتوفير إمكانات جديدة للمكوّنات مثل الحالة، إذ تُسمَّى هذه الدوال بالخطّافات Hooks، والخطّاف useState -كما يوحي اسمه- هو بالضبط الذي نحتاجه لإعطاء مكوِّننا حالةً، وهنا يجب استيراد خطّاف React من الوحدة react لاستخدامه، لذا عدِّل السطر الأول في الملف Form.js ليصبح كما يلي: import React, { useState } from "react"; يسمح لنا ذلك باستيراد الدالة useState()‎، واستخدامها في أيّ مكان من هذا الملف، كما تنشئ هذه الدالة حالةً لمكوِّن، ويحدِّد معامِلها الوحيد القيمة الأولية لتلك الحالة، كما تُعيد هذه الدالة الحالةَ ودالةً يمكن استخدامها لتحديث الحالة لاحقًا، ولنجرب ذلك أولًا من خلال إنشاء الحالة name ودالةً لتحديث هذه الحالة، لذا اكتب السطر التالي قبل الدالة handleSubmit()‎ ضمن الدالة Form()‎: const [name, setName] = useState('Use hooks!'); يحدُث ما يلي في السطر السابق: ضبط قيمة الحالة name الأولية على القيمة "Use hooks!‎". تعريف دالة بالاسم setName()‎ وظيفتها تعديل الحالة name. تعيد الدالة useState()‎ الشيئين السابقين، لذا فإننا نستخدِم عملية هدم المصفوفات Array Destructuring لإسنادهما إلى متغيرَين منفصلين. حالة القراءة يمكنك رؤية الحالة name قيد التشغيل مباشرةً، لذا أضِف السمة value إلى العنصر input في النموذج، واضبط قيمتها لتكون name، إذ سيصيِّر متصفحك التعليمة "Use hooks!‎" في العنصر input. <input type="text" id="new-todo-input" className="input input__lg" name="text" autoComplete="off" value={name} /> عدِّل بعد ذلك التعليمة "Use hooks!‎" إلى سلسلة نصية فارغة، فهذا ما نريده لحالتنا الأولية كما يلي: const [name, setName] = useState(''); قراءة دخل المستخدم يجب التقاط دخل المستخدِم الذي يكتبه قبل تمكننا من تغيير قيمة الحالة nameمن خلال الاستماع إلى الحدث onChange، فلنكتب الدالة handleChange()‎، ونستمع إليها على الوسم <input /‎>. // قُرب أعلى المكوِّن ‫`Form` function handleChange(e) { console.log("Typing!"); } // بعد تعليمة‫ `return` <input type="text" id="new-todo-input" className="input input__lg" name="text" autoComplete="off" value={name} onChange={handleChange} /> لن تتغير قيمة الدخل أثناء الكتابة حاليًا، ولكن سيطبع متصفحك الكلمة "Typing!" على طرفية جافاسكربت (نافذة console)، وبالتالي سنعلم أنّ مستمع الحدث متصل بالدخل، كما يمكنك تغيير قيمة الدخل من خلال استخدام الدالة handleChange()‎ لتحديث الحالة name. يمكن قراءة محتويات حقل الإدخال عند تغييرها من خلال الوصول إلى الخاصية value الخاصة بالعنصر input عن طريق قراءة القيمة e.target.value ضمن الدالة handleChange()‎، إذ يمثِّل e.target العنصر الذي أَطلق الحدث change، وهو العنصر input، وبالتالي تكون الخاصية value هي النص الموجود ضمنها، كما يمكنك تنفيذ التابع console.log()‎ على هذه القيمة لرؤيتها في طرفية متصفحك كما يلي: function handleChange(e) { console.log(e.target.value); } حالة التحديث يجب تخزين حالة name المحدَّثة مع تغير قيمة الدخل، لذا غيِّر التابع console.log()‎ إلى setName()‎ كما يلي: function handleChange(e) { setName(e.target.value); } يجب الآن تعديل الدالة handleSubmit()‎ لتستدعي الخاصية props.addTask مع الحالة name على أساس وسيط، مما يؤدي إلى إرسال المهمة مرةً أخرى إلى المكوِّن App، لنتمكن من إضافتها إلى قائمة المهام لاحقًا، كما يجب مسح الدخل بعد إرسال النموذج، لذلك سنستدعي الدالة setName()‎ مرةً أخرى مع سلسلة نصية فارغة. function handleSubmit(e) { e.preventDefault(); props.addTask(name); setName(""); } أخيرًا، يمكنك كتابة شيء ما في حقل الإدخال في متصفحك والنقر فوق زر "Add"، وبالتالي سيظهر كل ما كتبته في مربع حوار التنبيه، والآن يجب أن يكون الملف Form.js كما يلي: import React, { useState } from "react"; function Form(props) { const [name, setName] = useState(""); function handleChange(e) { setName(e.target.value); } function handleSubmit(e) { e.preventDefault(); props.addTask(name); setName(""); } return ( <form onSubmit={handleSubmit}> <h2 className="label-wrapper"> <label htmlFor="new-todo-input" className="label__lg"> What needs to be done? </label> </h2> <input type="text" id="new-todo-input" className="input input__lg" name="text" autoComplete="off" value={name} onChange={handleChange} /> <button type="submit" className="btn btn__primary btn__lg"> Add </button> </form> ); } export default Form; ملاحظة: هناك شيء واحد ستلاحظه، وهو أنه يمكنك إرسال مهام فارغة عند الضغط على زر الإضافة Add دون إدخال اسم المهمة، لذلك يجب منع إضافة المهام الفارغة من خلال إضافة تحقق ما إلى الدالة handleSubmit()‎ على سبيل المثال. إضافة مهمة أصبحنا الآن جاهزين لكتابة دوال تسمح للمستخدِم بإضافة مهمة جديدة من متصفحه بعد أن تدربنا على الأحداث وخاصيات رد النداء والخطّافات. استخدام المهام بوصفها حالات استورِد الخطّاف useState إلى الملف App.js لتتمكن من تخزين المهام في حالة ما من خلال تعديل سطر استيراد React إلى ما يلي: import React, { useState } from "react"; نريد تمرير الخاصية props.tasks إلى الخطّاف useState()‎، إذ ستحتفظ هذه الخاصية بحالة الخطّاف الأولية، لذا أضف السطر التالي في الجزء العلوي من تعريف الدالة App()‎: const [tasks, setTasks] = useState(props.tasks); يمكننا الآن تغيير ربط قائمة المهام taskList لتكون نتيجةً لربط tasks بدلًا من props.tasks، إذ يجب أن يبدو تصريح الثابت taskList كما يلي: const taskList = tasks.map(task => ( <Todo id={task.id} name={task.name} completed={task.completed} key={task.id} /> ) ); إضافة مهمة لدينا الآن الخطّاف setTasks الذي يمكننا استخدامه في الدالة addTask()‎ لتحديث قائمة المهام، ولكن هناك مشكلة أنه لا يمكننا فقط تمرير الوسيط name الخاص بالدالة addTask()‎ إلى الخطاف setTasks، لأنّ tasks هي مصفوفة من الكائنات؛ أما الوسيط name، فهو سلسلة نصية، وبالتالي ستكون السلسلة النصية مكان المصفوفة. يجب أولًا وضع الوسيط name في كائن له بنية مهامنا الحالية نفسها، إذ سننشئ ضمن الدالة addTask()‎ الكائن newTask لإضافته إلى المصفوفة، ويجب بعد ذلك إنشاء مصفوفة جديدة مع إضافة هذه المهمة الجديدة إليها، ثم تحديث حالة بيانات المهام إلى هذه الحالة الجديدة من خلال استخدام صيغة الانتشار Spread Syntax لنسخ المصفوفة الحالية، وإضافة الكائن في النهاية، ثم تمرير هذه المصفوفة إلى الدالة setTasks()‎ لتحديث الحالة، وبالتالي يجب أن تصبح الدالة addTask()‎ كما يلي: function addTask(name) { const newTask = { id: "id", name: name, completed: false }; setTasks([...tasks, newTask]); } يمكنك الآن استخدام المتصفح لإضافة مهمة إلى بياناتنا، لذا اكتب أيّ شيء تريده في النموذج، وانقر زر الإضافة Add أو اضغط على مفتاح Enter من لوحة المفاتيح، إذ سترى عنصر المهام الجديد يظهَر في واجهة المستخدِم، لكن هناك مشكلة أخرى تتمثّل بإعطاء الدالة addTask()‎ المعرّف id نفسه لكل مهمَّة، إذ يُعَدّ ذلك أمرًا سيئًا لإمكانية الوصول، كما يجعل التمييز بين المهام المستقبلية أمرًا مستحيلًا على React باستخدام الخاصية key، إذ ستعطيك React تحذيرًا في طرفية أدوات التطوير DevTools مثل رسالة التحذير التالية: "Warning: Encountered two children with the same key…‎". يجب إصلاح هذه المشكلة، كما يُعَدّ إنشاء معرّفات فريدة مشكلةً صعبةً، وهي مشكلة كتَب لها مجتمع جافاسكربت بعض المكتبات المفيدة من أجلها، إذ سنستخدِم حاليًا المكتبة nanoid لأنها صغيرة الحجم وتعمل جيدًا، لذا تأكّد من أنك في المجلد الجذر لتطبيقك وشغّل الأمر التالي في طرفيتك: npm install nanoid وفي ملاحظة مهمة، إذا أردتَ استخدام مدير الحزم yarn، فيجب كتابة الأمر: yarn add nanoid. يمكننا الآن استيراد المكتبة nanoid في الجزء العلوي من الملف App.js لنتمكّن من استخدامها لإنشاء معرِّفات فريدة لمهامنا الجديدة، لذا ضمّن أولًا سطر الاستيراد التالي في أعلى الملف App.js: import { nanoid } from "nanoid"; لنحدّث الآن الدالة addTask()‎ بحيث يصبح كل معرِّف مهمة مؤلفًا من البادئة todo-‎ بالإضافة إلى سلسلة نصية فريدة تنشئها المكتبة nanoid، لذا عدِّل تصريح الثابت newTask إلى ما يلي: const newTask = { id: "todo-" + nanoid(), name: name, completed: false }; احفظ كل شيء، وجرب تطبيقك مرةً أخرى، إذ يمكنك الآن إضافة المهام دون تلقِّي هذا التحذير بشأن المعرِّفات المُكرَّرة. عد المهام يمكننا الآن إضافة مهام جديدة، ولكن هناك مشكلة تتمثَّل بقراءة العنوان ثلاث مهام متبقية، بغض النظر عن عدد المهام، إذ يمكن إصلاح ذلك عن طريق حساب طول قائمة المهام taskList وتغيير نص العنوان وفقًا لذلك، لذا أضف ما يلي ضمن تعريف الدالة App()‎ قبل تعليمة return: const headingText = `${taskList.length} tasks remaining`; هذا صحيح تقريبًا باستثناء أنه إذا احتوت قائمتنا على مهمة واحدة، فسيظل العنوان يستخدِم الكلمة "tasks"، ولذلك يجب أن نجعلها متغيرةً، لذا عدِّل الشيفرة التي أضفتها للتو كما يلي: const tasksNoun = taskList.length !== 1 ? 'tasks' : 'task'; const headingText = `${taskList.length} ${tasksNoun} remaining`; يمكنك الآن استبدال المتغير headingText بمحتوى نص عنوان القائمة، لذا عدِّل العنصر <h2> ليصبح كما يلي: <h2 id="list-heading">{headingText}</h2> إكمال مهمة لاحظ أنّ مربع الاختيار يُحدَّد ويُلغَى تحديده بطريقة مناسبة عند النقر عليه، كما تُعَدّ معرفة المتصفح لكيفية تذكّر مدخلات مربعات الاختيار المُحدَّدة أو المُلغَى تحديدها دون مساعدتنا ميزةً في لغة HTML، ولكن تخفي هذه الميزة مشكلةً، إذ لا يغيِّر تحديد مربع الاختيار أو عدم تحديده الحالة في تطبيق React، مما يعني عدم تزامن المتصفح مع التطبيق، لذلك يجب كتابة شيفرتنا لإعادة المتصفح متزامنًا مع التطبيق. إثبات الخطأ لنكتب الدالة toggleTaskCompleted()‎ في المكوِّن App()‎، إذ سيكون لهذه الدالة المعامِل id، لكننا لن نستخدِمها حاليًا، إذ سنسجِّل الآن المهمة الأولى في المصفوفة في الطرفية، وسنفحص ما يحدث عندما نحدِّدها أو نلغي تحديدها في متصفحنا، لذا أضف ما يلي قبل التصريح عن الثابت taskList مباشرةً: function toggleTaskCompleted(id) { console.log(tasks[0]) } سنضيف بعد ذلك الخاصية toggleTaskCompleted إلى خاصيات كل مكوّن من مكوّنات <Todo /‎> المُصيَّرة ضمن taskList كما يلي: const taskList = tasks.map(task => ( <Todo id={task.id} name={task.name} completed={task.completed} key={task.id} toggleTaskCompleted={toggleTaskCompleted} /> )); انتقل إلى المكوِّن Todo.js وأضف معالِج الحدث onChange إلى العنصر <input /‎> الذي يجب أن يستخدِم دالةً مجهولةً لاستدعاء الخاصية props.toggleTaskCompleted()‎ مع المعامِل props.id، إذ يجب أن يكون العنصر <input /‎> الآن كما يلي: <input id={props.id} type="checkbox" defaultChecked={props.completed} onChange={() => props.toggleTaskCompleted(props.id)} /> احفظ كل شيء وعُد إلى متصفحك ولاحظ تحديد المهمة الأولى Eat، ثم افتح طرفية جافاسكربت، ثم انقر على مربع الاختيار الموجود بجوار الخيار Eat، وبالتالي فإنّ مربع الاختيار هذا غير محدَّد كما توقّعنا، ولكن ستعطي طرفية جافاسكربت الخاصة بك شيئًا كما يلي: Object { id: "task-0", name: "Eat", completed: true } يُلغَى تحديد مربع الاختيار في المتصفح، لكن تخبرنا الطرفية بأن المهمَّة Eat لا تزال مكتملةً، وسنصلح ذلك لاحقًا. مزامنة المتصفح مع بياناتنا لنَعُد إلى الدالة toggleTaskCompleted()‎ في الملف App.js، إذ نريدها أن تغيِّر الخاصية completed للمهمَّة التي أُلغِي تحديدها فقط، وترك المهام الأخرى كما هي، لذلك سنطبّق التابع map()‎ على قائمة المهام وسنغيّر القائمة التي أكملناها فقط، لذا عدِّل الدالة toggleTaskCompleted()‎ إلى ما يلي: function toggleTaskCompleted(id) { const updatedTasks = tasks.map(task => { // إذا كان لهذه المهمة معرِّف المهمة المُعدَّلة نفسه if (id === task.id) { // استخدم انتشار الكائن لإنشاء كائن جديد // ‫عُدِّلت الخاصية `completed` الخاصة به return {...task, completed: !task.completed} } return task; }); setTasks(updatedTasks); } عرّفنا الثابت updatedTasks الذي يمر على عناصر المصفوفة tasks الأصلية، فإذا طابقت خاصية معرّفُ id المهمة المعرّفَ id المقدَّم للدالة، فسنستخدِم صيغة انتشار الكائن Object Spread Syntax لإنشاء كائن جديد، وسنبدّل إلى الخاصية checked لهذا الكائن قبل إعادته؛ أما إذا لم يتطابقا، فسنعيد الكائن الأصلي. نستدعي بعد ذلك الدالة setTasks()‎ مع هذه المصفوفة الجديدة لتحديث الحالة. حذف مهمة سيتَّبع حذف مهمة نمطًا مشابهًا لتبديل حالتها المكتملة، إذ يجب تعريف دالة لتحديث الحالة، ثم تمرير هذه الدالة إلى المكوِّن <Todo /‎> بوصفها خاصيةً، واستدعاؤها عند حدوث الحدث الصحيح. خاصية رد النداء deleteTask سنكتب الدالة deleteTask()‎ في المكوِّن App، إذ ستأخذ هذه الدالة المعامِل id مثل الدالة toggleTaskCompleted()‎، وسنسجّل هذا المعرِّف في الطرفية، لذا أضف ما يلي بعد الدالة toggleTaskCompleted()‎: function deleteTask(id) { console.log(id) } أضف بعد ذلك خاصية رد نداء أخرى إلى مصفوفة مكوّنات <Todo /‎> كما يلي: const taskList = tasks.map(task => ( <Todo id={task.id} name={task.name} completed={task.completed} key={task.id} toggleTaskCompleted={toggleTaskCompleted} deleteTask={deleteTask} /> )); يجب استدعاء الدالة props.deleteTask()‎ في الملف Todo.js عند الضغط على زر الحذف Delete، كما تحتاج الدالة deleteTask()‎ إلى معرفة معرِّف المهمَّة التي ستستدعيها لتتمكّن من حذف المهمَّة الصحيحة من الحالة، لذا عدِّل زر الحذف Delete ضمن الملف Todo.js كما يلي: <button type="button" className="btn btn__danger" onClick={() => props.deleteTask(props.id)} > Delete <span className="visually-hidden">{props.name}</span> </button> إذا نقرتَ الآن على أيّ من أزرار الحذف Delete في التطبيق، فيجب أن تسجِّل طرفية المتصفح معرّف المهمَّة المرتبطة به. حذف المهام من الحالة وواجهة المستخدم يمكننا الآن استدعاء الخطّاف setTasks()‎ في الدالة deleteTask()‎ استدعاءً صحيحًا لحذف هذه المهمة فعليًا من حالة التطبيق وحذفها مرئيًا من واجهة مستخدِم التطبيق، وبما أنّ الخطّاف setTasks()‎ يتوقع مصفوفةً بوصفها وسيطًا له، فيجب تزويده بمصفوفة جديدة تنسخ المهام الحالية باستثناء المهمة التي يتطابق معرِّفها مع معرِّف المهمَّة المُمرَّرة إلى الدالة deleteTask()‎. يمكننا الآن استخدام التابع Array.prototype.filter()‎، إذ يمكننا اختبار كل مهمة، واستبعاد مهمة من المصفوفة الجديدة إذا تطابقت خاصيتها id مع المعامِل id المُمرَّر إلى الدالة deleteTask()‎، لذا عدِّل الدالة deleteTask()‎ ضمن الملف App.js كما يلي: function deleteTask(id) { const remainingTasks = tasks.filter(task => id !== task.id); setTasks(remainingTasks); } جرِّب تطبيقك مرةً أخرى، إذ يجب أن تكون قادرًا على حذف مهمَّة من تطبيقك. الخلاصة قدّمنا في هذا المقال معلومات حول كيفية تعامل React مع الأحداث والحالة، وتنفيذ وظائف إضافة المهام وحذفها ووضع علامة على المهام المكتملة، كما سنطبّق في المقال التالي وظيفة تعديل المهام الحالية وترشيح قائمة المهام جميعها والمهام المكتملة وغير المكتملة فقط، كما سنطّلع على التصيير الشرطي لواجهة المستخدِم UI. ترجمة -وبتصرُّف- للمقال React interactivity: Events and state. اقرأ أيضًا إنشاء تطبيق قائمة مهام باستخدام React تقسيم تطبيق React إلى مكونات أساسيات بناء تطبيقات الويب
  14. يُعَدّ تطبيقنا الذي عملنا عليه في المقال السابق وحدةً متراصةً، لذلك يجب تقسيمه إلى مكوّنات يمكن وصفها وإدارتها قبل تمكننا من جعل تطبيقنا يفعل شيئًا ما، إذ لا تحتوي مكتبة React على قواعد صارمة لتحديد ما يُعَدّ مكوّنًا Component، فالأمر متروك لك، وسنعرض في هذا المقال طريقةً معقولةً لتقسيم تطبيقنا إلى مكونات. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript ومعرفة استخدام سطر الأوامر أو الطرفية. الهدف: إظهار طريقة لتقسيم تطبيق قائمة المهام إلى مكوّنات. تحديد المكون الأول قد يبدو تحديد أحد المكوّنات أمرًا صعبًا إلى حين حصولك على بعض الخبرة العملية، ولكن الأمور المهمة هي: إذا كان أحد الأشياء جزءًا واضحًا من تطبيقك، فيُحتمَل أن يكون مكوّنًا. إذا أُعيد استخدام أحد الأشياء كثيرًا، فيُحتمَل أن يكون مكوّنًا. تُعَدّ النقطة الثانية ذات قيمة خاصة، إذ يتيح إنشاء مكوّن من عناصر واجهة المستخدِم تعديلَ شيفرتك البرمجية في مكان واحد ورؤية تلك التعديلات في جميع الأماكن التي يُستخدَم فيها هذا المكوّن، إذ ليس عليك تقسيم كل شيء إلى مكوِنات مباشرةً، ولنستخدِم النقطة الثانية لإنشاء مكوّن من أكثر الأجزاء المُعاد استخدامها والأكثر أهميةً في واجهة المستخدِم وهو عنصر قائمة المهام. إنشاء المكون <Todo /‎> يجب علينا إنشاء ملف جديد للمكوّن قبل إنشائه، ويجب إنشاء مجلد لهذه المكونات، إذ تنشئ الأوامر التالية المجلد components وملفًا ضمنه يُسمَّى Todo.js، ولكن تأكّد من وجودك في جذر تطبيقك قبل تشغيل هذه الأوامر: mkdir src/components touch src/components/Todo.js إنّ ملف Todo.js الجديد فارغ حاليًا، لذا افتحه واكتب فيه السطر الأول التالي: import React from "react"; سننشئ مكوّنًا يسمّى Todo، لذلك يمكننا إضافة شيفرتنا إلى الملف Todo.js على النحو التالي، إذ سنعرِّف الدالة Todo()‎ ونصدّرها على السطر نفسه كما يلي: export default function Todo() { return ( ); } كل شيء جيد حتى الآن، لكن يجب أن يعيد المكون شيئًا ما، لذا ارجع إلى الملف src/App.js، وانسخ أول عنصر <li> من القائمة غير المرتبة، والصقه في الملف Todo.js بحيث يصبح كما يلي: export default function Todo() { return ( <li className="todo stack-small"> <div className="c-cb"> <input id="todo-0" type="checkbox" defaultChecked={true} /> <label className="todo-label" htmlFor="todo-0"> Eat </label> </div> <div className="btn-group"> <button type="button" className="btn"> Edit <span className="visually-hidden">Eat</span> </button> <button type="button" className="btn btn__danger"> Delete <span className="visually-hidden">Eat</span> </button> </div> </li> ); } ملاحظة: يجب أن تعيد المكونات شيئًا ما دائمًا، فإذا حاولت لاحقًا تصيير Render مكون لا يعيد شيئًا، فستعرِض React خطأً في متصفحك. أصبح المكوّن Todo مكتملًا حاليًا، وبالتالي يمكننا استخدامه، والآن أضف السطر التالي في الملف App.js بالقرب من أعلى الملف لاستيراد المكوّن Todo: import Todo from "./components/Todo"; يمكنك مع استيراد هذا المكون وضع استدعاءات المكوّن <Todo /‎> مكان جميع عناصر <li> في الملف App.js، ويجب أن يصبح العنصر <ul> كما يلي: <ul role="list" className="todo-list stack-large stack-exception" aria-labelledby="list-heading" > <Todo /> <Todo /> <Todo /> </ul> إذا نظرت إلى متصفحك، فستلاحظ شيئًا مؤسفًا، إذ تُكرِّر قائمتك المهمة الأولى ثلاث مرات كما يلي: لا نريد الأكل فقط، إذ لدينا مهام أخرى يجب تنفيذها، وسنوضّح فيما يلي كيفية إجراء استدعاءات لمكوّنات مختلفة تصيّر محتوًى فريدًا. إنشاء مكون <Todo /‎> فريد تُعَدّ المكونات مهمةً لأنها تتيح إعادة استخدام أجزاء من واجهة المستخدِم، والإشارة إلى مكان ما ليكون مصدرًا لواجهة المستخدِم تلك، ولكن تكمن المشكلة في أننا لا نريد إعادة استخدام جميع المكونات، وإنما نريد إعادة استخدام معظم الأجزاء، وتعديل أجزاء صغيرة، لذا يجب استخدام الخاصيات Props. الخاصية name إذا أردنا تتبّع أسماء المهام التي نريد إكمالها، فيجب علينا التأكد من أنّ كل مكوّن <Todo /‎> يصيِّر اسمًا فريدًا، لذا امنح كل مكوّن <Todo /‎> في الملف App.js خاصية الاسم name، ولنستخدم أسماء المهام التي كانت لدينا سابقًا كما يلي: <Todo name="Eat" /> <Todo name="Sleep" /> <Todo name="Repeat" /> إذا حدّثتَ متصفحك، فسترى الشيء السابق نفسه بالضبط، إذ أعطينا المكوّن <Todo /‎> بعض الخاصيات، لكننا لم نستخدِمها بعد، فلنَعُد الآن إلى الملف Todo.js ونصلح كل شيء. عدّل أولًا تعريف الدالة Todo()‎ بحيث تأخذ الخاصيات props على أساس معامِل، كما يمكنك تطبيق التابع console.log()‎ على الخاصيات props كما فعلنا سابقًا إذا أردت التحقق من استلام المكوّن للخاصيات استلامًا صحيحًا، ثم يمكنك وضع خاصية الاسم name مكان تكرارات المهمة Eat، وتذكّر استخدام الأقواس المعقوصة لحقن قيمة متغير في تعابير JSX، إذ يجب أن تكون الدالة Todo()‎ بعد ذلك كما يلي: export default function Todo(props) { return ( <li className="todo stack-small"> <div className="c-cb"> <input id="todo-0" type="checkbox" defaultChecked={true} /> <label className="todo-label" htmlFor="todo-0"> {props.name} </label> </div> <div className="btn-group"> <button type="button" className="btn"> Edit <span className="visually-hidden">{props.name}</span> </button> <button type="button" className="btn btn__danger"> Delete <span className="visually-hidden">{props.name}</span> </button> </div> </li> ); } يجب أن يعرض متصفحك الآن ثلاث مهام فريدة، ولكن لا تزال جميع هذه المهام مُحدَّدةً افتراضيًا. الخاصية completed حُدِّدت المهمة Eat فقط في القائمة الثابتة الأصلية، إذ نريد إعادة استخدام معظم واجهة المستخدِم التي تشكل المكوِّن <Todo /‎‎‎> مع تعديل شيء واحد من خلال منح كل استدعاء للمكوّن <Todo /‎‎‎> في الملف App.js الخاصية completed الجديدة، كما يجب أن يكون للخاصية completed التابعة للمكوّن الأول الذي اسمه Eat القيمة true، وللمكونات الأخرى القيمة false كما يلي: <Todo name="Eat" completed={true} /> <Todo name="Sleep" completed={false} /> <Todo name="Repeat" completed={false} /> يجب علينا العودة إلى الملف Todo.js لاستخدام هذه الخاصيات، لذا عدّل السمة defaultChecked للعنصر <input /‎> بحيث تساوي قيمتها الخاصية completed، ثم يكون عنصر <input /‎> الخاص بالمكون Todo على النحو التالي: <input id="todo-0" type="checkbox" defaultChecked={props.completed} /> حدّث متصفحك لإظهار تحديد المكوِّن Eat فقط كما يلي: إذا عدّلت كل خاصيات completed الخاصة بالمكوّن <Todo /‎>، فسيحدِّد متصفحك أو يلغي تحديد مربعات الاختيار المكافئة والمُصيَّرة وفقًا لذلك. الخاصية id يعطي المكوّن <Todo /‎> لكل مهمة السمة id بالقيمة todo-0، وهذا خطأ في HTML لأن سمات id يجب أن تكون فريدةً، إذ تستخدِمها لغات جافاسكربت وCSS وغيرها بوصفها معرّفات فريدةً لأجزاء المستند، وبالتالي يجب أن نعطي المكون الخاصية id التي تأخذ قيمةً فريدةً لكل مكوّن Todo. إذًا لنمنح كل نسخة من المكوِّن <Todo /‎> معرّفًا باستخدام التنسيق todo-i، إذ تزيد قيمة i بمقدار واحد في كل مرة كما يلي: <Todo name="Eat" completed={true} id="todo-0" /> <Todo name="Sleep" completed={false} id="todo-1" /> <Todo name="Repeat" completed={false} id="todo-2" /> عُد الآن إلى الملف Todo.js واستفد من الخاصية id، إذ يجب تعديل قيمة السمة id للعنصر <input /‎> وقيمة السمة htmlFor الخاصة بالعنصر label كما يلي: <div className="c-cb"> <input id={props.id} type="checkbox" defaultChecked={props.completed} /> <label className="todo-label" htmlFor={props.id}> {props.name} </label> </div> كل شيء جيد حتى الآن، ولكن تُعَدّ شيفرتنا مكرَّرةً، فالأسطر الثلاثة التي تصيّر المكوّن <Todo /‎> متطابقة تقريبًا مع اختلاف واحد فقط هو قيمة كل خاصية، كما يمكننا تنظيف شيفرتنا باستخدام إحدى ميزات جافاسكربت الأساسية وهي التكرار Iteration، ولكن يجب أولًا إعادة التفكير في المهام لاستخدام هذه الميزة. بيانات المهام تحتوي كل مهمة من مهامنا حاليًا على ثلاثة أجزاء من المعلومات، وهي اسمها، وما إذا كانت مُحدَّدة، ومعرّفها الفريد، كما تُترجَم هذه البيانات إلى كائن Object، وبما أنه لدينا أكثر من مهمة، فسنستخدِم مصفوفةً من الكائنات لتمثيل هذه البيانات، لذا أنشئ ثابتًا const جديدًا بعد تعليمة الاستيراد الأخيرة في الملف src/index.js وقبل التابع ReactDOM.render()‎ كما يلي: const DATA = [ { id: "todo-0", name: "Eat", completed: true }, { id: "todo-1", name: "Sleep", completed: false }, { id: "todo-2", name: "Repeat", completed: false } ]; سنمرِّر بعد ذلك الثابت DATA إلى المكوّن <App /‎> بوصفه خاصيةً تُسمَّى tasks، إذ يجب أن يكون السطر الأخير من الملف src/index.js كما يلي: ReactDOM.render(<App tasks={DATA} />, document.getElementById("root")); أصبحت هذه المصفوفة متاحةً الآن للمكون App بالصورة props.tasks، كما يمكنك استخدام التابع console.log()‎ للتحقق من ذلك. التصيير مع التكرار يمكننا تصيير مصفوفة الكائنات من خلال تحويل كل منها إلى المكون <Todo /‎>، إذ تمنحنا لغة جافاسكربت تابع مصفوفة لتحويل البيانات إلى شيء آخر، وهو Array.prototype.map()‎، لذا أنشئ ثابتًا const جديدًا يسمى taskList قبل تعليمة return الخاصة بالدالة App()‎، واستخدِم التابع map()‎ لتحويله، ولنحوّل مجموعة مهامنا إلى شيء بسيط يتمثّل باسم name كل مهمة كما يلي: const taskList = props.tasks?.map(task => task.name); لنحاول وضع الثابت taskList مكان جميع أبناء العنصر <ul> كما يلي: <ul role="list" className="todo-list stack-large stack-exception" aria-labelledby="list-heading" > {taskList} </ul> يمنحنا ذلك طريقةً لإظهار جميع المكوّنات مرةً أخرى، إذ يصيّر المتصفح حاليًا اسم كل مهمة بوصفه نصًا دون بنية معينة، كما ينقصنا حاليًا بنية HTML مثل عنصر <li> ومربعات الاختيار والأزرار. يمكننا إصلاح ذلك من خلال إعادة المكوّن <Todo /‎> من التابع map()‎، وتذكَّر أنّ صيغة JSX تسمح لنا بخلط بنى جافاسكربت مع اللغات التوصيفية Markup، فلنجرب ما يلي بدلًا مما لدينا حاليًا: const taskList = props.tasks.map(task => <Todo />); انظر مرةً أخرى إلى تطبيقك، إذ تبدو مهامنا الآن كما كانت سابقًا، لكنها تفتقد إلى أسماء المهام نفسها، وتذكَّر أنّ كل مهمة نطبّق عليها التابع map()‎ لها الخاصيات id وname وchecked، والتي نريد تمريرها إلى المكوّن <Todo /‎>، وبالتالي سنحصل على الشيفرة التالية: const taskList = props.tasks.map(task => ( <Todo id={task.id} name={task.name} completed={task.completed} /> )); يبدو التطبيق الآن كما كان سابقًا، ولكن أصبحت شيفرتنا أقل تكرارًا. خاصيات key الفريدة يجب أن تتعقّب React المهام لتصييرها بصورة صحيحة بعد أن صيّرت هذه المهام من مصفوفة، إذ تستخدِم React التخمين لتتبع الأشياء، ولكن يمكننا مساعدتها عن طريق تمرير الخاصية key لمكونات <Todo /‎>، إذ تُعَدّ key خاصيةً خاصةً تديرها React، ولا يمكنك استخدام الكلمة key لأيّ غرض آخر، وبما أنّ الخاصية key يجب أن تكون فريدةً، فسنعيد استخدام خاصية id الخاصة بكل كائن مهمة على أساس مفتاح له، لذا عدِّل الثابت taskList كما يلي: const taskList = props.tasks.map(task => ( <Todo id={task.id} name={task.name} completed={task.completed} key={task.id} /> ) ); يجب عليك دائمًا تمرير مفتاح فريد لأيّ شيء تُصيّره مع التكرار، ولن يتغير شيء واضح في متصفحك، ولكن إذا لم تستخدِم مفاتيح فريدةً، فستعطي React تحذيرات في الطرفية console ويمكن أن يتصرف تطبيقك بطريقة غريبة. تقسيم أجزاء التطبيق المتبقية إلى مكونات يمكننا الآن تحويل باقي التطبيق إلى مكونات بعد فرزِنا المكوّن الأكثر أهميةً، وتذكَّر أنّ المكونات هي إما أجزاء واضحة من واجهة المستخدِم، أو أجزاء معاد استخدامها من واجهة المستخدِم، أو كليهما، كما يمكننا إنشاء مكونين آخرين هما: <Form/‎> <FilterButton/‎> بما أننا نعلم بحاجتنا لهذين المكوِنين، فيمكننا تجميع أوامر إنشاء الملفات في أمر واحد في الطرفية، لذا شغّل الأمر التالي في طرفيتك، مع الانتباه إلى أنك في المجلد الجذر لتطبيقك: touch src/components/Form.js src/components/FilterButton.js المكون <Form/‎> افتح الملف components/Form.js ونفِّذ ما يلي: استورد مكتبة React في أعلى الملف كما فعلنا في الملف Todo.js. أنشئ المكوِّن Form()‎ الجديد باستخدام بنية Todo()‎ الأساسية نفسها، ثم صدِّر هذا المكوِّن. انسخ وسوم <form> وما يوجد بينها من الملف App.js، والصقها ضمن تعليمة return الخاصة بالمكوِّن Form()‎. صدّر المكوِّن Form في نهاية الملف. يجب أن يكون الملف Form.js الآن كما يلي: import React from "react"; function Form(props) { return ( <form> <h2 className="label-wrapper"> <label htmlFor="new-todo-input" className="label__lg"> What needs to be done? </label> </h2> <input type="text" id="new-todo-input" className="input input__lg" name="text" autoComplete="off" /> <button type="submit" className="btn btn__primary btn__lg"> Add </button> </form> ); } export default Form; المكون <FilterButton/‎> كرِّر الأمور نفسها التي نفّذتها لإنشاء الملف Form.js على الملف FilterButton.js، ولكن استدعِ المكوِّن FilterButton()‎ وانسخ جزء HTML للزر الأول الموجود ضمن العنصر <div> ذو الصنف filters من الملف App.js في تعليمة return، إذ يجب أن يكون الملف الآن كما يلي: import React from "react"; function FilterButton(props) { return ( <button type="button" className="btn toggle-btn" aria-pressed="true"> <span className="visually-hidden">Show </span> <span>all </span> <span className="visually-hidden"> tasks</span> </button> ); } export default FilterButton; استيراد جميع المكونات أضف بعض تعليمات الاستيراد import في الجزء العلوي من الملف App.js لاستيراد هذه المكوّنات الجديدة، ثم عدّل تعليمة return الخاصة بالمكوِّن App()‎ لتصيير المكونات، إذ يجب أن يكون الملف ‎‎App.js كما يلي: import React from "react"; import Form from "./components/Form"; import FilterButton from "./components/FilterButton"; import Todo from "./components/Todo"; function App(props) { const taskList = props.tasks.map(task => ( <Todo id={task.id} name={task.name} completed={task.completed} key={task.id} /> ) ); return ( <div className="todoapp stack-large"> <h1>TodoMatic</h1> <Form /> <div className="filters btn-group stack-exception"> <FilterButton /> <FilterButton /> <FilterButton /> </div> <h2 id="list-heading">3 tasks remaining</h2> <ul role="list" className="todo-list stack-large stack-exception" aria-labelledby="list-heading" > {taskList} </ul> </div> ); } export default App; نكون بذلك جاهزين تقريبًا للتعامل مع التفاعل في تطبيق React الخاص بنا. الخلاصة تعمّقنا في كيفية تقسيم التطبيق إلى مكوّنات وتصييرها بكفاءة، إذ سننتقل الآن إلى إلقاء نظرة على كيفية التعامل مع الأحداث في React وإضافة التفاعل. ترجمة -وبتصرُّف- للمقال Componentizing our React app. اقرأ أيضًا أساسيات بناء تطبيقات الويب مكونات React الأساسية (React Components)
  15. لنفترض أننا نريد توضيح مفهوم React من خلال إنشاء تطبيق يسمح للمستخدِمين بإضافة المهام التي يريدون العمل عليها وتعديلها وحذفها، وكذلك وضع علامة على المهام المكتملة دون حذفها، إذ سنوجّهك من خلال هذا المقال لوضع بنية المكوّن App الأساسية وتصميمه في المكان الصحيح، وتعريف المكوّنات الفردية والتفاعلية التي سنضيفها لاحقًا. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript ومعرفة استخدام سطر الأوامر أو الطرفية. الهدف: تقديم دراسة حالة تطبيق قائمة المهام، ووضع بنية المكوّن App الأساسية وتصميمه في المكان الصحيح. قصص مستخدم التطبيق تُعَدّ قصة المستخدِم في تطوير البرمجيات هدفًا قابلًا للتنفيذ من منظور المستخدِم، إذ سيساعدنا تحديد قصص المستخدِمين قبل البدء في تركيز عملنا، ويجب على تطبيقنا تحقيق القصص التالية، إذ يمكن للمستخِدم تنفيذ ما يلي: قراءة قائمة المهام. إضافة مهمة باستخدام الفأرة أو لوحة المفاتيح. وضْع علامة على المهام المكتملة باستخدام الفأرة أو لوحة المفاتيح. حذف أيّ مهمة باستخدام الفأرة أو لوحة المفاتيح. تعديل أيّ مهمة باستخدام الفأرة أو لوحة المفاتيح. عرض مجموعة فرعية محدَّدة من المهام: جميع المهام، أو المهمة النشطة فقط، أو المهام المكتملة فقط. تجهيز المشروع الأولي أنشأتْ الأداة create-react-app بعض الملفات التي لن نستخدِمها مطلقًا في مشروعنا، إذ لن نضيف ملف تنسيق سابق لعرض المكونات، لذا احذف أولًا استيراد App.css من أعلى الملف App.js، كما أننا لن نستخِدم الملف logo.svg، لذا أزِل استيراده أيضًا، ثم انسخ والصق بعد ذلك الأوامر التالية في طرفيتك لحذف بعض الملفات غير الضرورية، وتأكّد من أنك تبدأ من المجلد الجذر للتطبيق: # ‫انتقل إلى المجلد src الخاص بمشروعك cd src # احذف بعض الملفات rm -- App.test.js App.css logo.svg serviceWorker.js setupTests.js # انتقل احتياطيًا إلى جذر المشروع cd .. ملاحظتان: هناك ملفان من الملفات التي حذفناها مخصَّصان لاختبار التطبيق، وبالتالي لن نغطّي الاختبار في مثالنا. إذا أوقفت خادمك لتنفيذ المهام السابقة في الطرفية، فيجب تشغيله مرةً أخرى باستخدام الأمر npm start. شيفرة المشروع الأساسية سنقدِّم فيما يلي شيفرة الدالة App()‎ وشيفرة CSS لتنسيق تطبيقك لتستخدمها بدلًا من الشيفرة التي لديك الآن. صيغة JSX انسخ مقتطف الشيفرة التالي إلى مفكرتك، ثم الصقه في الملف App.js بحيث يحل محل دالة App()‎ الحالية: function App(props) { return ( <div className="todoapp stack-large"> <h1>TodoMatic</h1> <form> <h2 className="label-wrapper"> <label htmlFor="new-todo-input" className="label__lg"> What needs to be done? </label> </h2> <input type="text" id="new-todo-input" className="input input__lg" name="text" autoComplete="off" /> <button type="submit" className="btn btn__primary btn__lg"> Add </button> </form> <div className="filters btn-group stack-exception"> <button type="button" className="btn toggle-btn" aria-pressed="true"> <span className="visually-hidden">Show </span> <span>all</span> <span className="visually-hidden"> tasks</span> </button> <button type="button" className="btn toggle-btn" aria-pressed="false"> <span className="visually-hidden">Show </span> <span>Active</span> <span className="visually-hidden"> tasks</span> </button> <button type="button" className="btn toggle-btn" aria-pressed="false"> <span className="visually-hidden">Show </span> <span>Completed</span> <span className="visually-hidden"> tasks</span> </button> </div> <h2 id="list-heading"> 3 tasks remaining </h2> <ul role="list" className="todo-list stack-large stack-exception" aria-labelledby="list-heading" > <li className="todo stack-small"> <div className="c-cb"> <input id="todo-0" type="checkbox" defaultChecked={true} /> <label className="todo-label" htmlFor="todo-0"> Eat </label> </div> <div className="btn-group"> <button type="button" className="btn"> Edit <span className="visually-hidden">Eat</span> </button> <button type="button" className="btn btn__danger"> Delete <span className="visually-hidden">Eat</span> </button> </div> </li> <li className="todo stack-small"> <div className="c-cb"> <input id="todo-1" type="checkbox" /> <label className="todo-label" htmlFor="todo-1"> Sleep </label> </div> <div className="btn-group"> <button type="button" className="btn"> Edit <span className="visually-hidden">Sleep</span> </button> <button type="button" className="btn btn__danger"> Delete <span className="visually-hidden">Sleep</span> </button> </div> </li> <li className="todo stack-small"> <div className="c-cb"> <input id="todo-2" type="checkbox" /> <label className="todo-label" htmlFor="todo-2"> Repeat </label> </div> <div className="btn-group"> <button type="button" className="btn"> Edit <span className="visually-hidden">Repeat</span> </button> <button type="button" className="btn btn__danger"> Delete <span className="visually-hidden">Repeat</span> </button> </div> </li> </ul> </div> ); } افتح الآن الملف public/index.html وعدّل نص العنصر <title> ليصبح TodoMatic، بحيث يطابق العنصر <h1> الموجود أعلى تطبيقنا. <title>TodoMatic</title> يجب أن ترى ما يلي عند تحديث متصفحك: لا يبدو هذا التطبيق جميلًا ولا يعمل بعد، لكنه جيد حاليًا، وضَع في بالك شيفرة JSX، وكيفية توافقها مع قصص المستخدِمين: لدينا عنصر <form> مع العنصر <input type="text"‎> لكتابة مهمة جديدة، وزر لإرسال النموذج. لدينا مجموعة من الأزرار التي سنستخدِمها لمهامنا. لدينا عنوان heading يخبرنا عن عدد المهام المتبقية. لدينا ثلاث مهام مرتبة ضمن قائمة غير مرتبة، إذ تُعَدّ كل مهمة أنها عنصر قائمة <li>، كما تحتوي على أزرار لتعديلها وحذفها، بالإضافة إلى مربع اختيار لإيقاف تشغيلها. سيسمح النموذج بإنشاء المهام، إذ تسمح الأزرار بترشيح المهام، ويُعَدّ العنوان والقائمة طريقةً لقراءتها، إذ ليست واجهة المستخدِم المُستخدَمة لتعديل مهمة موجودةً في الوقت الحالي، ولكن سنكتبها لاحقًا. ميزات الشمولية قد تلاحظ بعض السمات غير العادية هنا مثل: <button type="button" className="btn toggle-btn" aria-pressed="true"> <span className="visually-hidden">Show </span> <span>all</span> <span className="visually-hidden"> tasks</span> </button> تخبر السمة aria-pressed التقنيات المساعدة مثل قارئات الشاشة أنه قد يكون الزر في إحدى حالتين وهما مضغوط pressed أو غير مضغوط unpressed، إذ تمثِّلان حالة التشغيل on والإيقاف off، ويعني تعيين القيمة true أنّ الزر مضغوط افتراضيًا. ليس للصنف visually-hidden أيَّ تأثير حتى الآن، لأننا لم نضمِّن شيفرة CSS، فإذا وضعنا التنسيقات في مكانها الصحيح، فسيُخفَى أيّ عنصر في هذا الصنف عن المستخدِمين المبصرين، وسيظل متاحًا لمستخدِمي قارئ الشاشة، لأن هذه الكلمات لا يحتاجها المستخدِمون المبصرون، إذ تُستخدَم لتقديم المزيد من المعلومات حول ما يفعله الزر لمستخدِمي قارئ الشاشة الذين ليس لديهم القدرة البصرية الإضافية لمساعدتهم، كما يمكنك العثور على العنصر <ul> التالي: <ul role="list" className="todo-list stack-large stack-exception" aria-labelledby="list-heading" > تساعد السمة role التقنيات المساعِدة في توضيح نوع العنصر الذي يمثِّله الوسم، إذ يجري التعامل مع العنصر <ul> بوصفه قائمةً افتراضيًا، ولكن ستؤدي التنسيقات التي نضيفها إلى تعطيل هذه الوظيفة، في حين ستؤدي السمة role إلى استعادة القائمة التي تعني العنصر <ul>. تخبر السمة aria-labelledby التقنيات المساعِدة بتعاملنا مع عنوان قائمتنا بوصفه العنوان الذي يصف الغرض من القائمة الموجودة تحته، مما يساعد مستخدِمي قارئ الشاشة على فهم الغرض منها بصورة أفضل، وأخيرًا، فتملك عناصر label وinput في عناصر القائمة بعض السمات الفريدة الخاصة بصيغة JSX، وهي: <input id="todo-0" type="checkbox" defaultChecked={true} /> <label className="todo-label" htmlFor="todo-0"> Eat </label> السمة defaultChecked في الوسم <input /‎> تخبر React بتحديد مربع الاختيار مبدئيًا، فإذا أردنا استخدام السمة checked كما نفعل في HTML، فستعرِض React بعض التحذيرات المتعلقة بمعالجة أحداث مربع الاختيار في طرفية متصفحك (نافذة console)، والتي يجب تجنبها، ولا تقلق كثيرًا بشأن ذلك في الوقت الحالي، إذ سنغطي ذلك لاحقًا عندما نبدأ باستخدام الأحداث. تتوافق السمة htmlFor مع السمة for المُستخدَمة في لغة HTML، ولا يمكننا استخدام الكلمة for بوصفها سمةً في صيغة JSX لأنها كلمة محجوزة، لذلك تستخدِم React السمة htmlFor بدلًا من ذلك. ملاحظتان: يمكنك استخدام القيم المنطقية -أي true وfalse- في سمات JSX من خلال إحاطة هذه القيم بأقواس معقوصة، فإذا كتبت السمة defaultChecked="true"‎ مثلًا، فستكون "true" هي قيمة السمة defaultChecked، والتي تُعَدّ سلسلةً حرفيةً String Literal، لأنها لغة جافاسكربت وليست لغة HTML. تملك السمة aria-pressed القيمة "true" في مثالنا لأنّ aria-pressed ليست سمةً منطقيةً حقيقيةً بالطريقة التي تستخدِمها السمة checked بها. تنفيذ التنسيقات الصق شيفرة CSS التالية في الملف src/index.css لتحُل محل ما هو موجود حاليًا: /* إعادة الضبط */ *, *::before, *::after { box-sizing: border-box; } *:focus { outline: 3px dashed #228bec; outline-offset: 0; } html { font: 62.5% / 1.15 sans-serif; } h1, h2 { margin-bottom: 0; } ul { list-style: none; padding: 0; } button { border: none; margin: 0; padding: 0; width: auto; overflow: visible; background: transparent; color: inherit; font: inherit; line-height: normal; -webkit-font-smoothing: inherit; -moz-osx-font-smoothing: inherit; -webkit-appearance: none; } button::-moz-focus-inner { border: 0; } button, input, optgroup, select, textarea { font-family: inherit; font-size: 100%; line-height: 1.15; margin: 0; } button, input { overflow: visible; } input[type="text"] { border-radius: 0; } body { width: 100%; max-width: 68rem; margin: 0 auto; font: 1.6rem/1.25 Arial, sans-serif; background-color: #f5f5f5; color: #4d4d4d; } @media screen and (min-width: 620px) { body { font-size: 1.9rem; line-height: 1.31579; } } /* نهاية إعادة الضبط */ /* التنسيقات العامة */ .form-group > input[type="text"] { display: inline-block; margin-top: 0.4rem; } .btn { padding: 0.8rem 1rem 0.7rem; border: 0.2rem solid #4d4d4d; cursor: pointer; text-transform: capitalize; } .btn.toggle-btn { border-width: 1px; border-color: #d3d3d3; } .btn.toggle-btn[aria-pressed="true"] { text-decoration: underline; border-color: #4d4d4d; } .btn__danger { color: #fff; background-color: #ca3c3c; border-color: #bd2130; } .btn__filter { border-color: lightgrey; } .btn__primary { color: #fff; background-color: #000; } .btn-group { display: flex; justify-content: space-between; } .btn-group > * { flex: 1 1 49%; } .btn-group > * + * { margin-left: 0.8rem; } .label-wrapper { margin: 0; flex: 0 0 100%; text-align: center; } .visually-hidden { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px 1px 1px 1px); clip: rect(1px, 1px, 1px, 1px); white-space: nowrap; } [class*="stack"] > * { margin-top: 0; margin-bottom: 0; } .stack-small > * + * { margin-top: 1.25rem; } .stack-large > * + * { margin-top: 2.5rem; } @media screen and (min-width: 550px) { .stack-small > * + * { margin-top: 1.4rem; } .stack-large > * + * { margin-top: 2.8rem; } } .stack-exception { margin-top: 1.2rem; } /* نهاية التنسيقات العامة */ .todoapp { background: #fff; margin: 2rem 0 4rem 0; padding: 1rem; position: relative; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 2.5rem 5rem 0 rgba(0, 0, 0, 0.1); } @media screen and (min-width: 550px) { .todoapp { padding: 4rem; } } .todoapp > * { max-width: 50rem; margin-left: auto; margin-right: auto; } .todoapp > form { max-width: 100%; } .todoapp > h1 { display: block; max-width: 100%; text-align: center; margin: 0; margin-bottom: 1rem; } .label__lg { line-height: 1.01567; font-weight: 300; padding: 0.8rem; margin-bottom: 1rem; text-align: center; } .input__lg { padding: 2rem; border: 2px solid #000; } .input__lg:focus { border-color: #4d4d4d; box-shadow: inset 0 0 0 2px; } [class*="__lg"] { display: inline-block; width: 100%; font-size: 1.9rem; } [class*="__lg"]:not(:last-child) { margin-bottom: 1rem; } @media screen and (min-width: 620px) { [class*="__lg"] { font-size: 2.4rem; } } .filters { width: 100%; margin: unset auto; } ‏/* تنسيقات عناصر‫ Todo */ .todo { display: flex; flex-direction: row; flex-wrap: wrap; } .todo > * { flex: 0 0 100%; } .todo-text { width: 100%; min-height: 4.4rem; padding: 0.4rem 0.8rem; border: 2px solid #565656; } .todo-text:focus { box-shadow: inset 0 0 0 2px; } /* تنسيقات مربعات الاختيار */ .c-cb { box-sizing: border-box; font-family: Arial, sans-serif; -webkit-font-smoothing: antialiased; font-weight: 400; font-size: 1.6rem; line-height: 1.25; display: block; position: relative; min-height: 44px; padding-left: 40px; clear: left; } .c-cb > label::before, .c-cb > input[type="checkbox"] { box-sizing: border-box; top: -2px; left: -2px; width: 44px; height: 44px; } .c-cb > input[type="checkbox"] { -webkit-font-smoothing: antialiased; cursor: pointer; position: absolute; z-index: 1; margin: 0; opacity: 0; } .c-cb > label { font-size: inherit; font-family: inherit; line-height: inherit; display: inline-block; margin-bottom: 0; padding: 8px 15px 5px; cursor: pointer; touch-action: manipulation; } .c-cb > label::before { content: ""; position: absolute; border: 2px solid currentColor; background: transparent; } .c-cb > input[type="checkbox"]:focus + label::before { border-width: 4px; outline: 3px dashed #228bec; } .c-cb > label::after { box-sizing: content-box; content: ""; position: absolute; top: 11px; left: 9px; width: 18px; height: 7px; transform: rotate(-45deg); border: solid; border-width: 0 0 5px 5px; border-top-color: transparent; opacity: 0; background: transparent; } .c-cb > input[type="checkbox"]:checked + label::after { opacity: 1; } احفظ الملف وألقِ نظرةً على المتصفح، إذ يجب أن يتمتع تطبيقك الآن بتنسيق مقبول. الخلاصة يبدو تطبيق قائمة المهام الآن أشبه بتطبيق حقيقي، ولكنه لا يفعل أيّ شيء فعليًا، إذ سنبدأ في إصلاح ذلك لاحقًا في المقالات القادمة. ترجمة -وبتصرُّف- للمقال Beginning our React todo list. اقرأ أيضًا أساسيات بناء تطبيقات الويب مكونات React الأساسية (React Components) إنشاء تطبيق قائمة مهام بسيط باستخدام Laravel 5
  16. سنلقي في هذا المقال نظرةً على مكتبة React، إذ سنطّلع على بعض التفاصيل حول خلفيتها وحالات استخدامها، وسننشئ سلسلة أدوات React الأساسية وتطبيقًا بسيطًا بحيث نتعلم كيفية عمل React. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript ومعرفة استخدام سطر الأوامر أو الطرفية، إذ تستخدِم React صيغة لغة HTML ضمن جافاسكربت HTML-in-JavaScript، والتي تسمى JSX، أي JavaScript وXML، كما سيساعدك التعرف على كل من لغة HTML وجافاسكربت على تعلّم صيغة JSX، وتحديد ما إذا كانت الأخطاء في تطبيقك مرتبطةً بجافاسكربت أو بمجال أكثر تحديدًا من React. الهدف: إعداد بيئة تطوير React المحلية، وإنشاء تطبيق بسيط، وفهم أساسيات عمله. تُعَدّ React مكتبةً لبناء واجهات المستخدِم، ولا تُعَدّ إطار عمل، فهي ليست حصريةً للويب، كما تُستخدَم مكتبة React مع المكتبات الأخرى للتصيير Render إلى بيئات معينة، إذ يمكن استخدام إطار عمل React Native لبناء تطبيقات الهاتف المحمول، لكن يستخدِم المطورون مكتبة React جنبًا إلى جنب مع ReactDOM للبناء للويب، إذ تُستخدَم React و ReactDOM في المجالات نفسها ولحل المشكلات نفسها التي تستخدِمها أطر تطوير الويب الحقيقية الأخرى، لذلك نشير إلى React بوصفها إطار عمل Framework. تهدف React إلى تقليل الأخطاء التي تحدث عندما يبني المطورون واجهات المستخدِم من خلال استخدام المكوّنات Components، والتي تُعَدّ أجزاءً من الشيفرة البرمجية المنطقية والمستقلة ذاتيًا والتي تصف جزءًا من واجهة المستخدِم، إذ يمكن تكوين هذه المكونات مع بعضها البعض لإنشاء واجهة مستخدِم كاملة، كما تجرِّّد React كثيرًا من أعمال التصيير، وبالتالي تجعلك تركِّز على تصميم واجهة المستخدِم. حالات الاستخدام Use cases لا تفرض React قواعد صارمةً حول اصطلاحات الشيفرة أو تنظيم الملفات خلاف أطر العمل Frameworks الأخرى، مما يتيح لفرق العمل تحديد الاصطلاحات التي تناسبها بصورة أفضل، واستخدام مكتبة React بالطريقة التي ترغب بها، إذ يمكن لمكتبة React معالجة زر واحد أو أجزاء من الواجهة أو واجهة المستخدِم للتطبيق بأكمله، فإذا أردت استخدام React لأجزاء صغيرة من الواجهة، فلا يُعَدّ ذلك سهلًا مثل بناء تطبيق باستخدام مكتبة مثل jQuery أو إطار عمل مثل Vue، إذ يكون استخدام مكتبة React أسهل عند إنشاء تطبيقك بالكامل باستخدامها. كما تتطلب العديد من مزايا تجربة المطوِّر لتطبيق React مثل كتابة الواجهات باستخدام صيغة JSX، عملية تصريف Compilation، في حين تبطّئ إضافة مصرِّف مثل Babel إلى موقع ويب الشيفرة الموجودة عليه، لذلك يُعِدّ المطورون مثل هذه الأدوات باستخدام خطوة بناء، إذ يمكن القول أنّ React لها متطلبات أدوات كثيرة، ولكن يمكن تعلّمها، كما سيركِّز هذا المقال على حالة استخدام React لتصيير واجهة المستخدِم بالكامل لتطبيق ما باستخدام الأدوات التي توفرها أداة create-react-app الخاصة بفيسبوك. كيفية استخدام React للغة جافاسكربت تستخدِم React ميزات لغة جافاسكربت الحديثة للعديد من أنماطها، ولكن يأتي أكبر تحوّل لها عن جافاسكربت عند استخدام صيغة JSX التي توسِّع صيغة جافاسكربت، بحيث يمكن أن تكون الشيفرة البرمجية التي تشبه HTML جنبًا إلى جنب معها، وإليك المثال التالي: const heading = <h1>Mozilla Developer Network</h1>; يُعرَف الثابت heading السابق بتعبير JSX، ويمكن لمكتبة React استخدامه لتصيير الوسم <h1> في التطبيق، ولنفترض أننا أردنا تغليف العنوان heading بوسم <header> لأسباب دلالية، إذ تتيح صيغة JSX بتداخل العناصر ضمن بعضها بعضًا كما نفعل مع لغة HTML كما يلي: const header = ( <header> <h1>Mozilla Developer Network</h1> </header> ); ملاحظة: لا تُعَدّ الأقواس في المقتطف السابق خاصةً بصيغة JSX، وليس لها أيّ تأثير على تطبيقك، وإنما تُعَدّ إشارةً لك ولحاسوبك بأن الأسطر المتعددة من الشيفرة البرمجية الموجودة ضمنها هي جزء من التعبير نفسه، كما يمكنك كتابة تعبير header كما يلي: const header = <header> <h1>Mozilla Developer Network</h1> </header> لا يمكن لمتصفحك قراءة صيغة JSX بدون مساعدة، إذ سيبدو التعبير header كما يلي عند تصريفه باستخدام أداة Babel أو Parcel: const header = React.createElement("header", null, React.createElement("h1", null, "Mozilla Developer Network") ); يمكن تخطي خطوة التصريف واستخدام التابع React.createElement()‎ لكتابة واجهة المستخدِم بنفسك، ولكنك تفقد بذلك ميزة JSX التصريحية، وتصبح قراءة شيفرتك أصعب، إذ يُعَدّ التصريف خطوةً إضافيةً في عملية التطوير، في حين يعتقد العديد من المطورين في مجتمع React أنّ قابلية قراءة JSX تستحق العناء، كما تجعل الأدوات الشائعة تصريف صيغة JSX إلى جافاسكربت جزءًا من عملية الإعداد، ولا يتعين عليك إعداد التصريف بنفسك إلّا إذا أردت ذلك. تُعَدّ صيغة JSX مزيجًا من لغتَي HTML وجافاسكربت، لذلك يجدها بعض المطورين سهلة التعلم، ويجدها آخرون مربكةً بسبب طبيعتها الممزوجة، ولكنها ستسمح لك ببناء واجهات مستخدِم بسرعة وبسهولة إذا أتقنتها، كما ستسمح للآخرين بفهم قاعدة شيفرتك البرمجية فهمًا أفضل وبسرعة، كما يمكنك الاطلاع على صفحة شرح JSX بالتفصيل من توثيق React في موسوعة حسوب لقراءة المزيد عن JSX. إعداد تطبيق React الأول هناك العديد من الطرق لاستخدام React، لكننا سنستخدِم create-react-app وهي أداة واجهة سطر الأوامر -أو CLI اختصارًا-، إذ تسرّع هذه الأداة عملية تطوير تطبيق React عن طريق تثبيت بعض الحزم وإنشاء بعض الملفات، والتعامل مع الأدوات الموضَّحة سابقًا، كما يمكن إضافة React إلى موقع ويب دون استخدام الأداة create-react-app عن طريق نسخ بعض عناصر <script> في ملف HTML، ولكن تُعَدّ الأداة create-react-app نقطة بداية شائعة لتطبيقات React، إذ سيسمح لك استخدامها بقضاء المزيد من الوقت في بناء تطبيقك ووقت أقل في التفكير في الإعداد. المتطلبات يجب تثبيت Node.js من أجل استخدام create-react-app، كما يوصى باستخدام إصدار الدعم طويل الأمد Long-term Support -أو LTS اختصارًا-، إذ يتضمن Node مدير الحزم npm ومشغّل الحزم npx، كما يمكنك استخدام مدير الحزم Yarn، لكننا سنفترض أنك تستخدِم npm في هذا المقال، وهنا يمكنك الاطلاع على مقال أساسيات إدارة الحزم لمزيد من المعلومات حول npm وYarn. إذا استخدمت نظام ويندوز Windows، فستحتاج إلى تثبيت بعض البرامج التي تمنحك التكافؤ مع طرفية نظام يونيكس Unix أو نظام ماك macOS لاستخدام أوامر الطرفية التي سنستخدِمها، إذ يُعَدّ كل من Gitbash الذي يكون جزءًا من مجموعة أدوات git لنظام ويندوز أو نظام ويندوز الفرعي للينكس Windows Subsystem for Linux -أو WSL اختصارًا- مناسبَين، كما يمكنك الاطلاع على دليل استخدام سطر الأوامر للحصول على مزيد من المعلومات حول هذه الأوامر وحول أوامر الطرفية بصفة عامة. ضع في بالك أنّ React و ReactDOM ينتجان تطبيقات تعمل فقط على مجموعة حديثة إلى حد ما من المتصفحات مثل IE9+‎ باستخدام تعويض نقص دعم المتصفحات Polyfill، كما يوصَى باستخدام متصفح حديث مثل فايرفوكس Firefox أو مايكروسوفت إيدج Microsoft Edge أو سفاري Safari أو كروم Chrome. تهيئة التطبيق تأخذ الأداة create-react-app وسيطًا واحدًا هو الاسم الذي ترغب في منحه لتطبيقك، وتستخدِمه لإنشاء مجلد جديد، ثم تنشئ الملفات الضرورية بداخله، وتأكد من تطبيق الأمر cd على المكان الذي تريد أن يكون فيه تطبيقك على القرص الصلب، ثم شغّل الأمر التالي في الطرفية: npx create-react-app moz-todo-react يؤدي تشغيل الأمر السابق إلى إنشاء المجلد moz-todo-response، مع تنفيذ الأمور التالية ضمنه: تثبيت بعض حزم npm الأساسية لعمل التطبيق. كتابة سكربتات لبدء التطبيق وتنفيذه. إنشاء بنية من الملفات والمجلدات التي تحدِّد معمارية التطبيق الأساسية. تهيئة المجلد بوصفه مستودع جيت git إذا كان جيت مثبتًا على حاسوبك. ملاحظة: إذا كان مدير الحزم yarn مثبتًا لديك، فستُستخدَم أداة create-react-app افتراضيًا لاستخدام yarn بدلًا من npm، وإذا كان كل من مديرَي الحزم مثبَّتَين لديك وتريد استخدام npm صراحةً، فيمكنك إضافة الراية ‎--use-npm عند تشغيل create-react-app: npx create-react-app moz-todo-react --use-npm ستعرِض create-react-app عددًا من الرسائل في الطرفية أثناء عملها، وهذا أمر طبيعي، إذ يمكن أن يستغرق ذلك بضع دقائق، فالوقت مناسب الآن لتحضير كوب من الشاي. غيّر المسار الحالي إلى المجلد moz-todo-react باستخدام الأمر cd عند اكتمال العملية، ثم شغّل الأمر npm start، إذ سيبدأ تشغيل السكربتات المُثبَّتة باستخدام الأداة create-react-app على خادم محلي على المضيف المحلي localhost الذي هو 3000، وافتح التطبيق في تبويب جديد من المتصفح، إذ سيعرِض متصفحك ما يلي: بنية التطبيق تمنحنا أداة create-react-app كل ما نحتاجه لتطوير تطبيق React، إذ تبدو بنية الملفات الأولية الخاصة به كما يلي: moz-todo-react ├── README.md ├── node_modules ├── package.json ├── package-lock.json ├── .gitignore ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src ├── App.css ├── App.js ├── App.test.js ├── index.css ├── index.js ├── logo.svg ├── reportWebVitals.js └── setupTests.js يُعَدّ المجلد src بأنه المكان الذي سنقضي فيه معظم وقتنا، فهو مكان وجود شيفرة تطبيقنا البرمجية، كما يحتوي المجلد public على ملفات سيقرأها متصفحك أثناء تطوير التطبيق وأهمها index.html، إذ تحقن React شيفرتك البرمجية في هذا الملف ليتمكّن متصفحك من تشغيلها، وهناك بعض الوسوم الأخرى التي تساعد الأداة create-react-app في عملها، لذا احرص على عدم تعديلها إلا إذا كنت متأكدًا مما تفعله، ولكن يجب عليك تغيير النص الموجود داخل العنصر <title> في هذا الملف ليعكس عنوان تطبيقك، وعناوين الصفحات الدقيقة مهمة من أجل إمكانية الوصول. سيُنشَر أيضًا المجلد public عند إنشاء ونشر إصدار الإنتاج من تطبيقك، إذ لن نغطّي مرحلة النشر في هذا المقال، ولكن يجب أن تكون قادرًا على استخدام حل مشابه لذلك الموضَّح في مقال نشر التطبيق، في حين يحتوي الملف package.json على معلومات حول مشروعنا، والتي يستخدِمها كل من Node.js وnpm لإبقائه منظمًّا، كما لا يُعَدّ هذا الملف خاصًا بتطبيقات React، ولا تحتاج إلى فهم هذا الملف على الإطلاق لإكمال هذا المقال، ولكن إذا أردتَ معرفة المزيد عنه، فيمكنك قراءة مقال أساسيات إدارة الحزم. استكشاف مكون React الأول يُعَدّ المكوّن Component في React وحدةً قابلةً لإعادة الاستخدام والتي تصيّر جزءًا من التطبيق، كما يمكن أن تكون هذه الأجزاء كبيرةً أو صغيرةً، لكنها تكون عادةً محددةً بوضوح، فهي تخدم غرضًا واحدًا واضحًا، ولنفتح الملف src/App.js، لأنّ متصفحنا يطالبنا بتعديله، إذ يحتوي هذا الملف على المكوِّن الأول App وعدد قليل من سطور الشيفرة البرمجية الأخرى: import React from 'react'; import logo from './logo.svg'; import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } export default App; يتكون ملف App.js من ثلاثة أجزاء رئيسية وهي كما يلي، إذ تتبع معظم مكونات React هذا النمط: بعض تعليمات الاستيراد import في الأعلى. المكوِّن App في المنتصف. تعليمة تصدير export في الأسفل. تعليمات الاستيراد Import تسمح تعليمات الاستيراد الموجودة في أعلى الملف App.js باستخدام الشيفرة المُعرَّفة في مكان آخر، وهذه التعليمات هي: import React from 'react'; import logo from './logo.svg'; import './App.css'; تستورِد التعليمة الأولى مكتبة React التي تحوِّل صيغة JSX التي نكتبها إلى التابع React.createElement()‎، ويجب على جميع مكونّات React استيراد وحدة React، فإذا تخطيت هذه الخطوة، فسيعطي تطبيقك خطأً، في حين تستورِد التعليمة الثانية صورة شعار Logo من الملف '‎./logo.svg'، ولاحظ استخدام /. في بداية المسار، والامتداد ‎.svg في نهايته، إذ يدل ذلك على أن الملف محلي وأنه ليس ملف جافاسكربت، ويوجد الملف logo.svg في مجلدنا المصدر، ولا نكتب مسارًا أو امتدادًا عند استيراد وحدة React، لأنه لا يُعَدّ ملفًا محليًا، وإنما يُدرَج بوصفه اعتماديةً Dependency في الملف package.json. تستورِد التعليمة الثالثة ملف CSS المتعلق بالمكوّن App، ولاحظ عدم وجود اسم متغير والموجِّه from، إذ لا تُعَدّ هذه الصيغة أصيلةً Native في صيغة وحدة جافاسكربت، وإنما تأتي من أداة Webpack وهي الأداة التي تستخدِمها create-react-app لتجميع جميع ملفات جافاسكربت مع بعضها بعضًا وتقديمها إلى المتصفح. المكون App توجد دالة تسمَّى App بعد تعليمات الاستيراد، إذ يفضِّل مجتمع جافاسكربت استخدام الأسماء بحالة الجَمل Camel-case مثل helloWorld، في حين تستخدِم مكونات React أسماء المتغيرات بحالة باسكال Pascal-case مثل HelloWorld لتوضيح أنّ عنصر JSX المحدَّد هو مكون React وليس وسم HTML عادي، فإذا أردت إعادة تسمية الدالة App لتصبح app، فسيعطي متصفحك خطأً. function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } تعيد الدالة App تعبير JSX الذي يحدِّد ما يصيّره متصفحك على DOM في النهاية، كما تحتوي بعض العناصر في هذا التعبير على سمات Attributes مكتوبةً كما تُكتَب في لغة HTML تمامًا باتباع النمط attribute="value"‎، في حين يحتوي وسم الفتح <div> على السمة className في السطر الثالث، وهي الخاصية class نفسها في لغة HTML، ولكن لا يمكننا استخدام الكلمة class، لأنّ صيغة JSX هي لغة جافاسكربت وهي كلمة محجوزة فيها، مما يعني أنّ لغة حافاسكربت تستخدِمها مسبقًا لغرض معيَّن، وقد يتسبّب استخدامها في شيفرتنا في حدوث مشاكل، كما تُكتَب بعض سمات HTML الأخرى بطريقة مختلفة في JSX عن تلك الموجودة في لغة HTML للسبب ذاته. عدِّل الوسم <p> في السطر السادس، بحيث يصبح "Hello, world!‎"، ثم احفظ ملفك، إذ ستلاحظ أن هذا التعديل سيُصيَّر مباشرةً في خادم التطوير الذي يعمل على المضيف المحلي http://localhost:3000 في متصفحك، ثم احذف بعد ذلك الوسم <a> واحفظ الملف، مما يؤدي إلى اختفاء رابط "Learn React"، إذ يجب أن يبدو المكوّن App الآن كما يلي: function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Hello, World! </p> </header> </div> ); } تعليمات التصدير تجعل تعليمة التصدير export default App في الجزء السفلي من الملف App.js المكوّنَ App متاحًا للوحدات الأخرى. الملف index.js لنفتح الملف src/index.js الذي يُعَدّ المكان الذي يُستخدَم فيه المكوّن App، وهو نقطة الدخول إلى تطبيقنا، إذ يبدو في البداية كما يلي: import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') ); // إذا أردت أن يعمل تطبيقك في وضع عدم الاتصال وأن يُحمَّل بسرعة، فيمكنك تغيير // ‫unregister()‎ إلى register()‎ في الأسفل، ولاحظ أنّ هذا يأتي مع بعض المخاطر. serviceWorker.unregister(); يبدأ الملف index.js باستيراد جميع وحدات JS والملفات الأخرى التي يحتاجها للعمل كما هو الحال مع الملف App.js، ويحتفظ الملف src/index.css بالتنسيقات العامة المطبَّقة على تطبيقنا بالكامل، كما يمكننا رؤية المكوّن App الذي استوردناه، فهو متاح للاستيراد بفضل تعليمة التصدير export في أسفل الملف App.js، في يستدعي السطر السابع الدالة ReactDOM.render()‎ مع وسيطين هما: المكوِّن الذي نريد تصييره، وهو <App /‎> في هذه الحالة. عنصر DOM الذي نريد تصيير المكوِّن ضمنه، وهو العنصر ذو المعرِّف root في هذه الحالة، فإذا نظرت ضمن الملف public/index.html، فستجد أن هذا العنصر هو <div> ضمن العنصر <body>. وهذا يعني أننا نريد تصيير تطبيق React الخاص بنا مع المكوِّن App بوصفه الجذر أو المكوِّن الأول. ملاحظة: يجب أن تحتوي مكونات React وعناصر HTML على شرطات إغلاق مائلة في صيغة JSX، إذ ستؤدي كتابة المكوّن <App> فقط أو الوسم <img> فقط إلى حدوث خطأ. تُعَدّ عمّال الخدمة Service workers أجزاءً مثيرةً من الشيفرة البرمجية التي تحسّن أداء التطبيق وتسمح لميزات تطبيقات الويب بالعمل في وضع عدم الاتصال، لكننا لن نتحدّث عنها في هذا المقال، إذ يمكنك حذف السطر الخامس ومعظم الشيفرة الموجودة أسفله، وهنا يجب أن يبدو ملف index.js النهائي كما يلي: import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; ReactDOM.render(<App />, document.getElementById('root')); المتغيرات والخاصيات سنستخدِم فيما يلي بعضًا من مهارات جافاسكربت لنتمكّن من تعديل المكوّنات والتعامل مع البيانات في React، إذ سنتحدث عن كيفية استخدام المتغيرات في JSX، وسنشرح الخاصيات Props التي تُعَدّ طريقةً لتمرير البيانات إلى المكوّن الذي يمكن الوصول إليه بعد ذلك باستخدام المتغيرات. المتغيرات في JSX لنركّز على السطر التاسع في الملف App.js: <img src={logo} className="App-logo" alt="logo" /> وُضِعت قيمة السمة src الخاصة بالوسم <img /‎> ضمن أقواس معقوصة، وهي الطريقة التي تتعرف بها صيغة JSX على المتغيرات، إذ تشير القيمة {logo} إلى استيراد الشعار logo في السطر الثاني من التطبيق، ثم استرداد ملف الشعار وتصييره، ولنحاول إنشاء متغير خاص بنا من خلال إضافة التعليمة const subject = 'React';‎ قبل تعليمة return في الدالة App، إذ يجب أن يبدو المكوّن App الآن كما يلي: function App() { const subject = "React"; return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Hello, World! </p> </header> </div> ); } غيّر السطر الثامن لاستخدام المتغير subject بدلًا من االكلمة "world" كما يلي: function App() { const subject = "React"; return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Hello, {subject}! </p> </header> </div> ); } يجب أن يعرض المتصفح التعليمة "Hello, React!‎" بدلًا من التعليمة "Hello, world!‎" عند الحفظ، ولا يستفيد المتغير الذي ضبطناه للتو استفادةً كبيرةً من ميزات React، لذلك نحتاج إلى استخدام الخاصيات Props. خاصيات المكون الخاصية هي البيانات الممرَّرة إلى مكوِّن React، كما تشبه الخاصيات إلى حد ما سمات HTML، ولكن تمتلك عناصر HTML سمات وتمتلك مكونات React خاصيات، إذ تُكتَب الخاصيات ضمن استدعاءات المكوِّن، وتستخدِم الصيغة نفسها التي تستخدمها سمات HTML وهي prop="value"‎، كما يكون تدفّق البيانات أحادي الاتجاه في React، إذ يمكن تمرير الخاصيات من المكوّنات الآباء إلى المكوّنات الأبناء فقط، وتكون الخاصيات للقراءة فقط، فلنفتح الملف index.js ونمنح المكوّن <App/‎> استدعاءه الأول، ثم أضف الخاصية subject إلى استدعاء المكوِّن <App/‎> مع القيمة Clarice، إذ يجب أن تبدو شيفرتك البرمجية كما يلي: ReactDOM.render(<App subject="Clarice" />, document.getElementById('root')); لنفتح الملف App.js ولننتقل إلى الدالة App()‎ التي يجب أن تكون كما يلي مع اختصار تعليمة return للإيجاز: function App() { const subject = "React"; return ( // ‫تعليمة return ); } عدّل الدالة App بحيث تقبل الخاصيات props على أساس معامِل لها، واحذف الثابت subject، كما يمكنك وضع الخاصيات props في التابع console.log()‎ لطباعتها على طرفية المتصفح كما يلي: function App(props) { console.log(props); return ( // ‫تعليمة return ); } احفظ ملفك وتحقق من طرفية جافاسكربت (نافذة console) في متصفحك، إذ يجب أن ترى شيئًا يشبه ما يلي: Object { subject: "Clarice" } تتوافق خاصية الكائن subject مع الخاصية subject التي أضفناها إلى استدعاء المكون <App /‎>، كما تتوافق سلسلة Clarice النصية مع قيمتها، إذ تُجمَع خاصيات المكوِّن في React دائمًا ضمن كائنات بهذه الطريقة، ولنستخدِم الخاصية subject في الملف App.js، لذا غيّر الثابت subject لقراءة قيمة props.subject بدلًا من تعريفه على أنه سلسلة React، كما يمكنك حذف التابع console.log()‎ إذا أردت ذلك. function App(props) { const subject = props.subject; return ( // تعليمة‫ return ); } يجب أن يعرض التطبيق عبارة "Hello, Clarice!‎" عند الحفظ، فإذا عدت إلى الملف index.js وعدّلت قيمة subject ثم حفظته، فسيتغيّر النص. الخلاصة تعرّفنا في هذا المقال على مكتبة React، بما في ذلك كيفية تثبيتها محليًا، وإنشاء تطبيق بسيط، وكيفية عمل الأساسيات؛ أما في المقال التالي، فسنبدأ بإنشاء أول تطبيق مناسب وهو تطبيق قائمة المهام، لكن لنلخّص بعض الأشياء التي تعلمناها حتى الآن. في React: يمكن للمكونات استيراد الوحدات التي تحتاجها ويجب أن تصدِّر نفسها في الجزء السفلي من ملفاتها. تُسمَّى دوال المكوِّن باستخدام حالة باسكال PascalCase. يمكنك قراءة متغيرات JSX بوضعها بين أقواس معقوصة مثل {so}. تختلف بعض سمات JSX عن سمات HTML بحيث لا تتعارض مع كلمات جافاسكربت المحجوزة، إذ تُترجَم class في لغة HTML إلى className في JSX مثلًا، ولاحظ أنّ السمات متعددة الكلمات تُسمَّى باستخدام حالة الجَمل camel-cased. تُكتَب الخاصيات تمامًا مثل السمات ضمن استدعاءات المكوِّن وتُمرَّر إلى المكوّنات. ترجمة -وبتصرُّف- للمقال Getting started with React. اقرأ أيضًا مدخل إلى React.js - مكتبة تطوير الواجهات الرسومية من فيس بوك مدخل إلى استعمال المكتبة React-Router اختبار تطبيقات React باستعمال Jest ومكتبة React Testing Library المصطلحات المستخدمة في React
  17. يملك كل إطار عمل جافاسكربت رئيسي نهجًا مختلف لتحديث نموذج كائن المستند DOM، ومعالجة أحداث المتصفح، وتوفير تجربة مطوِّر ممتعة، إذ سنستكشف في هذا المقال الميزات الرئيسية لأطر عمل "الأربعة الكبار"، وكيفية عمل هذه الأطر، والاختلافات بينها. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت. الهدف: فهم ميزات شيفرة أطر العمل Frameworks الرئيسية. لغات المجال المحدد Domain-specific Languages سنشغّل جميع أطر العمل التي سنناقشها في هذا المقال باستخدام لغة جافاسكربت، إذ ستتيح جميعها استخدام لغات المجال المحدد Domain-specific Languages -أو DSLs اختصارًا- لبناء التطبيقات، كما تستخدم مكتبة React صيغة JSX لكتابة مكوناتها، في حين يستخدِم إطار عمل Ember لغة Handlebars، كما تعرف هذه اللغات كيفية قراءة متغيرات البيانات على عكس لغة HTML، ويمكن استخدام هذه البيانات لتبسيط عملية كتابة واجهة المستخدِم؛ أما تطبيقات Angular، فتستخدِم لغة TypeScript التي لا تهتم بكتابة واجهات المستخدِم ولكنها لغة مجال محدد، وتختلف كثيرًا عن لغة جافاسكربت الصرفة Vanilla JavaScript. لا يستطيع المتصفح قراءة لغات DSL مباشرةً، لذلك يجب تحويلها إلى لغة جافاسكربت أو HTML أولًا، إذ يُعَدّ التحويل خطوةً إضافيةً في عملية التطوير، ولكن تتضمن أدوات إطار العمل الأدوات المطلوبة لمعالجة هذه الخطوة، أو يمكن تعديلها لتضمين هذه الخطوة، كما يمكن إنشاء تطبيقات إطار عمل دون استخدام هذه اللغات، لكن سيبسّط استخدامها عملية التطوير ويسهّل العثور على المساعدة من مجتمعات تلك الأطر. صيغة JSX يرمز الاختصار JSX إلى لغتي جافاسكربت و XML، ويُعَدّ امتدادًا للغة جافاسكربت، إذ يضيف صيغةً تشبه لغة HTML إلى بيئة جافاسكربت، وقد اخترع فريق React صيغة JSX لاستخدامها في تطبيقات React، ولكن يمكن استخدامها لتطوير تطبيقات أخرى مثل تطبيقات Vue، وإليك مثال بسيط لصيغة JSX: const subject = "World"; const header = ( <header> <h1>Hello, {subject}!</h1> </header> ); يمثِّل التعبير السابق عنصر <header> في لغة HTML وبداخله عنصر <h1>، إذ تخبر الأقواس المعقوصة حول subject في السطر الرابع التطبيق بقراءة قيمة الثابت subject وإدخاله في العنصر <h1>، في حين ستُصرَّف صيغة JSX من جزء الشيفرة السابق عند استخدامها مع إطار عمل React إلى ما يلي: var subject = "World"; var header = React.createElement("header", null, React.createElement("h1", null, "Hello, ", subject, "!") ); سينتج جزء الشيفرة السابق ما يلي في لغة HTML عندما يصيّره المتصفح في النهاية: <header> <h1>Hello, World!</h1> </header> لغة Handlebars لا تُعَدّ لغة القوالب Handlebars لغةً خاصةً بتطبيقات Ember، ولكنها تُستخدَم بكثرة معها، إذ تشبه شيفرة Handlebars لغة HTML، ولكن لديها خيار سحب البيانات من مكان آخر، إذ يمكن استخدام هذه البيانات للتأثير على ملفات HTML التي يبنيها التطبيق في النهاية، كما تستخدِم لغة Handlebars -مثل صيغة JSX- الأقواس المعقوصة لحقن قيمة متغير، ولكنها تستخدِم زوجًا مزدوجًا من الأقواس المعقوصة بدلًا من زوج واحد، وإليك قالب Handlebars التالي: <header> <h1>Hello, {{subject}}!</h1> </header> والبيانات التالية: { subject: "World" } ستبني لغة Handlebars جزء HTML التالي: <header> <h1>Hello, World!</h1> </header> لغة TypeScript تُعَدّ لغة TypeScript مجموعةً شاملةً من جافاسكربت، أي أنها توسّعها، إذ تُعَدّ كل شيفرات جافاسكربت صالحةً للغة TypeScript، ولكن العكس ليس صحيحًا، كما تُعَدّ لغة TypeScript مفيدةً للصرامة التي تسمح للمطورين بفرضها على شيفرتهم البرمجية مثل دالة add()‎ التي تأخذ الأعداد الصحيحة a وb وتعيد ناتج جمعهما، ويمكن كتابة هذه الدالة في لغة جافاسكربت على النحو التالي: function add(a, b) { return a + b; } قد تكون هذه الشيفرة بسيطةً جدًا بالنسبة لشخص اعتاد على استخدام جافاسكربت، ولكنها يمكن أن تكون أوضح، إذ تتيح لنا لغة جافاسكربت استخدام المعامل + لربط السلاسل مع بعضها بعضًا، لذلك ستعمل هذه الدالة أيضًا إذا كان a وb عبارة عن سلاسل نصية Strings، وبالتالي قد لا تمنحك النتيجة التي تتوقعها، فإذا أردنا السماح فقط بتمرير الأعداد إلى هذه الدالة، فستجعل لغة TypeScript ذلك ممكنًا كما يلي: function add(a: number, b: number) { return a + b; } يخبر النوع ‎: number المكتوب بعد كل معامِل في لغة TypeScript أنّ كلا المعامِلَين a وb يجب أن يكونا عددَين، فإذا أردنا استخدام هذه الدالة وتمرير القيمة '2' إليها بوصفها وسيطًا، فستعطي لغة TypeScript خطأً أثناء التصريف Compilation، وبالتالي سنضطر إلى إصلاح هذا الخطأ، كما يمكننا كتابة شيفرة جافاسكربت الخاصة بنا والتي تعطينا هذه الأخطاء، إلا أنها ستجعل شيفرتنا البرمجية أكثر تفصيلًا، إذ يمكن أن يكون السماح للغة TypeScript بمعالجة مثل هذه الفحوصات نيابةً عنا أمرًا منطقيًا. كتابة المكونات تحتوي معظم أطر العمل على نموذج مكونات، إذ يمكن كتابة مكونات React باستخدام صيغة JSX، ومكونات Ember باستخدام لغة Handlebars، ومكونات Angular وVue باستخدام صيغة القوالب التي توسّع لغة HTML قليلًا، كما توفِّر مكونات كل إطار عمل -بغض النظر عن كيفية كتابة المكونات- طريقةً لوصف الخاصيات الخارجية التي قد تحتاجها، والحالة الداخلية التي يجب أن يديرها المكوِّن، والأحداث التي يمكن للمستخدِم أن أن يتفاعل بها مع المكوِّن، كما سنعطي في هذا المقال أمثلةً من مقتطفات شيفرة React والتي ستُكتَب باستخدام صيغة JSX. الخاصيات Properties تُعَدّ الخاصيات Properties -أو props اختصارًا- بيانات خارجية يحتاجها المكوِّن من أجل تصييرها Render، ولنفترض أنك تنشئ موقعًا إلكترونيًا لمجلة على الإنترنت، وتحتاج إلى التأكُّد من أن كل كاتب مساهم يُنسَب له عمله، فيمكنك إنشاء مكوِّن AuthorCredit لكل مقال، إذ يحتاج هذا المكوِّن إلى عرض صورة شخصية للمؤلف وسطر قصير يحتوي على بعض المعلومات عنه، لذلك يحتاج المكوِّن AuthorCredit إلى قبول بعض الخاصيات من أجل معرفة الصورة المراد تصييرها والسطر القصير المطلوب طباعته، إذ يمكن أن يبدو تمثيل React للمكوِّن AuthorCredit كما يلي: function AuthorCredit(props) { return ( <figure> <img src={props.src} alt={props.alt} /> <figcaption>{props.byline}</figcaption> </figure> ); } تمثِّل {props.src} و{props.alt} و{props.byline} المكان الذي ستُدرَج فيه الخاصيات ضمن المكوِّن، إذ يمكن تصيير هذا المكوِّن من خلال كتابة الشيفرة التالية في المكان الذي نريده، والذي سيكون على الأرجح ضمن مكوِّن آخر: <AuthorCredit src="./assets/zelda.png" alt="Portrait of Zelda Schiff" byline="Zelda Schiff is editor-in-chief of the Library Times." /> مما يؤدي في النهاية إلى تصيير عنصر <figure> التالي في المتصفح مع بنيته المحدَّدة في المكوِّن AuthorCredit، ومحتواه المحدَّد في الخاصيات المدرجة في استدعاء المكوِّن AuthorCredit: <figure> <img src="assets/zelda.png" alt="Portrait of Zelda Schiff" > <figcaption> Zelda Schiff is editor-in-chief of the Library Times. </figcaption> </figure> الحالة State يُعَدّ وجود آلية قوية للتعامل مع الحالة مفتاحًا لإطار عمل فعّال، وقد يحتوي كل مكوِّن على بيانات يجب التحكم بحالتها، إذ ستستمر هذه الحالة بطريقة ما طالما أنّ المكوِّن قيد الاستخدام، ويمكن استخدام الحالة مثل الخاصيات للتأثير على كيفية تصيير المكوِّن، ولنفترض مثلًا وجود زر يحسب عدد مرات النقر فوقه، إذ يجب أن يكون هذا المكوِّن مسؤولًا عن تتبّع حالة العد count الخاصة به، ويمكن كتابته كما يلي: function CounterButton() { const [count] = useState(0); return ( <button>Clicked {count} times</button> ); } يُعَدّ useState()‎ خطاف React الذي سيتتبع قيمة بيانات أولية أثناء تحديثها عند إعطائه تلك القيمة، وستُصيَّر الشيفرة بدايةً كما يلي في المتصفح: <button>Clicked 0 times</button> يتتبّع استدعاء الخطاف useState()‎ الحالة count بطريقة قوية عبر التطبيق دون الحاجة إلى كتابة شيفرة لتنفيذ ذلك بنفسك. الأحداث Events تحتاج المكونات إلى طرق للاستجابة لأحداث المتصفح من أجل أن تكون تفاعلية، وبالتالي ستتمكن تطبيقاتنا من الاستجابة للمستخدِمين، إذ يوفّر كل إطار من أطر العمل صيغته الخاصة للاستماع إلى أحداث المتصفح، والتي تشير إلى أسماء أحداث المتصفح الأصيلة المكافِئة، إذ يتطلب الاستماع إلى حدث النقر click خاصيةً خاصةً هي onClick في React، ولنحدّث شيفرة CounterButton السابقة للسماح لها بحساب عدد النقرات كما يلي: function CounterButton() { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}>Clicked {count} times</button> ); } استخدمنا دالة useState()‎ إضافية لإنشاء دالة setCount()‎ خاصة يمكن استدعاؤها لتحديث قيمة count، إذ نستدعي هذه الدالة في السطر الرابع، ونضبط قيمة count على قيمتها الحالية مع إضافة 1 إليها. مكونات التنسيق Styling components يوفِّر كل إطار من أطر العمل طريقةً لتحديد تنسيقات لمكوناتك أو للتطبيق كله، إذ توفِّر جميعها طرقًا متعددةً لتعريف تنسيقات المكوِّن على الرغم من اختلاف نهج كل إطار عن الآخر، ويمكنك تصميم تطبيقات إطار العمل باستخدام Sass أو Less، أو تحويل Transpile ملفات تنسيقات CSS باستخدام PostCSS مع إضافة بعض الوحدات المساعِدة. التعامل مع الاعتماديات توفِّر جميع الأطر الرئيسية آليات للتعامل مع الاعتماديات Dependencies باستخدام مكوِّنات ضمن مكوّنات أخرى وبمستويات هرمية متعددة في بعض الأحيان، وستختلف آليات هذه الإطارات عن بعضها بعضًا، ولكن النتيجة النهائية هي نفسها كما هو الحال مع الميزات الأخرى، كما تميل المكوّنات إلى استيراد مكوّنات في مكوّنات أخرى باستخدام صيغة وحدة جافاسكربت المعيارية أو شيء آخر مشابه. مكونات ضمن مكونات أخرى تتمثَّل إحدى الفوائد الرئيسية لبنية واجهة المستخدِم القائمة على المكوّنات في أنه يمكن تكوين المكوّنات مع بعضها بعضًا، إذ يمكنك استخدام مكونات ضمن مكونات أخرى لبناء تطبيق ويب مثل كتابة وسوم HTML ضمن بعضها بعضًا لإنشاء موقع ويب، كما يتيح لك كل إطار عمل بكتابة مكوّنات تستخدِم وتعتمد على مكوّنات أخرى، كما يمكن استخدام مكوِّن React الذي هو AuthorCredit ضمن المكوِّن Article مثلًا، وهذا يعني حاجة المكوِّن Article إلى استيراد المكوِّن AuthorCredit. import AuthorCredit from "./components/AuthorCredit"; يمكن بعد ذلك استخدام المكوِّن AuthorCredit ضمن المكوِّن Article كما يلي: ... <AuthorCredit /> … حقن الاعتماديات تشتمل التطبيقات الواقعية على بنى مكونات ذات مستويات متعددة من التداخل Nesting في أغلب الأحيان، وقد يحتاج مكوِّن AuthorCredit المتداخل بعمق في العديد من المستويات لسبب ما إلى بيانات من المستوى الجذر لتطبيقنا، ولنفترض تنظيم موقع المجلة الذي نبنيه على النحو التالي: <App> <Home> <Article> <AuthorCredit {/* props */} /> </Article> </Home> </App> يحتوي المكوِّن App على البيانات التي يحتاجها المكوِّن AuthorCredit، كما يمكننا إعادة كتابة المكوِّن Home وArticle لمعرفة كيفية تمرير الخاصيات، ولكن قد يكون ذلك مملًا إذا كان هناك العديد من المستويات بين أصل ووجهة البيانات، كما قد يؤثر ذلك على الأداء، فلا يستخدِم المكوِّنان Home وArticle صورة المؤلف أو السطر القصير الذي يعطي معلومات مختصَرةً عن المؤلف، ولكن إذا أردنا الحصول على هذه المعلومات في المكوِّن AuthorCredit، فينحتاج إلى تغيير المكوِّنَين Home وArticle لإضافتها. تُسمَّى مشكلة تمرير البيانات عبر العديد من طبقات المكوّنات بتمرير الخاصيات Prop Drilling التي لا تُعَدّ مثاليةً للتطبيقات الكبيرة، إذ يمكن التحايل على هذه المشكلة من خلال توفير أطر العمل وظيفة تُعرَف باسم حقن الاعتمادية Dependency Injection، وهي طريقة لإعطاء بيانات معينة مباشرةً إلى المكوّنات التي تحتاجها دون تمريرها عبر المستويات المتداخلة، إذ ينفّذ كل إطار عمل عملية حقن الاعتمادية تحت اسم مختلف وبطريقة مختلفة، ولكن التأثير هو نفسه في النهاية. يسمّي إطار العمل Angular هذه العملية حقن الاعتمادية، في حين يمتلك إطار العمل Vue توابع المكوّنات provide()‎ وinject()‎؛ أما React، فيحتوي على واجهة برمجة تطبيقات السياق Context API، بينما يشارك إطار عمل Ember الحالة من خلال خدمات. دورة الحياة Life Cycle تُعَدّ دورة حياة المكوّن في سياق إطار العمل مجموعةً من المراحل التي يمر بها المكوِّن من وقت إلحاقه بنموذج DOM ثم تصييره بواسطة المتصفح -والذي يدعى بالتركيب Mounting في أغلب الأحيان- إلى وقت إزالته من نموذج DOM -والذي يطلَق عليه التفكيك Unmounting في أغلب الأحيان، كما يسمّي كل إطار عمل مراحل دورة الحياة هذه بطريقة مختلفة، ولا تمنح جميعها المطورين الوصول إلى المراحل نفسها، كما تتبع جميع الأطر النموذج العام نفسه، إذ تسمح للمطورين بتنفيذ إجراءات معينة عند تركيب Mount المكوِّن، وعند تصييره، وعند تفكيكه Unmount، وفي عدة مراحل بينها. تُعَدّ مرحلة التصيير Render المرحلة الأهم، لأنها تتكرر عندما يتفاعل المستخدِم مع تطبيقك، وتُشغَّل في كل مرة يحتاج فيها المتصفح إلى تصيير شيء جديد، سواءً كانت هذه المعلومات الجديدة إضافةً إلى ما هو موجود في المتصفح أو حذفه أو تعديله، كما يمكنك الاطلاع على هذا الرسم البياني لدورة حياة مكون React الذي يوضِّح هذا المفهوم. تصيير العناصر تتخِذ أطر العمل أساليبًا مختلفةً ولكنها متشابهة لتصيير التطبيقات، وتتعقّب جميعها الإصدار الحالي المُصيَّر من DOM في متصفحك، كما تتخذ كل منها قرارات مختلفةً قليلًا حول كيفية تغيير نموذج DOM مثل إعادة تصيير المكوّنات في تطبيقك، وبما أنّ أطر العمل تتخِذ هذه القرارات نيابةً عنك، فهذا يعني أنك لا تتفاعل مع نموذج DOM بنفسك، كما يُعَدّ هذا التجريد البعيد عن نموذج DOM أكثر تعقيدًا واستهلاكًا للذاكرة من تحديثه بنفسك، ولكن لا يمكن لأطر العمل بدونه السماح لك بالبرمجة بالطريقة التصريحية المعروفة بها. يُعَدّ نموذج DOM الافتراضي Virtual DOM نهجًا يمكن من خلاله تخزين معلومات حول نموذج DOM في متصفحك ضمن ذاكرة جافاسكربت، إذ يحدّث تطبيقك هذه النسخة من DOM، ثم يوازنها مع DOM الحقيقي المصيَّر لمستخدِميك فعليًا لتحديد ما سيُصيَّر، كما ينشئ التطبيق اختلافًا Diff لموازنة الاختلافات بين DOM الافتراضي المُحدَّث و DOM المُصيَّر حاليًا، إذ يُستخدَم هذا الاختلاف لتطبيق التحديثات على نموذج DOM الحقيقي، كما يستخدِم كل من React وVue نموذج DOM الافتراضي، لكنهما لا يطبِّقان المنطق نفسه بالضبط عند تطبيق الاختلاف Diffing أو التصيير Rendering، ويمكنك قراءة المزيد عن DOM الافتراضي في توثيق React على موسوعة حسوب. يشبه نموذج DOM التزايدي Incremental DOM نموذج DOM الافتراضي Virtual DOM في أنه ينشئ اختلافًا في نموذج DOM لتحديد ما سيُصيَّر، إلا أنه يختلف في عدم إنشائه نسخةً كاملةً من DOM في ذاكرة جافاسكربت، وهو يتجاهل أجزاء DOM التي لا تحتاج إلى تغيير، فإطار العمل Angular هو الإطار الوحيد الذي ناقشناه حتى الآن والذي يستخدِم نموذج DOM التزايدي، كما يمكنك قراءة المزيد حول نموذج DOM التزايدي على مدونة Auth0. آلة Glimmer الافتراضية خاصة بإطار عمل Ember، ولا تُعَدّ نموذج DOM افتراضي أو DOM تزايدي، وإنما هي عملية منفصلة يمكن من خلالها تحويل قوالب Ember إلى نوع من شيفرة ثنائية Byte Code تكون أسهل وأسرع في القراءة من جافاسكربت. التوجيه Routing يُعَدّ التوجيه جزءًا مهمًا من تجربة الويب، إذ يوفِّر كل إطار عمل مكتبةً أو أكثر بحيث تساعد المطورين على تنفيذ التوجيه من جانب العميل في تطبيقاتهم، لتجنّب تجربة معطَّلة في التطبيقات المعقدة ذات المشاهدات الكثيرة. الاختبار Testing تستفيد جميع التطبيقات من تغطية الاختبار التي تضمن استمرار برنامجك في التصرف بالطريقة التي تتوقعها، إذ يوفر النظام المجتمعي لكل إطار عمل الأدوات التي تسهّل كتابة الاختبارات، ولا تُضمَّن أدوات الاختبار في الأطر نفسها، ولكن تمنحك أدوات واجهة سطر الأوامر المُستخدَمة لإنشاء تطبيقات إطار العمل، الوصول إلى أدوات الاختبار المناسبة، إذ يحتوي كل إطار على أدوات واسعة النطاق في نظامه المجتمعي مع إمكانات اختبار الوحدة والتكامل على حد سواء. تُعَدّ مكتبة الاختبار Testing Library مجموعةً من أدوات الاختبار المساعِدة التي تحتوي على أدوات للعديد من بيئات جافاسكربت بما في ذلك React وVue وAngular، ويغطي توثيق Ember اختبار تطبيقاته، وإليك اختبار سريع للمكوِن CounterButton مكتوب بمساعَدة مكتبة اختبار React، إذ يختبر هذا الاختبار عددًا من الأشياء مثل وجود الزر وما إذا كان الزر يعرض النص الصحيح بعد النقر عليه 0 و1 و2 مرة: import React from "react"; import { render, fireEvent } from "@testing-library/react"; import "@testing-library/jest-dom/extend-expect"; import CounterButton from "./CounterButton"; it("renders a semantic with an initial state of 0", () => { const { getByRole } = render(<CounterButton />); const btn = getByRole("button"); expect(btn).toBeInTheDocument(); expect(btn).toHaveTextContent("Clicked 0 times"); }); it("Increments the count when clicked", () => { const { getByRole } = render(<CounterButton />); const btn = getByRole("button"); fireEvent.click(btn); expect(btn).toHaveTextContent("Clicked 1 times"); fireEvent.click(btn); expect(btn).toHaveTextContent("Clicked 2 times"); }); الخلاصة يجب أن يكون لديك الآن مزيدًا من الأفكار حول اللغات والميزات والأدوات الفعلية التي ستستخدِمها أثناء إنشاء التطبيقات باستخدام أطر العمل، ولا بدّ أنك متحمس للبدء بكتابة الشيفرة، وهذا ما ستفعله لاحقًا، ولكن يمكنك الآن اختيار إطار العمل الذي ترغب في بدء تعلمه أولًا مثل: React Ember Vue Svelte Angular ترجمة -وبتصرُّف- للمقال Framework main features. اقرأ أيضًا مقدمة إلى أطر عمل تطوير الويب من طرف العميل فهم أدوات تطوير الويب من طرف العميل بناء نموذج كامل لسلسلة أدوات تطوير الويب من طرف العميل
  18. سنبدأ مقالنا بإلقاء نظرة على تاريخ لغة جافاسكربت JavaScript وأطر العمل Frameworks، وسبب وجود هذه الأطر وفوائدها، وكيفية اختيار إطار عمل، وما هي البدائل المتاحة لأطر العمل من طرف العميل. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML و CSS و جافاسكربت. الهدف: فهم أطر عمل جافاسكربت من طرف العميل والمشاكل التي تحلها وبدائلها وكيفية اختيارها. لمحة تاريخية ظهرت لغة جافاسكربت لأول مرة في عام 1996 والتي أضافت تفاعلًا إلى الويب الذي احتوى على مستندات ثابتة سابقًا، وبالتالي أصبح الويب مكانًا لفعل أشياء، وليس مجرد مكان لقراءة تلك الأشياء، كما زادت شعبية جافاسكربت، كما كتب المطورون الذين عملوا معها، أدوات لحل المشاكل التي واجهوها، وجمّعوها في حزم قابلة لإعادة الاستخدام سميّت بمكتبات Libraries ليتمكنوا من مشاركة حلولهم مع بعضهم البعض، إذ ساعد هذا النظام المجتمعي المشترك للمكتبات في تشكيل نمو الويب. تُعَدّ لغة جافاسكربت الآن جزءًا أساسيًا من الويب، وتُستخدَم في 95% من مواقع الويب، كما أصبح المستخدِمون يكتبون أوراقًا papers، ويديرون ميزانياتهم، ويستمعون إلى الموسيقى، ويشاهدون الأفلام، ويتواصلون مع بعضهم البعض عن بعد فوريًا من خلال الدردشة النصية أو الصوتية أو المرئية، وبالتالي أتاح الويب تنفيذ أمور كانت ممكنةً سابقًا فقط في التطبيقات الأصيلة المثبَّتة على حواسيبنا، إذ يُشار إلى هذه المواقع الحديثة والمعقَّدة والتفاعلية باسم تطبيقات الويب Web Applications. أدى ظهور أطر عمل جافاسكربت الحديثة إلى تسهيل إنشاء تطبيقات تفاعلية وديناميكية، فإطار العمل Framework هو عبارة عن مكتبة تقدّم آراءً لبناء البرمجيات، إذ تسمح هذه الآراء بإمكانية التنبؤ والتجانس في التطبيق، فالقدرة على التنبؤ تسمح للبرمجيات بالتوسع إلى حجم هائل مع بقائها قابلةً للصيانة، إذ تُعَدّ القدرة على التنبؤ وقابلية الصيانة أمران ضروريان لصحة البرمجيات وطول عمرها. تعمل أطر عمل جافاسكربت على تشغيل الكثير من البرمجيات الرائعة على الويب الحديث، بما في ذلك العديد من مواقع الويب التي يُحتمَل استخدامها كل يوم، إذ تستخدِم صفحة توثيق الويب في MDN مثلًا إطار عمل React/ReactDOM لتشغيل واجهتها الأمامية، وتوجد هناك أطر عمل متعددة، ولكن "الأربعة الكبار" هي: إمبر Ember أُصدِر إطار عمل Ember في ديسمبر كانون الأول عام 2011 على أساس استمرار للعمل الذي بدأ في مشروع SproutCore، ويُعَدّ Ember إطار عمل قديم به عدد مستخدِمين أقل من البدائل الحديثة مثل React وVue، لكنه لا يزال يتمتع بقدر لا بأس به من الشعبية نظرًا لاستقراره ودعم المجتمع وبعض مبادئ البرمجة الذكية. Angular هو إطار عمل لتطبيق ويب مفتوح المصدر بقيادة فريق أنجولار Angular في جوجل ومجتمع من الأفراد والشركات، إذ نتج Angular عن إعادة كتابة AngularJS بالكامل من الفريق نفسه الذي بناه وقد أُصدِر أنجولار رسميًا في 14 سبتمبر أيلول من عام 2016، وهي إطار عمل قائم على المكونات، وتستخدِم قوالب HTML التصريحية Declarative، كما يترجم مصرّف إطار العمل القوالب إلى تعليمات جافاسكربت محسَّنة في وقت البناء وبشفافية عن المطورين، تستخدِم أنجولار لغة TypeScript، وهي مجموعة شاملة من لغة جافاسكربت التي سنلقي نظرةً عليها بمزيد من التفصيل لاحقًا. Vue أصدَر إيفان يو Evan You لأول مرة إطار عمل Vue في عام 2014 بعد العمل والتعلم من مشروع AngularJS الأصلي، إذ يُعَدّ Vue الأصغر بين الأربعة الكبار، لكنه تمتَّع مؤخرًا بشعبية متزايدة، كما يوسّع إطار عمل Vue مثل AngularJS لغة HTML بشيفرته، وهو يعتمد بصورة أساسية على لغة جافاسكربت المعيارية الحديثة. React أصدَرت شركة فيسبوك مكتبة React في عام 2013، إذ استخدِمت React قبل ذلك في حل العديد من مشاكلها داخليًا، ولا تُعَدّ React نفسها إطار عمل، وإنما مكتبةً لتصيير Rendering مكونات واجهة المستخدِم، كما تُستخدَم React جنبًا إلى جنب مع المكتبات الأخرى لإنشاء التطبيقات، إذ تُمكِّن React وReact Native المطورين من إنشاء تطبيقات للهاتف المحمول، في حين تمكِّن React وReactDOM المطورين من إنشاء تطبيقات الويب، وتُعرَف React بوصفها إطار عمل جافاسكربت نظرًا لاستخدام React وReactDOM معًا في كثير من الأحيان، كما توسّع React لغة جافاسكربت بصيغة تشبه لغة HTML، إذ تُعرَف هذه الصيغة باسم JSX. سبب وجود أطر العمل ناقشنا البيئة التي ألهمت إنشاء أطر العمل، ولكننا لم نناقش السبب الحقيقي لحاجة المطورين إلى إنشائها، إذ يتطلب استكشاف الأسباب فحص تحديات تطوير البرمجيات أولًا. ضع في الحسبان نوعًا شائعًا من التطبيقات، وهو مُنشئ قائمة المهام To-do List Creator التي سننفِّذها باستخدام مجموعة متنوعة من أطر العمل لاحقًا، إذ يجب أن يسمح هذا التطبيق للمستخدِمين بتطبيق بعض الأمور مثل عرض قائمة المهام وإضافة مهمة جديدة وحذف مهمة، كما يجب أن ينفّذ ذلك أثناء تتبّع وتحديث بيانات التطبيق الأساسية بصورة موثوقة، إذ تُعرَف هذه البيانات الأساسية بالحالة State في تطوير البرمجيات. هذه الأهداف بسيطة من الناحية النظرية بمعزل عن أهداف الأخرى، إذ يمكننا تكرار البيانات لتصييرها، كما يمكننا إضافة كائن لعمل مهمة جديدة، واستخدام معرِّف Identifier للعثور على مهمة أو تعديلها أو حذفها، إذ يجب أن يسمح التطبيق للمستخدِم بتطبيق كل هذه الأشياء من خلال المتصفح، ولكن قد تبدأ بعض المشاكل في الظهور، إلا أنّ المشكلة الحقيقية هي الحاجة إلى تحديث واجهة المستخدِم المناسبة في كل مرة نغيّر فيها حالة تطبيقنا، إذ يمكننا فحص صعوبة هذه المشكلة من خلال النظر إلى ميزة واحدة فقط من تطبيق قائمة مهام وهي عرض قائمة المهام. تغيرات DOM المطولة يستغرق إنشاء عناصر HTML وتصييرها في المتصفح في الوقت المناسب قدرًا كبيرًا من الشيفرة، ولنفترض أنّ حالتنا هي مصفوفة من الكائنات كما يلي: const state = [ { id: 'todo-0', name: 'Learn some frameworks!' } ] يمكنك التساؤل عن كيفية عرض إحدى هذه المهام للمستخدِم، إذ نريد تمثيل كل مهمة بعنصر قائمة، أي العنصر <li> في لغة HTML ضمن عنصر القائمة غير المرتبة <ul> كما يلي: function buildTodoItemEl(id, name) { const item = document.createElement('li'); const span = document.createElement('span'); const textContent = document.createTextNode(name); span.appendChild(textContent); item.id = id; item.appendChild(span); item.appendChild(buildDeleteButtonEl(id)); return item; } استخدمنا التابع document.createElement()‎ لإنشاء العنصر <li> والعديد من أسطر الشيفرة لإنشاء الخصائص والعناصر الأبناء التي يحتاجها، في حين يشير جزء الشيفرة التالي إلى دالة بناء أخرى هي buildDeleteButtonEl()‎، والتي تتبع نمطًا مشابهًا للنمط الذي استخدمناه لبناء عنصر القائمة: function buildDeleteButtonEl(id) { const button = document.createElement('button'); const textContent = document.createTextNode('Delete'); button.setAttribute('type', 'button'); button.appendChild(textContent); return button; } لا ينفِّذ هذا الزر أيّ شيء حتى الآن، ولكنه سينفِّذ شيئًا ما لاحقًا عندما نقرِّر تنفيذ ميزة الحذف، كما يمكن أن تقرأ الشيفرة التي ستصيّر العناصر على الصفحة شيئًا كما يلي: function renderTodoList() { const frag = document.createDocumentFragment(); state.tasks.forEach(task => { const item = buildTodoItemEl(task.id, task.name); frag.appendChild(item); }); while (todoListEl.firstChild) { todoListEl.removeChild(todoListEl.firstChild); } todoListEl.appendChild(frag); } لدينا الآن أكثر من ثلاثين سطرًا من الشيفرة المخصَّصة لواجهة المستخدِم فقط -أي إلى خطوة تصيير شيء ما في DOM- دون إضافة أصناف Classes التي يمكننا استخدامها لاحقًا لتصميم عناصر القائمة، كما يتطلب العمل مباشرةً مع نموذج DOM فهم أشياء كثيرة حول كيفية عمله مثل كيفية إنشاء العناصر، وتغيير خصائصها، وكيفية وضع العناصر ضمن بعضها البعض، والحصول عليها على الصفحة، فلا تعالج هذه الشيفرة تفاعلات المستخدِم أو إضافة مهمة أو حذفها، فإذا أضفنا هذه الميزات، فيجب علينا تذكّر تحديث واجهة المستخدِم في الوقت المناسب وبالطريقة الصحيحة. أُنشِئت أطر عمل جافاسكربت لتسهيل هذا النوع من العمل، إذ أُوجِدت لتوفير تجربة مطوِّر أفضل، فهي لا تضيف ميزات جديدة إلى جافاسكربت، وإنما تمنحك وصولًا أسهل لميزاتها لتتمكّن من بناء تطبيقات ويب بطريقة عصرية، فإذا أردت رؤية نماذج شيفرة هذا المقال عمليًا، فيمكنك التحقق من إصدار عامل من التطبيق على CodePen الذي يسمح للمستخدِمين بإضافة مهام جديدة وحذفها. طريقة أخرى لبناء واجهات المستخدم توفّر إطارات عمل جافاسكربت طريقةً لكتابة واجهات المستخدِم بطريقة تصريحية، أي أنها تسمح بكتابة الشيفرة التي توضِّح كيف يجب أن تبدو واجهة المستخدِم، كما يحقّق إطار العمل ذلك ضمن نموذج DOM في الخلفية، وكان فهم منهج جافاسكربت الصرفة Vanilla JavaScript لإنشاء عناصر DOM جديدة بطريقة تكرارية في لمح البصر أمرًا صعبًا، لكن يوضِّح الجزء التالي من الشيفرة الطريقة التي يمكنك من خلالها استخدام إطار عمل Vue لوصف قائمة من المهام: <ul> <li v-for="task in tasks" v-bind:key="task.id"> <span>{{task.name}}</span> <button type="button">Delete</button> </li> </ul> يختصر جزء الشيفرة السابق ما يقرب من اثنين وثلاثين سطرًا من الشيفرة في ستة أسطر، فإذا كانت الأقواس المعقوصة وسمات v-‎ غير مألوفة لك، فلا بأس بذلك، إذ سنتعرّف على الصيغة التي يستخدِمها إطار عمل Vue لاحقًا، كما تشبه الشيفرة السابقة واجهة المستخدِم التي تمثِّلها، في حين لا تشبه شيفرة جافاسكربت الصرفة ذلك. يمكن عدم كتابة دوالنا لبناء واجهة المستخدم بفضل إطار عمل Vue الذي سيتعامل مع ذلك بطريقة مثلى وفعالة، فدورنا الوحيد هو شرحنا لإطار عمل Vue الشكل الذي يجب أن يبدو عليه كل عنصر، كما يمكن للمطورين الذين هم على دراية بإطار عمل Vue الانضمام إلى مشروعنا والعمل بسرعة على ما يجري، فاستعمال إطار العمل Vue -وأي إطار عمل آخر- يحسّن كفاءة الفريق وأعضائه. يمكن تطبيق أشياء مشابهة في لغة جافاسكربت الصرفة، إذ تسهّل سلاسل القالب الحرفية Template literal strings كتابة سلاسل HTML التي تمثِّل الصورة التي سيبدو عليها العنصر الأخير، وقد يكون ذلك فكرةً مفيدةً لشيء بسيط مثل تطبيق قائمة المهام، ولكنه ليس قابلًا للصيانة للتطبيقات الكبيرة التي تتعامل مع آلاف سجلات البيانات، كما يمكن تصيير العديد من العناصر الفريدة في واجهة المستخدِم. فوائد أطر العمل الأخرى لنلقِ نظرةً على بعض المزايا الأخرى التي تمنحنا إياها أطر العمل، كما يمكن تحقيق مزايا الأطر في لغة جافاسكربت الصرفة، ولكن يزيح استخدام إطار العمل عبء ضرورة حل هذه المشاكل بأنفسنا. الأدوات يمتلك كل إطار من أطر العمل مجتمع مطورين كبير ونشط، لذلك يوفِّر النظام المجتمعي لكل إطار الأدوات التي تعمل على تحسين تجربة المطوِّر، إذ تسهّل هذه الأدوات إضافة أشياء مثل الاختبار للتأكد من عمل تطبيقك كما ينبغي، أو تدقيق الصياغة Linting للتأكد من خلو شيفرتك البرمجية من الأخطاء متجانسها من حيث الأسلوب. التجزئة Compartmentalization تشجع معظم الأطر الرئيسية المطورين على تجريد الأجزاء المختلفة من واجهات المستخدِم إلى مكونات Components، إذ تُعَدّ هذه المكونات أجزاءً من شيفرة برمجية قابلة للصيانة وإعادة الاستخدام ويمكنها التواصل مع بعضها بعضًا، كما يمكن وضْع الشيفرة المتعلقة بمكوّن معيّن في ملف واحد أو ملفين محدَّدين، بحيث يعرف المطوِّر بالضبط إلى أين يذهب لإجراء تغييرات على هذا المكوّن، في حين سيتعيّن عليك في تطبيق مكتوب بلغة جافاسكربت الصرفة إنشاء مجموعة اصطلاحات لتحقيق ذلك بطريقة فعالة وقابلة للتوسيع، كما يمكن انتهاء المطاف بالعديد من مطوري جافاسكربت بنشر الشيفرة البرمجية المتعلقة بجزء واحد من واجهة المستخدِم في جميع أنحاء الملف أو في ملف آخر تمامًا. التوجيه Routing يتيح الويب للمستخدمين التنقل من صفحة إلى أخرى، إذ يُعَدّ شبكةً من الوثائق المترابطة، فإذا ضغطتَ على رابط في موقع الويب هذا، فسيتصل متصفحك بخادم ما ويجلب محتوًى جديدًا لعرضه لك، وبالتالي سيتغير عنوان URL في شريط العنوان، ويمكنك حفظ عنوان URL الجديد والعودة إلى الصفحة لاحقًا، أو مشاركته مع الآخرين ليتمكنوا من العثور على الصفحة نفسها بسهولة، كما يتذكر متصفحك سجل التنقل ويسمح لك بالتنقل ذهابًا وإيابًا، وهذا ما يسمى بالتوجيه من طرف الخادم Server Side Routing. لا تجلب تطبيقات الويب الحديثة عادةً ملفات HTML الجديدة لتصييرها، وإنما تحمّل صفحة HTML واحدةً وتحدِّث نموذج DOM ضمنها باستمرار -ويشار إليها باسم تطبيقات الصفحة الواحدة Single Page Apps أو SPAs اختصارًا- دون انتقال المستخدِمين إلى عناوين جديدة على الويب، كما يطلَق عادةً على كل صفحة ويب وهمية Pseudo-Webpage جديدة اسم عرض View دون إجراء أيّ توجيه. إذا كان تطبيق SPA معقدًا بدرجة كافية ويصيِّر عددًا كافيًا من العروض الفريدة، فيجب إدخال وظائف التوجيه في تطبيقك، وقد اعتاد الناس على القدرة على الارتباط بصفحات معينة في التطبيقات، والانتقال ذهابًا وإيابًا في سجل التنقّل وما إلى ذلك، ولكن تتأثر تجربتهم عند تعطل ميزات الويب المعيارية هذه، فإذا تعامل تطبيق عميل مع التوجيه بهذه الطريقة، فإنه يسمى بالتوجيه من طرف العميل Client Side Routing، كما يمكن إنشاء موجّه باستخدام إمكانيات جافاسكربت والمتصفح الأصيلة، لكن الأطر الشائعة والمطوّرة بطريقة نشطة لها مكتبات مرافقة تجعل التوجيه جزءًا أسهل في عملية التطوير. أمور يجب مراعاتها عند استخدام الأطر يفضِّل مطور الويب الفعال استخدام أنسب الأدوات لكل عمل، كما تسهّل أطر عمل جافاسكربت تطوير التطبيقات الأمامية، لكنها ليست حلًا سحريًا لحل جميع المشاكل، إذ سنوضِّح فيما يلي الأمور التي يجب مراعاتها عند استخدام الأطر، وضَع في الحسبان أنك قد لا تحتاج إلى إطار عمل إطلاقًا. معرفة كيفية استخدام الأداة تستغرق الأطر وقتًا للتعلم تمامًا مثل لغة جافاسكربت الصرفة، لذلك تأكد من امتلاكك الوقت لتعلّم ما يكفي من ميزات إطار العمل قبل أن تقرر استخدامه لمشروع ما ليكون مفيدًا لك بدلًا من أن يعيقك، وتأكد من راحة زملائك في الفريق في التعامل معه أيضًا. الهندسة الفائقة Overengineering إذا كان مشروع تطوير الويب ملفًا شخصيًا يتكون من بضع صفحات، وكانت هذه الصفحات ذات قدرة تفاعلية قليلة أو معدومة، فقد لا يكون إطار العمل وجافاسكربت ضروريين إطلاقًا، إذ لا تُعَدّ أطر العمل وحدةً مترابطةً، فبعضها أكثر ملاءمةً للمشاريع الصغيرة من غيرها، إذ كتبت سارة دراسنر Sarah Drasner في مقال لمجلة Smashing Magazine عن كيفية استبدال Vue بـ jQuery بوصفها أداةً لجعل أجزاء صغيرة من صفحة ويب تفاعلية. قاعدة شيفرة أكبر وتجريد أكبر تسمح لك أطر العمل بكتابة المزيد من الشيفرة البرمجية التصريحية -وأحيانًا مقدار أقل من الشيفرة البرمجية- من خلال التعامل مع تفاعلات DOM نيابةً عنك في الخلفية، ويُعَدّ هذا التجريد رائعًا لتجربتك بوصفك مطورًا، ولكنه ليس مجانيًا، إذ يجب على أطر العمل تشغيل شيفرتها البرمجية لترجمة ما تكتبه إلى تغييرات DOM، والتي بدورها تجعل الجزء الأخير من البرنامج أكبر وأكثر تكلفةً. تُعَدّ الشيفرة البرمجية الإضافية أمرًا لا مفر منه، وسيسمح لك إطار العمل الذي يدعم تقنية هز الشجرة Tree-Shaking -أي إزالة أي شيفرة غير مُستخدَمة فعليًا في التطبيق أثناء عملية البناء- بالحفاظ على تطبيقاتك صغيرة، ولكن ذلك لا يزال عاملًا يجب وضعه في الحسبان عند التفكير في أداء تطبيقك، خاصةً على الأجهزة المقيَّدة بالشبكة أو بالتخزين مثل الهواتف المحمولة. لا يؤثِّر تجريد الأطر على شيفرة جافاسكربت فحسب، وإنما يؤثِّر على علاقتك بطبيعة الويب ذاتها، فنتيجة تطبيقك النهائية أو الطبقة التي يتفاعل معها المستخدِمون في النهاية هي HTML بغض النظر عن كيفية بنائك لتطبيق الويب، إذ يمكن أن تجعلك كتابة تطبيقك بالكامل باستخدام لغة جافاسكربت غافلًا عن HTML والغرض من وسومها المختلفة، وتقودك إلى إنتاج مستند HTML غير دلالي Un-semantic ولا يمكن الوصول إليه، إذ يمكن كتابة تطبيق هش يعتمد كليًا على جافاسكربت ولن يعمل بدونه. ليست الأطر مصدر مشاكلنا، إذ يمكن أن يكون أيّ تطبيق هشًا ومتضخمًا ولا يمكن الوصول إليه مع وجود أولويات خاطئة، ولكن يُعَدّ تضخم أطر العمل من أولويات المطورين، فإذا كانت أولوياتك هي إنشاء تطبيق ويب معقَّد، فيمكنك تنفيذ ذلك بسهولة، في حين إذا كانت أولوياتك هي عدم حماية الأداء وإمكانية الوصول، فستزيد أطر العمل من هشاشة تطبيقك وتضخمه وعدم إمكانية الوصول إليه، وقد أدّت أولويات المطور الحديثة التي ضخَّمتها أطر العمل، إلى قلب بنية الويب في مواضع مختلفة، إذ يضع الويب الآن جافاسكربت أولًا وتجربة المستخدِم أخيرًا في أغلب الأحيان بدلًا من إنشاء شبكة مستندات قوية وتعتمد على المحتوى. إمكانية الوصول على شبكة ويب مقادة بأطر العمل تتطلب إمكانية الوصول إلى واجهات المستخدِم بعض التفكير والجهد دائمًا، ويمكن أن تؤدي الأطر إلى تعقيد هذه العملية، إذ يجب استخدام واجهات برمجة تطبيقات إطار عمل متقدمة في أغلب الأحيان للوصول إلى ميزات المتصفح الأصيلة مثل مناطق ARIA الحية أو إدارة التركيز. تخلق تطبيقات إطار العمل في بعض الحالات حواجز وصول غير موجودة في المواقع التقليدية مثل التوجيه من طرف العميل كما ذكرنا سابقًا، في حين تكون للتنقل عبر الويب باستخدام التوجيه التقليدي من طرف الخادم نتائج يمكن التنبؤ بها، إذ يعرف المتصفح كيفية ضبط التركيز على الجزء العلوي من الصفحة وستعلن التقنيات المساعدة عن عنوان الصفحة، إذ تحدث هذه الأمور في كل مرة تنتقل فيها إلى صفحة جديدة. لا يحمّل متصفحك صفحات ويب جديدة باستخدام التوجيه من طرف العميل، لذلك لا يعرف أنه يجب عليه ضبط التركيز تلقائيًا أو الإعلان عن عنوان صفحة جديد، وقد كرّس مؤلفو أطر العمل وقتًا وعملًا هائلَين لكتابة شيفرة جافاسكربت التي تعيد إنشاء هذه الميزات، ولكن لم يطبّق أيّ إطار عمل ذلك بطريقة مثالية، وبذلك يجب عليك التفكير في إمكانية الوصول منذ بداية كل مشروع ويب، وضع في الحسبان أنه من المرجَّح معاناة قواعد الشيفرة المجردة التي تستخدِم الأطر من مشاكل الوصول الرئيسية إذا لم تفعل ذلك. كيفية اختيار إطار العمل يتّخذ كل إطار من أطر العمل التي ناقشناها سابقًا مناهج مختلفةً لتطوير تطبيقات الويب، إذ يتحسّن كل منها أو يتغير بانتظام، ولكل منها إيجابياته وسلبياته، كما يُعَدّ اختيار إطار العمل الصحيح عمليةً تعتمد على الفريق والمشروع، إذ يجب عليك إجراء بحث للكشف عمّا يناسب احتياجاتك، ولكننا حدّدنا بعض الأسئلة التي يمكنك طرحها من أجل البحث في خياراتك بفعالية أكبر، وهي كما يلي: ما المتصفحات التي يدعمها إطار العمل؟ ما اللغات الخاصة بالنطاق التي يستخدمها إطار العمل؟ هل يحتوي الإطار على مجتمع قوي وتوثيق جيد ودعم متاح؟ يوفِّر الجدول الموجود في هذا المقال ملخصًا سريعًا لدعم المتصفح الحالي الذي يقدِّمه كل إطار عمل، بالإضافة إلى لغات المجال المحدَّد Domain-specific Languages -أو DSLs اختصارًا- التي يمكن استخدامها، إذ تُعَدّ لغات المجال المحدَّد لغات برمجة مرتبطةً بمجالات محدَّدة من تطوير البرمجيات، كما تُعَدّ في سياق أطر العمل أنها أنواع من لغات جافاسكربت أو HTML التي تسهّل التطوير باستخدام هذا الإطار. لا يتطلب أيّ إطار من أطر العمل مطوِّرًا لاستخدام لغة DSL معينة، ولكن صُمِّمت جميعها تقريبًا مع وضع لغة DSL محدَّدة في الحسبان، إذ يعني اختيار عدم استخدام لغة DSL المفضلة لإطار العمل أنك ستفقد الميزات التي من شأنها تحسين تجربة المطوِّر، كما يجب عليك التفكير بجدية في مصفوفة الدعم ولغات DSL الخاصة بإطار العمل عند اختيارك لأيّ مشروع جديد، إذ يمكن أن يكون دعم المتصفح غير المتطابق عائقًا أمام المستخدِمين، ويمكن أن يكون دعم لغة DSL غير المناسب عائقًا أمامك وأمام زملائك في الفريق. table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } إطار العمل دعم المتصفح لغة DSL المفضلة لغات DSL المدعومة إطار العمل Angular المتصفح IE9+ لغة TypeScript لغات HTML-based وTypeScript إطار العمل React متصفح IE9+ الحديث مع تعويض نقص دعم المتصفحات Polyfill صيغة JSX صيغة JSX ولغة TypeScript إطار العمل Vue المتصفح IE9+ لغة HTML-based لغات HTML-based وJSX وPug إطار العمل Ember متصفح IE9+ الحديث في إصدار Ember رقم 2.18 لغة Handlebars لغات Handlebars وTypeScript وفي ملاحظة مهمة، تجدر الإشارة إلى أن لغات DSL التي وصفناها بأنها " لغات تستند إلى HTML أو HTML-based" لا تمتلك أسماءً رسميةً، فهي ليست لغات DSL حقيقية، ولكنها لغة HTML غير معيارية، لذا وجب الإشارة إلى هذه النقطة. قد يكون مجتمع إطار العمل المقياس الأصعب في القياس، لأن حجم المجتمع لا يرتبط مباشرةً بالأعداد التي يسهل الوصول إليها، ويمكنك التحقق من عدد نجوم مشروع جيت هاب GitHub أو التنزيلات الأسبوعية من npm للحصول على فكرة عن شعبيته، ولكن أفضل ما يمكنك فعله في بعض الأحيان هو البحث في عدد قليل من المنتديات أو التحدث إلى مطوِّرين آخرين، إذ لا يتعلق الأمر بحجم المجتمع فحسب، وإنما يتعلق بشموليته ومدى جودة التوثيق المتاح. هناك مناقشات كثيرة في جميع أنحاء الويب حول إطار العمل الأفضل، فقد اختارت مؤسسة ويكيميديا Wikimedia مؤخرًا استخدام إطار العمل Vue لواجهتها الأمامية، ونشرت طلبًا للتعليقات Request For Comments -أو RFC اختصارًا- حول اعتماد هذا الإطار، وقد استغرق إريك جاردنر Eric Gardner مؤلف RFC وقتًا لتوضيح احتياجات مشروع ويكيميديا وسبب كون بعض أطر العمل اختيارات جيدة للفريق، إذ يُعَدّ طلب التعليقات هذا مثالًا رائعًا لنوع البحث الذي يجب عليك تطبيقه بنفسك عند التخطيط لاستخدام إطار عمل للواجهة الأمامية. يُعَدّ استبيان حالة جافاسكربت مجموعةً مفيدةً من ملاحظات مطوري جافاسكربت، كما يغطّي العديد من الموضوعات المتعلقة بجافاسكربت بما في ذلك البيانات حول استخدام أطر العمل ورأي المطورين بها، وهناك حاليًا مجموعة من البيانات المتاحة على مدى عدة سنوات، مما يسمح لك بالتعرف على شعبية إطار العمل، كما وازن فريق Vue بين Vue وأطر العمل الشائعة الأخرى، إذ قد يكون هناك بعض التحيز في هذه الموازنة، لكنها تُعَدّ موردًا قيّمًا. بدائل لأطر العمل من طرف العميل إذا أردت البحث عن أدوات لتسريع عملية تطوير الويب، وعلمتَ أنّ مشروعك لن يتطلب شيفرة جافاسكربت مكثفةً من طرف العميل، فيمكنك الوصول إلى أحد الحلول القليلة لبناء الويب مثل: نظام إدارة المحتوى Content Management System. التصيير من طرف الخادم Server-side Rendering. مولّد موقع ساكن Static Site Generator. أنظمة إدارة المحتوى تُعَدّ أنظمة إدارة المحتوى Content Management Systems -أو CMSes اختصارًا- أدوات تسمح للمستخدِم بإنشاء محتوى للويب دون كتابة الشيفرة البرمجية مباشرةً، كما تُعَدً حلًا جيدًا للمشاريع الكبيرة وخاصةً المشاريع التي تتطلب مدخلات من كتّاب المحتوى الذين لديهم قدرةً محدودةً على كتابة شيفرة برمجية، أو للمبرمجين الذين يرغبون في توفير الوقت، إلا أنها تتطلب قدرًا كبيرًا من الوقت لإعدادها. يعني استخدام نظام CMS أنك تتخلى على الأقل عن قدر من التحكم في ناتج موقعك النهائي على الويب، فإذا لم يؤلِّف نظام إدارة المحتوى الذي اخترته محتوًى يمكن الوصول إليه افتراضيًا على سبيل المثال، فسيكون تحسين ذلك أمرًا صعبًا في أغلب الأحيان، وتشمل الأمثلة المستخدَمة حاليًا ووردبريس Wordpress وجوملا Joomla ودروبال Drupal. التصيير من طرف الخادم يُعَدّ التصيير من طرف الخادم Server-side Rendering -أو SSR اختصارًا- بنية تطبيقات تكون مهمة الخادم فيها تصيير تطبيق مؤلف من صفحة واحدة، وهو عكس التصيير من طرف العميل Client-side Rendering، كما يُعَدّ الطريقة الأكثر شيوعًا والأكثر مباشرةً لبناء تطبيق جافاسكربت، إذ يكون التصيير من طرف الخادم أسهل على جهاز العميل، لأنك ترسل ملف HTML المُصيَّر إليه فقط، ولكن يمكن أن يكون إعداده صعبًا بالموازنة مع التطبيق المُصيَّر من طرف العميل. تدعم جميع أطر العمل التي ذكرهانا في هذا المقال التصيير من طرف الخادم والتصيير من طرف العميل، كما يمكنك الاطلاع على Next.js لإطار العمل React وNuxt.js لإطار العمل Vue وFastBoot لإطار العمل Ember وAngular Universal لإطار العمل Angular. مولدات الموقع الساكنة تُعَدّ مولِّدات المواقع الساكنة Static Site Generators برامج تنشئ ديناميكيًا جميع صفحات الويب الخاصة بموقع متعدِّد الصفحات -بما في ذلك شيفرة جافاسكربت أو CSS ذات الصلة، بحيث يمكن نشرها في أماكن متعددة، كما يمكن أن يكون مضيف النشر فرعًا من صفحات جيت هاب أو مثيل Netlify أو أيّ خادم خاص من اختيارك مثلًا، ولهذا النهج مزايا متعددة فيما يتعلق بالأداء، فلا يبني جهاز المستخدِم الخاص بك الصفحة باستخدام جافاسكربت، فهو مكتمل فعليًا، والأمان، إذ تمتلك الصفحات الساكنة عددًا أقل من متجهات الهجوم، كما لا يزال بإمكان هذه المواقع استخدام شيفرة جافاسكربت حيثما يحتاجون إليها، لكنها لا تعتمد عليها، وتستغرق مولّدات المواقع الساكنة وقتًا لتعلّمها مثل أيّ أداة أخرى، كما يمكن أن تكون عائقًا أمام عملية التطوير. يمكن أن تحتوي المواقع الساكنة على صفحات فريدة قليلة أو كثيرة حسبما تريد، كما تمكّنك أطر العمل من كتابة تطبيقات جافاسكربت من طرف العميل بسرعة، وتتيح لك مولّدات المواقع الساكنة طريقةً لإنشاء ملفات HTML بسرعة، كما تسمح مولّدات المواقع الساكنة للمطورين بكتابة المكوّنات التي تحدِّد الأجزاء المشتركة من صفحات الويب الخاصة بك، وتكوين هذه المكونات معًا لإنشاء صفحة نهائية، إذ تسمَّى هذه المكونات ضمن سياق مولّدات الموقع الساكنة قوالبًا Templates، ويمكن أن تكون صفحات الويب التي أنشأها مولِّد المواقع الساكنة موطنًا لتطبيقات إطار العمل إذا أردت صفحةً واحدةً محدَّدةً من موقع الويب المُولَّد بطريقة ساكنة لتشغيل تطبيق React عندما يزوره المستخدِم مثلًا. مولّدات المواقع الساكنة موجودة منذ فترة طويلة، لكنها شهدت بعض التجدّد في تاريخ الويب الحديث، وتتوفر الآن منها مجموعة من الخيارات القوية مثل Hugo وJekyll وEleventy وGatsby، فإذا أردت معرفة المزيد حول مولّدات المواقع الساكنة، فراجع دليل تاتيانا ماك Tatiana Mac للمبتدئين في Eleventy، إذ تشرح في المقال الأول من السلسلة ما هو مولِّد الموقع الساكن، وكيفية ارتباطه بالوسائل الأخرى لنشر محتوى الويب. الخلاصة لم نعلّمك كتابة أيّ شيفرة برمجية حتى الآن، ولكن نأمل أننا قدّمنا لك خلفيةً مفيدةً حول سبب استخدامك لأطر العمل في المقام الأول وكيفية البدء في الاختيار، كما يبحث مقالنا القادم في أنواع محدَّدة من ميزات أطر العمل، وسبب عملها بطريقة معينة. ترجمة -وبتصرُّف- للمقال Introduction to client-side frameworks. اقرأ أيضًا دليل استخدام سطر الأوامر في عملية تطوير الويب من طرف العميل بناء نموذج كامل لسلسلة أدوات تطوير الويب من طرف العميل أساسيات إدارة الحزم في تطوير الويب من طرف العميل
  19. يستخدِم المتصفح خيطًا واحدًا افتراضيًا لتنفيذ شيفرة جافاسكربت في صفحتك، إضافةً إلى تخطيط الصفحة وإعادة ضبط العناصر وتجميع الموارد المستهلكة، ويعني هذا أنّ التنفيذ الطويل لدوال جافاسكربت قد يعيق خيط التنفيذ، والذي يقود بدوره إلى صفحة ضعيفة الاستجابة، وبالتالي تجربة مستخدِم سيئة، كما تستطيع استخدام أداتَي تحليل الأداء Waterfall وFrame rate الموضحتين في مقال المكونات الرئيسية للأداة Performance لتحليل أداء صفحات الويب لمراقبة تنفيذ جافاسكربت والمشاكل التي تسببها في الأداء والإشارة إلى دوال محددة تستدعي الانتباه. سنستخدِم في هذا المقال مثالًا لموقع يسبب فيه التنفيذ الطويل لجافاسكربت عدم الاستجابة، وسنقدِّم بعد ذلك مقاربتين مختلفتين لحل هذه المشكلة، بحيث تقتضي الأولى ضرورة فصل الدوال التي تستغرق وقتًا طويلًا في التنفيذ إلى أجزاء واستخدام الدالة ()requestAnimationFrame لجدولة تنفيذ هذه القطع؛ أما الثانية، فهي تنفيذ الدالة كلها في خيط مستقل مستخدِمين عمّال ويب، ولتجريب المثال التطبيقي يمكنك إيجاده في المستودع الخاص به على جيت هاب، إذ سيبدو هذا الموقع بالصورة التالية: يتضمن الموقع ثلاث أدوات تحكم: مجموعة أزرار اختيار للتحكم بطريقة تنفيذ شيفرة جافاسكربت: على أساس كتلة واحدة ضمن الخيط الرئيسي. على أساس سلسلة من عمليات أصغر ضمن الخيط الرئيسي باستخدام ()requestAnimationFrame. ضمن خيط مستقل باستخدام عمّال ويب. زر لتنفيذ شيفرة جافاسكربت عنوانه "!Do pointless computations". زر لتشغيل وإيقاف بعض رسوم CSS المتحركة لكي ينفِّذ المتصفح بعض الأعمال خلف الستار. ابقي الخيار على التنفيذ على أساس كتلة واحدة ضمن الخيط الرئيسي Use blocking call in main thread، واستعد لتسجيل ملف أداء عبر الأداة Performance. انقر على زر Start animations. ابدأ بتسجيل ملف الأداء. انقر على زر Do pointless computations!‎ مرتين أو ثلاث مرات. أوقف تسجيل الملف. سيختلف ما تراه ضمن نافذة الأداة Performance تبعًا لحاسوبك، لكنه يبدو مشابهًا للقطة الشاشة التالية: يعرض القسم الأعلى من اللقطة نظرة عامة للأداة Waterfall يخبرنا عن نوع العمليات التي يجريها المتصفح خلال فترة التسجيل، حيث يدل اللون الزهري على أنّ أغلب ما ينفذه المتصفح هي حسابات خاصة بتنسيق CSS وقد تجد بعض عمليات تخطيط الصفحة وإعادة تدفقها وجمع البيانات المهملة، إذ ينتج ذلك عن رسوميات CSS المتحركة التي تُنفَّذ خلال فترة تسجيل ملف الأداء، وسترى أيضًا كتلًا برتقالية اللون تمثِّل شيفرة جافاسكربت التي تُنفَّذ في كل مرة ننقر فيها الزر، في حين نشاهد في القسم السفلي معدل الإطارات المرتبط بالمسار الزمني للتسجيل، إذ يمكننا رؤية أنّ معدل الإطارات سليم طيلة فترة التسجيل، لكنه ينهار تمامًا عند النقر على الزر، كما سنختار إحدى هذه الفترات ونلقي نظرةً عن قرب على ما يجري من خلال نافذة التفاصيل التي تعرض بيانات الأداة Waterfall: يُنفِّذ المتصفح دالة جافاسكربت أو سلسلة من الدوال الأصغر عندما نضغط الزر، مما يعيق التنفيذ ضمن الخيط الرئيسي مدة 71.73 ميلي ثانية، إذ يُعَدّ هذا الزمن أعلى بأربع مرات من الزمن المفترض لتنفيذ الإطار أي 16.7 ميلي ثانية، فما الدالة التي سببت المشكلة إذًا؟ لننتقل إلى الأداة Flame Chart لنرى تفاصيلًا أكثر ضمن نافذة التفاصيل: تعرض لنا الأداة مكدس استدعاء جافاسكربت في لحظة التنفيذ هذه، إذ تتوضع الدالة ()calculatePrimes في قمة المكدس، كما يمكننا من خلال التفاصيل المعروضة معرفة اسم الملف الذي يضمها والسطر الذي عُرّفت فيه، وإليك شيفرة الدالة، بالإضافة إلى الدالة التي تستدعيها مباشرةً: const iterations = 50; const multiplier = 1000000000; function calculatePrimes(iterations, multiplier) { var primes = []; for (var i = 0; i < iterations; i++) { var candidate = i * (multiplier * Math.random()); var isPrime = true; for (var c = 2; c <= Math.sqrt(candidate); ++c) { if (candidate % c === 0) { // not prime isPrime = false; break; } } if (isPrime) { primes.push(candidate); } } return primes; } function doPointlessComputationsWithBlocking() { var primes = calculatePrimes(iterations, multiplier); pointlessComputationsButton.disabled = false; console.log(primes); } ما تنفذه الشيفرة هو اختبار -غير فعال إطلاقًا- لأوّليِّة primality عدد عشوائي كبير ويُكرَّر الاختبار 50 مرة. استخدام الدالة requestAnimationFrame سنقسم الدالة إلى عدد من الدوال الأصغر والمستقلة بذاتها على أساس محاولة أولى لحل المشكلة، وسنستخدِم بعد ذلك الدالة ()requestAnimationFrame لجدولة تنفيذ هذه الأجزاء، إذ تطلب هذه الدالة من المتصفح تنفيذ دالة محددة في كل إطار قبل عملية إعادة رسم الصفحة مباشرةً. وطالما أن الدوال المجتزأة صغيرة بما يكفي، فسيتمكن المتصفح من تنفيذ العمل ضمن الزمن المفترض لكل إطار، كما أنه من السهل تجزئة الدالة ()calculatePrimes بحيث تحسب أوّليّة كل عدد في دالة مستقلة: function doPointlessComputationsWithRequestAnimationFrame() { function testCandidate(index) { // finishing condition if (index == iterations) { console.log(primes); pointlessComputationsButton.disabled = false; return; } // test this number var candidate = index * (multiplier * Math.random()); var isPrime = true; for (var c = 2; c <= Math.sqrt(candidate); ++c) { if (candidate % c === 0) { // not prime isPrime = false; break; } } if (isPrime) { primes.push(candidate); } // schedule the next var testFunction = testCandidate.bind(this, index + 1); window.requestAnimationFrame(testFunction); } var primes = []; var testFunction = testCandidate.bind(this, 0); window.requestAnimationFrame(testFunction); } انتقل إلى الخيار Use requestAnimationFrame لاختبار هذا الحل، ثم ابدأ تسجيلًا جديدًا، إذ سيبدو التسجيل هذه المرة مشابهًا للقطة الشاشة التالية: ما نراه في اللقطة هو ما نتوقعه تمامًا، فبدلًا من ظهور كتلة برتقالية واحدة في كل مرة ننقر فيها على الزر، سنجد سلسلةً طويلةً مكونةً من كتل برتقالية قصيرة جدًا تبدو منفصلة عن بعضها بمقدار إطار -أي زمن تنفيذ إطار- وتمثِّل كل منها تنفيذ دالة واحدة من الدوال التي تستدعيها ()requestAnimationFrame، كما يتخلل الكتل البرتقالية كتلًا زهرية اللون تمثِّل عمليات CSS، وسيتمكن المتصفح من تنفيذ عمليات الإطار جميعها في الوقت المفترض دون انخفاضات مفاجئة عند نقر الزر -عند تنفيذ جافاسكربت- نظرًا لصغر فترة تنفيذ الدوال المجزَّأة. حسّن استخدام الدالة ()requestAnimationFrame استجابة الموقع، لكن هناك مشكلتَين محتملتَين: من الصعب أحيانًا تقسيم الدالة التي تتطلب وقتًا كبيرًا في التنفيذ إلى دوال مستقلة أصغر، فقد ولَّدت هذه العملية في مثالنا البسيط شيفرةً أعقد. تستغرق الدوال المجزأة وقتًا أطول حتى تُنفَّذ جميعها، إذ يمكن في مثالنا تحديد الوقت الذي استغرقته بدقة: إذ تُكرَّر العملية 50 مرة، ويولّد المتصفح 60 إطار في الثانية، وبالتالي سيستغرق تنفيذ التكرارات الخمسين قرابة الثانية، وهذا الزمن ملحوظ سواء في ملف الأداء أو من قبل المستخدِم. استخدام عمال ويب سنحاول حل المشكلة الآن باستخدام تقنية عمَّال الويب التي تمكّنك من تنفيذ شيفرة جافاسكربت في خيط مستقل، ولا يمكن بالطبع استدعاء دوال تُنفَّذ ضمن الخيط الرئيسي من خيط العامل أو العكس مباشرةً، وإنما يمكنها التواصل من خلال واجهة برمجية تضمن تراسلًا غير متزامن، وتبدو الشيفرة التي تُنفَّذ ضمن الخيط الرئيسي كما يلي: const iterations = 50; const multiplier = 1000000000; var worker = new Worker("js/calculate.js"); function doPointlessComputationsInWorker() { function handleWorkerCompletion(message) { if (message.data.command == "done") { pointlessComputationsButton.disabled = false; console.log(message.data.primes); worker.removeEventListener("message", handleWorkerCompletion); } } worker.addEventListener("message", handleWorkerCompletion, false); worker.postMessage({ "multiplier": multiplier, "iterations": iterations }); } إنّ الاختلاف الرئيسي في هذه الحالة موازنةً بالسابقة هو الحاجة إلى: إنشاء عامل. إرسال رسالة عندما يحين وقت الحسابات. الإنصات إلى رسالة نصها "done" تشير إلى انتهاء العامل. نحتاج بعد ذلك إلى ملف يُدعى calculate.js ويضم الشفرة التالية: self.addEventListener("message", go); function go(message) { var iterations = message.data.iterations; var multiplier = message.data.multiplier; primes = calculatePrimes(iterations, multiplier); self.postMessage({ "command":"done", "primes": primes }); } function calculatePrimes(iterations, multiplier) { var primes = []; for (var i = 0; i < iterations; i++) { var candidate = i * (multiplier * Math.random()); var isPrime = true; for (var c = 2; c <= Math.sqrt(candidate); ++c) { if (candidate % c === 0) { // not prime isPrime = false; break; } } if (isPrime) { primes.push(candidate); } } return primes; } لا بد في شيفرة العامل أن ننصت إلى الرسالة التي تخبرنا ببدء التنفيذ، ومن ثم نرسل الرسالة "done" عندما ننهي الحسابات. لاحظ أنّ الشيفرة التي تنفّذ الحسابات هي نفسها تمامًا الشيفرة الأصلية دون تعديل، فكيف سيتغير الأداء الآن؟ انتقل إلى الخيار Use a worker وأنشئ تسجيلًا جديدًا، إذ سيبدو التسجيل مشابهًا للقطة الشاشة التالية: نقرنا الزر في هذا التسجيل ثلاث مرات، حيث تبدو كل نقرة موازنةً بالملف الأول كتلتَين برتقاليتَي اللون وقصيرتين جدًا هما: الدالة ()doPointlessComputationsInWorker التي تتعامل مع حدث النقر وتبدأ تنفيذ شيفرة عامل الويب. الدالة ()handleWorkerCompletion التي تُنفَّذ عندما ينتهي استدعاء العامل. سينفِّذ العامل اختبارات الأوّليّة في الفترة الفاصلة بين تنفيذ الدالتين السابقتين ولم يلاحظ أيّ تأثير على استجابة الخيط الرئيسي، وقد يبدو هذا الأمر مستبعدًا بعض الشيء، لكن قد تستغل تقنية عمال الويب إيجابيات المعالجات متعددة النوى كونها تعمل على خيط مختلف، في حين لا يمكن للمواقع التي تعتمد خيطًا واحدًا استغلال هذه الناحية في تحسين الأداء، وتبقى المحدودية الرئيسية لاستخدام عمال ويب هي عدم توفر واجهة برمجية لشجرة DOM لدعم عملياتها. ترجمة -وبتصرف- للمقال Intensive Javascript. اقرأ أيضًا المقال السابق: تأثير رسوم CSS المتحركة على أداء مواقع الويب خطوات أساسيّة لتحسين أداء المواقع
  20. يصف المقال طريقة إعداد خادم اختبار محلي على حاسوبك وكيفية استخدامه، كما ننصحك بدايةً بالاطلاع على كيفية عمل الإنترنت والتعرُّف على خادم الويب. الملفات على الخادم المحلي موازنة بالملفات على خادم بعيد يمكنك بكل بساطة فتح ملف HTML أو صفحة ويب على حاسوبك بالنقر المزدوج عليه، أو من خلال سحبه ثم إفلاته داخل المتصفح، أو من خلال قائمة ملف File ثم فتح Open، ومن ثم الانتقال إلى الملف المطلوب، ولاحظ أنّ أيّ ملف موجود على القرص الصلب لحاسوبك سيبدأ عنوانه بالشكل //:file يلي ذلك مسار الملف، بينما إذا استعرضت ملفًا على جيت هاب GitHub أو على أيّ خادم آخر، فستلاحظ بدء عنوانه بـ //:http أو //:https للدلالة على حصولنا عليه عبر بروتوكول HTTP. مشكلة اختبار الملفات المحلية لن تتمكن من فتح بعض الأمثلة إذا عاملتها على أنها ملفات محلية، إذ يعود ذلك إلى أسباب مختلفة أهمها: قد تمثل هذه الملفات ردودًا لطلبات غير متزامنة asynchronous requests: لن تنفِّذ بعض المتصفحات -بما فيها جوجل كروم- الطلبات غير المتزامنة إذا جرى طلبها عن طريق ملف محلي لأسباب تتعلق بقيود أمنية. قد تستخدم هذه الملفات شيفرة لغات تعمل على الخادم: مثل PHP أو بايثون، والتي تتطلب خوادم خاصة لتفسير الشيفرة وإيصال النتائج. تعامل المتصفحات الطلبات التي تحمِّل موارد محلية عبر بروتوكول file://‎ على أنها طلبات ذات أصل مختلط cross-origin requests، لذا إن حمَّلت ملفًأ محليًا يحوي يطلب ملفات محلية أخرى، فقد يولد هذا خطأ CORS (اختصارٌ إلى سياسة مشاركة الموارد ذات الأصول المختلطة). تشغيل خادم HTTP محلي بسيط لا بدّ من اختبار الأمثلة المشابهة على خادم ويب محلي لتجاوز مشكلة الطلبات غير المتزامنة، إذ سيكون استخدام وحدة خادم HTTP لبايثون http.server من أسهل طرق تنفيذ ذلك. اتبع الخطوات التالية لإعداد الخادم: ثبِّت بايثون: من المفترض أن تكون حزمة بايثون مثبتةً على جهازك مسبقًا إذا كنت تستخدِم لينوكس Linux أو ماك أو إس macOS‎، بينما إذا كنت تستخدِم ويندوز Windows، فعليك زيارة الصفحة الرسمية لبايثون والحصول على المُثبِّت الخاص بويندوز من خلال الخطوات التالية: انتقل إلى العنوان python.org. انقر على الرابط "Python "3.xxx الموجود ضمن قسم التنزيل Download. انقر على رابط مُثبِِّت ويندوز Windows Installer الموجود أسفل الصفحة لتنزيل ملف التثبيت. شغّل ملف التثبيت عند انتهاء التنزيل. تأكد من تفعيل مربع التحقق الذي يشير إلى إضافة بايثون إلى المسار Add Python 3.xxx to PATH. انقر على تثبيت Install، ثم انقر على إغلاق Close عند انتهاء العملية. افتح موجه سطر الأوامر Command Prompt في ويندوز أو الطرفية Terminal في لينوكس أو ماك لتتحقق من تثبيت بايثون. نفذ الأمر التالي: python -V # إن فشلت التعليمة السابقة استخدم التعليمة python3 -V # إن كان متاحًا "py" أو حاول استخدام الأمر py -V سيعيد إليك الأمر السابق رقم إصدار بايثون الذي ثُبِّت، ثم انتقل بعد ذلك إلى المجلد الذي يحتوي على المثال الذي تريد اختباره باستخدام الأمر cd إذا كان المثال على سطح المكتب أو في مكان آخر، فجرّب الانتقال إليه وفق أحد الأسلوبين التاليين: # ضع عنوان المجلد الذي يحوي المثال الذي تريد اختباره cd Desktop # استخدم النقطتين للانتقال إلى مجلد أعلى بمستوى واحد cd .. استخدم الأمر التالي لتشغيل الخادم ضمن المجلد المحدد: # إذا كان إصدار بايثون 3 # حاول ما يلي على ويندوز # "python -m http.server" # أو # "py -3 -m http.server" python3 -m http.server # إذا كان إصدار بايثون 2 python -m SimpleHTTPServer ستنفِّذ التعليمة السابقة محتوى المجلد على خادم ويب محلي ورقم المنفذ الخاص به 8000، كما يمكن الوصول إلى هذا الخادم من خلال كتابة العنوان localhost:8000 ضمن المتصفح الذي سيعرض لك محتويات المجلد، وانقر على ملف HTML الذي تريد تشغيله. # بايثون 3 python3 -m http.server 7800 # بايثون 2 python -m SimpleHTTPServer 7800 ستتمكن من الوصول إلى الخادم على هذا المنفذ بكتابة العنوان localhost:7800 ضمن المتصفح. تنفيذ شيفرة لغة تعمل على الخادم لن تستطيع وحدتَي بايثون http.server أو SimpleHTTPServer التي تعمل على الإصدار 2 على الرغم من فائدتهما من تنفيذ شيفرة مكتوبة بلغات مثل بايثون أو PHP أو جافاسكربت، فهي تقدِّم بالكاد خادم ملفات ساكن static file server، ولتنفيذ شيفرات مثل هذه، فلا بدّ من عمل إضافي يتعلق بطبيعة اللغة التي تستخدِمها، وإليك بعض الأمثلة: لتنفيذ شيفرة بايثون، لا بدّ من استخدام إطار عمل بايثون خاص بالويب Python web framework، وستجد الكثير من إطارات العمل هذه مثل Django أو Flask أو Pyramid. لتنفيذ شيفرة Node.js -جافاسكربت-، لا بدّ من استخدام Node الأساسي أو أيّ إطار عمل مبني على أساسه مثل إكسبرس Express الذي يمثِّل خيارًا جيدًا. لتنفيذ شيفرة PHP، شغّل خادم PHP المدمج كما يلي: $ cd path/to/your/php/code $ php -S localhost:8000 ترجمة -وبتصرف- للمقال ?How do you set up a local testing server. اقرأ أيضًا دليل إعداد خادم ويب محلي خطوة بخطوة الفرق بين صفحة الويب وموقع الويب وخادم الويب ومحرك البحث
  21. تتكون الأداة Performance من أربع أدوات جزئية سنشرحها في هذا المقال بشيء من التفصيل. الأداة Waterfall تقدِّم لك هذه الأداة عرضًا للأعمال التي ينفِّذها المتصفح عندما يستعرض موقعك أو تطبيقك، ويعتمد عمل الأداة على فكرة أنّ الأشياء التي ينفذها المتصفح عندما يستعرض موقعًا يمكن تصنيفها ضمن عدة فئات منها تشغيل شيفرة جافاسكربت وتحديث مخطط الصفحة وغيرها، وأنّ ما ينفذه المتصفح في لحظة ما هو شيء واحد فقط، فإذا لاحظت علامةً على مشكلة في الأداء مثل انخفاض عدد إطارات مع الزمن، فيمكنك التوجه إلى الأداة Waterfall لترى ما كان يفعله المتصفح في هذه اللحظة إذا كنت تسجل ملف الأداء. يمثِّل المحور X الزمن المنقضي، كما تظهر العمليات المسجلة التي ندعوها علامات Markers على أساس أشرطة أفقية تتتابع مثل الشلال لتعكس الطبيعة التسلسلية لآلية تنفيذ المتصفح للعمليات، فعندما تختار علامةً ما، فسترى معلومات عنها ضمن الشريط الجانبي الذي يقع على اليمين وتتضمن الفترة الزمنية التي استغرقتها العلامة وغيرها من المعلومات الخاصة بنوع العلامة marker type. العلامات تملك العلامات التي ترتبط بالعمليات المنفذة نظامًا لونيًا محددًا وسنستعرضها في هذا القسم: أحداث DOM: تمثَّل باللون، وهي شيفرة جافاسكربت التي تُنفَّذ على أساس استجابة لأحداث DOM. تقدم هذه العلامات معلومات عن ما يلي: نوع الحدث Event type: مثل نقرة click أو رسالة message. مرحلة الحدث Event Phase: مثل هدف Target أو التقاط Capture. دوال جافاسكربت: تُمثَّل باللون، وهي الدوال التي ينفِّذها المتصفح في صفحة الويب وتُعنون وفقًا للسبب الذي استُدعي التابع لأجله، كما تقدِّم المعلومات على صورة مكدّس استدعاءات call stack مزوَّد بروابط إلى هذه الدوال: Script tag: تتعلق بالتعامل مع وسوم الشيفرة. setInterval: تتعلق بضبط الفترات الزمنية. setTimeout: تتعلق بتحديد زمن انتهاء العملية. requestAnimationFrame: تتعلق بطلبات إطار رسومي. Promise Callback: وتتعلق ياستدعاءات الوعود Promises. Promise Init: وتتعلق بتهيئة الوعود. Workers: تتعلق بعمّال ويب. JavaScript URI: تتعلق بعناوين موارد جافاسكربت. Event Handler: تتعلق بمعالجة الأحداث. تفسير HTML: تُمثَّل باللون وتمثِّل الوقت المستغرق في تفسير شيفرة HTML لصفحة الويب، كما تُقدِّم المعلومات على صورة مكدّس استدعاءات call stack مزوَّد بروابط إلى الدوال المستخدَمة. تفسير XML: تُمثَّل باللون وتمثِّل الوقت المستغرق في تفسير شيفرة XML لصفحة الويب، كما تُقدِّم المعلومات على صورة مكدّس استدعاءات مزوَّد بروابط إلى الدوال المستخدَمة. إعادة ضبط التنسيق Recalculate Style: تُمثَّل باللون، وتعرض الحسابات اللازمة لضبط التنسيق المحدد لعناصر الصفحة، كما تقدِّم المعلومات على صورة تلميحات لإعادة التنسيق Restyle Hint، والتي هي قيم نصية تشير إلى نوع إعادة التنسيق الواجب تطبيقه وتأخذ القيم التالية: Self. Subtree. LaterSiblings. CSSTransitions. CSSAnimations. SVGAttrAnimations. StyleAttribute. StyleAttribute_Animations. Force. ForceDescendants. ضبط التخطيط Layout: تُمثَّل باللون وتعرض الحسابات اللازمة لتحديد موقع وحجم عناصر الصفحة، كما تدعى هذه العملية بإعادة الإنسياب reflow أحيانًا. رسم الصفحة Paint: تُمثَّل باللون وترسم البكسلات على الشاشة. تجميع الموارد المستهلكة Garbage Collection تُمثَّل باللون وتعرض أحداث تجميع الموارد المستهلكة، ويُشار إلى أحداث تجميع الموارد المستهلكة اللاتصاعدي بالعبارة Non-incremental، كما تُقدِّم المعلومات على صورة قيم نصية وهي: "ReasonA": سلسلة نصية تدل على سبب استخدام مجمع الموارد المستهلكة GC. "Non-incremental Reason": سلسلة نصية تدل على سبب استخدام مجمع الموارد المستهلكة اللاتصاعدي. ستجد في فايرفوكس 46 ميزةً جديدةً هي تجميع الموارد المستهلكة، نظرًا لضغوطات ناتجة عن حجز الذاكرة، حيث يظهر في هذه الحالة رابطًا بعنوان عرض المسببات الناتجة عن حجز الذاكرة Show Allocation Triggers. انقر عندها على الرابط لتتابع ملف الأداء المتعلق بحجز الذاكرة، وصولًا إلى لحظة وقوع حدث تجميع الموارد المستهلكة. تدوير المستهلكات Cycle Collection: تُمثَّل باللون، وهي عملية تحرير بُنى البيانات التي تُخزِّن أرقام المراجع في اللغة ++C، وتشبه هذه العملية تجميع الموارد المستهلكة لكنها خاصة بكائنات ++C، وهذه العمليات دائمًا من النوع تجميع Collect. اختزال المنحني البياني لعملية تدوير المستهلكات CC Graph Reduction: تُمثَّل باللون، وتضم عمليتي التحضير أو التحسين الأولي لتدوير المستهلكات CC، وتكون هذه العمليات دائمًا من نوع تجاهل ما يمكن تجاوزه ForgetSkippable. العلامة Console: تُمثَّل باللون، وهي الفترة الزمنية بين استدعائين مترابطين للدالتين ()console.time و()console.timeEnd، كما تُقدِّم المعلومات عبر: اسم المؤقت Timer name: على هيئة وسيط يمرَّر إلى الدالة console. المكدس في البداية stack at start: يعرض حالة مكدس الاستدعاء عند استدعاء الدالة ()console.time مع روابط إلى بقية الدوال. المكدس في النهاية stack at end: وهو جديد في فايرفوكس 46، إذ يعرض حالة مكدس الاستدعاء عند استدعاء الدالة ()console.timeEnd، فإذا كان الاستدعاء داخل وعد Promise؛ فسيعرض أيضًا القيمة النصية "Async stack". العلامة Timestamp: تُمثَّل باللون، وهي استدعاء مفرد للدالة ()console.timeStamp، كما تُظهر الوسيط الذي مُرِّر إلى هذه الدالة. تحميل محتوى DOM: تُمثّل باللون، وتُظهر الحدث DOMContentLoaded. تحميل Load: تُمثّل باللون، وتظهر حدث تحميل المستند load. أحداث عمّال ويب في الخيط الأساسي Worker event in main thread: تُمثَّل باللون، وتظهر الحالات التي يرسل فيها الخيط الأساسي رسائل إلى عمّال الويب أو التي يستقبل فيها رسائل من العمّال، كما وتأخذ إحدى القيمتين: تهيئة البيانات على الخيط الرئيسي Serialize data on the main thread*، إذ يحوِّل الخيط الرئيسي الرسالة إلى صيغة مناسبة كي تُرسَل إلى العامل. تجميع البيانات على الخيط الرئيسي Deserialize data on the main thread: إذ يحوّل الخيط الرئيسي البيانات القادمة من العمال إلى بنية يمكن تخزينها أو عرضها. أحداث عمال ويب في خيط العمال Worker event in worker thread: تُمثَّل باللون، وتظهر الحالات التي يرسَل فيها العامل رسائلًا إلى الخيط الرئيسي، أو التي يستقبل فيها رسائل من الخيط الرئيسي، كما تأخذ إحدى القيمتين: تهيئة البيانات للعامل Serialize data in worker: إذ يحوِّل العامل الرسالة إلى صيغة مناسبة كي تُرسَل إلى الخيط الرئيسي. تجميع البيانات عند العامل Deserialize data in worker: ويحوِّل العامل البيانات القادمة من الخيط الرئيسي إلى بنية يمكن للعامل فهمها. تملك العلامات النمط اللوني نفسه في الأدلة Waterfall وفي لوحة النظرة العامة عندما تعرض هذه الأداة، مما يُسهِّل المطابقة بينهما. ترشيح العلامات يمكنك التحكم بالعلامات التي تريد استعراضها، وذلك من خلال زر الترشيح ضمن شريط الأدوات: أنماط لعمليات تشير إليها الأداة Waterfall يعتمد ما تعرضه لك الأداة Waterfall كثيرًا على نوع النشاط الذي يفعله موقعك، إذ ستبدو مواقع الويب التي تستخدِم جافاسكربت بشدة باللون البرتقالي، بينما سيظهر اللونين الأرجواني والأخضر بكثرة في المواقع التي تغير مظهرها المرئي بوتيرة مرتفعة، لكنك قد تلحظ بعض الأنماط التي تحذِّرك من مشاكل محتملة في الأداء. تصيير التنفيذ المتسلل لعمل المتصفح قد تعرض لك الأداة Waterfall النمط التالي: يمثل الشكل السابق تصوّرًا للخوارزمية الأساسية التي يعتمدها المتصفح في الاستجابة لحدث: استدعاء دالة جافاسكربت: تتسبب بعض الأحداث في صفحة ويب مثل أحداث DOM، بتنفيذ شيفرة جافاسكربت، وقد تغيّر هذه الشيفرة بعض أجزاء شجرة DOM أو شجرة CSSOM. إعادة ضبط التنسيق: إذا لاحظ المتصفح تغيرات في التنسيق المحسوب لعناصر الصفحة، فلا بد من إعادة الحسابات وضبط التنسيق مجددًا. تخطيط الصفحة: يستخدِم المتصفح التنسيق المحسوب حديثًا في تقدير موقع وهندسة العناصر في الصفحة.، إذ تدعى هذه العملية بتخطيط الصفحة Layout، كما تدعى أحيانًا إعادة إنسياب reflow. رسم الصفحة: لا بد أخيرًا من إعادة رسم الصفحة على الشاشة، في حين لا تظهر الخطوة الأخيرة على الشاشة، وهي إمكانية تقسيم الصفحة إلى طبقات يُعاد رسمها بصورة مستقلة، ومن ثم يعاد دمجها بعملية التركيب Composition. لا بد أن يتسع إطار واحد للتسلسل السابق طالما أنّ الشاشة لن تُحدَّث حتى اكتمال سلسلة التنفيذ السابقة، ومن المعروف أنّ معدل 60 إطار في الثانية كاف لإظهار الرسوميات المتحركة بسلاسة ونعومة على الشاشة، ويتطلب هذا من المتصفح تنفيذ السلسلة السابقة خلال 16.7 ميلي ثانية، كما من المهم معرفة من منظور الاستجابة أنّ المتصفح لا ينفِّذ بالضرورة كل خطوة من الخطوات السابقة: تُحدِّث رسوميات CSS الصفحة دون تنفيذ أيّ شيفرة جافاسكربت. لا يعيد كل تغيير في خصائص تنسيق CSS تخطيط الصفحة، وإنما فقط تلك الخصائص التي تغيِّر موقع وهندسة العناصر، مثل width أو display أو font-size أو top؛ بينما لا تغير الخصائص مثل color أو opacity تخطيط الصفحة. لا يعيد كل تغيير في خصائص تنسيق CSS رسم الصفحة، فإذا حرّكت العنصر باستخدام خاصية التنسيق transform تحديدًا، فسيستخدِم المتصفح طبقةً منفصلةً للعنصر الذي طُبقت عليه تلك الخاصية، ولا حاجة حتى لإعادة الرسم عندما يتحرك العنصر، وإنما يُضبَط موقعه وهندسته أثناء عملية تركيب الطبقات. سنطلع لاحقًا على تأثير رسوم CSS المتحركة على الأداء، وكيف تساعد الأداة Waterfall في الإشارة إلى ذلك. حجب جافاسكربت تُنفَّذ جافاسكربت افتراضيًا على الخيط نفسه الذي يستخدِمه المتصفح لتحديث تخطيط الصفحة وإعادة الرسم وتنفيذ أحداث DOM وغيرها، وهكذا فإنّ تنفيذ دوال جافاسكربت يستغرق وقتًا طويلًا قد يسبب توقف استجابة الصفحة وخشونةً في عرض الرسوميات، كما قد تتجمد الصفحة بالكامل، في حين يمكن بسهولة رصد الأوقات التي تسبب فيها شيفرة جافاسكربت مشاكلًا في الاستجابة باستخدام الأداتين Frame rate وWaterfall معًا. لاحظ كيف حددنا في الشكل التالي منطقةً سببت فيه دالة جافاسكربت نقصًا في معدل الإطارات. سنطّلع في مقال تأثير الاستخدام المكثف لجافاسكربت على الأداء على أهمية الأداة Waterfall في تحديد مشاكل الاستجابة التي تسببها دوال جافاسكربت وكيفية استخدام التوابع غير المتزامنة للمحافظة على استجابة خيط التنفيذ. عملية رسم عناصر مستنزفة للوقت قد تستنزف بعض تأثيرات رسم العناصر وقتًا طويلًا مثل box-shadow، وخاصةً عند تطبيق هذه التأثيرات في حالات تحريك العناصر، إذ ينبغي على المتصفح إعادة ضبط هذه التأثيرات مع كل إطار، فإذا لاحظت انخفاضًا في معدل الإطارات عند تنفيذ عمليات رسومية كثيفة أو عمليات تحريك للعناصر خصوصًا، فتحقق من العلامات الخضراء في الأداة Waterfall. تجميع الموارد المستهلكة تشير العلامات الحمراء في الأداة Waterfall إلى عمليات تجميع الموارد المستهلكة GC -بغية تحرير الذاكرة المرتبطة بها-، إذ يتجول محرك جافاسكربت والذي يُدعى SpiderMonkey في فايرفوكس في كومة الذاكرة عن المناطق التي لم يَعُد بالإمكان الوصول إليها وتحريرها، وتؤثر هذه العملية في الأداء لأنه لا بد من إيقاف محرك جافاسكربت عن تنفيذ الشيفرة مؤقتًا عندما تبدأ عملية تجميع الموارد المستهلكة، وبالتالي سيُعلَّق تنفيذ برنامجك وقد لا يستجيب إطلاقًا. ينفذ المحرك SpiderMonkey ما يسمى التجميع المتصاعد للموارد المستهلكة incremental GC لتخفيف هذه المشكلة في فايرفوكس، إذ تقتضي هذه العملية تنفيذ عملية تجميع الموارد المستهلكة على دفعات متصاعدة صغيرة تلائم تنفيذ البرنامج خلالها، لكن لا بد في بعض الحالات من تنفيذ عملية تجميع موارد مستهلكة لا تصاعدي، وبالتالي على البرنامج الانتظار حتى نهاية العملية، كما من الأفضل عدم محاولة تخصيص طريقة لتفادي عملية تجميع موارد مستهلكة لمحرك جافاسكربت معيّن وخاصةً أحداث تجميع الموارد المستهلكة التصاعدية، إذ يستخدِم SpiderMonkey مثلًا مجموعةً معقدةً من الطرق الاستدلالية لتقدير الحاجة إلى استخدام عملية تجميع الموارد المستهلكة اللاتصاعدية والتصاعدية خصوصًا، ويمكن القول أنه: نحتاج إلى تجميع الموارد المستهلكة عندما تُحجَز مساحات كبيرة من الذاكرة. نحتاج إلى تجميع الموارد تصاعديًا عندما يكون معدل حجز الذاكرة مرتفعًا إلى الحد الذي تستنزف فيه ذاكرة محرك جافاسكربت عند تنفيذ التجميع اللاتصاعدي. عندما تسجل الأداة Waterfall علامة تجميع موارد مستهلكة، فهي تشير إلى: العملية تصاعدية أم لا. سبب تنفيذ العملية. سبب تنفيذ العملية اللاتصاعدية إذا كانت كذلك. ستجد في فايرفوكس 46 وما بعد ميزةً جديدةً هي تجميع الموارد المستهلكة على أساس نتيجة لضغوطات ناتجة عن حجز الذاكرة، حيث يظهر في هذه الحالة رابطًا بعنوان عرض المسببات الناتجة عن حجز الذاكرة Show Allocation Triggers، وانقر عندها على الرابط لتتابع ملف الأداء المتعلق بحجز الذاكرة، وصولًا إلى لحظة وقوع حدث تجميع الموارد المستهلكة، كما سنعرض هذه الموضوع لاحقًا بشيء من التفصيل. إضافة العلامات من خلال الواجهة البرمجية للطرفية يمكن التحكم بعلامتين فقط من خلال الواجهة البرمجية للطرفية Console API هما: "Console" و"Timestamp". علامات Console تساعدك على تحديد قسم محدد من التسجيل، وبالتالي استدع الدالة ()console.time في بداية القسم الذي تريد تحديده والدالة ()console.timeEnd عند نهايته، كما تقبل هاتين الدالتين وسيطًا يُستخدَم في تسمية هذا القسم، ولنفرض مثلًا أننا أمام الشيفرة التالية: var iterations = 70; var multiplier = 1000000000; function calculatePrimes() { console.time("calculating..."); var primes = []; for (var i = 0; i < iterations; i++) { var candidate = i * (multiplier * Math.random()); var isPrime = true; for (var c = 2; c <= Math.sqrt(candidate); ++c) { if (candidate % c === 0) { // not prime isPrime = false; break; } } if (isPrime) { primes.push(candidate); } } console.timeEnd("calculating..."); return primes; } سيبدو خرج الأداة waterfall كما يلي: تُحدَّد العلامة من خلال الوسيط الذي مُرِّر إلى الدالة ()console.time، كما يمكنك متابعة مكدس البرنامج ضمن الشريط الجانبي إلى اليمين عندما تختار هذه العلامة. أما بخصوص مكدس العملية اللامتزامنة Async stack، فسيعرِض الشريط الجانبي ابتداءً من النسخة فايرفوكس 41 حالة المكدس في نهاية التسجيل عند استدعاء ()console.timeEnd، فإذا استدعيت ()console.timeEnd من داخل وعد Promise، فسيعرض الشريط العبارة "(Async: Promise)" وسترى تحتها العبارة "async stack" التي تدل على حالة مكدس الاستدعاء في لحظة تنفيذ الوعد، وتأمل الشيفرة التالية على سبيل المثال: var timerButton = document.getElementById("timer"); timerButton.addEventListener("click", handleClick, false); function handleClick() { console.time("timer"); runTimer(1000).then(timerFinished); } function timerFinished() { console.timeEnd("timer"); console.log("ready!"); } function runTimer(t) { return new Promise(function(resolve) { setTimeout(resolve, t); }); } وستعرض الأداة Waterfall علامةً للفترة الزمنية المنقضية بين الاستدعائين ()time و()timeEnd، فإذا نقرت عليها، فستشاهد المكدس اللامتزامن ضمن الشريط الجانبي: علامات Timestamp تساعدك هذه العلامة على تحديد لحظة زمنية ضمن التسجيل، لذا استدع الدالة ()console.timeStamp لتضع علامة timestamp، كما يمكنك تمرير اسم لهذه العلامة من خلال تمرير الاسم على أساس وسيط للدالة السابقة، ولنفترض أننا عدلنا الشيفرة التي عرضناها سابقًا لتحديد لحظة زمنية كل 10 تكرارات للحلقة على سبيل المثال، ونختار عدد التكرارات على أساس اسم لهذه اللحظة: var iterations = 70; var multiplier = 1000000000; function calculatePrimes() { console.time("calculating..."); var primes = []; for (var i = 0; i < iterations; i++) { if (i % 10 == 0) { console.timeStamp(i.toString()); } var candidate = i * (multiplier * Math.random()); var isPrime = true; for (var c = 2; c <= Math.sqrt(candidate); ++c) { if (candidate % c === 0) { // not prime isPrime = false; break; } } if (isPrime) { primes.push(candidate); } } console.timeEnd("calculating..."); return primes; } ستعرض الأداة waterfall شيئًا من هذا القبيل: الأداة Frame rate يُعَدّ معدّل الإطارات مقياسًا لاستجابة الصفحة، وقد تتسم الصفحات التي تتمتع بمعدل إطارات ضعيف أو غير مستقر بضعف الاستجابة أو الجمود، مما يؤثر على تجربة المستخدِم. يتيح الرسم البياني لمعدل الإطارات في الأداة Performance إمكانية إظهار تغير معدل الإطارات خلال فترة التسجيل، ويدلك بسرعة إذا كانت الصفحة ستعاني من مشاكل في الأداء وتمكِّنك من استخدام بقية الأدوات الجزئية بفعالية أكبر لتحليل المشكلة. علاقة معدل الإطارات باستجابة الصفحة يُعرَّف معدل الإطارات تقليديًا بأنه معدّل التقاط الصور أو الإطارات بواسطة جهاز فيديو، وهو مألوف جدًا في عالم التصوير والألعاب، لكنه يستخدَم حاليًا على نطاق واسع في قياس أداء موقع ويب أو تطبيق ويب، حيث يمثِّل الإطار -عند تقييم الأداء في ويب- العمل الذي ينبغي على المتصفح تنفيذه لتحديث وإعادة رسم الشاشة، كما تُعَدّ الرسوم المتحركة النواحي الأكثر التي يُطبق فيها هذا المقياس، فإذا كان المعدل منخفضًا جدًا، فستكون استجابة الرسوم المتحركة بطيئةً وخشنةً، في حين سيكون عرض الرسوم أكثر سلاسةً عندما يكون معدل الإطارات أسرع، ومع ذلك يُعَدّ معدل الإطارات مفيدًا في قياس مدى استجابة المواقع عندما يتفاعل معها المستخدِم. إذا أدى تمرير مؤشر الفأرة على سبيل المثال فوق أحد عناصر صفحة ويب إلى وقوع حدث من أحداث جافاسكربت يغير مظهر ذلك العنصر، فسيؤدي ذلك إلى تنفيذ عمليتَي إعادة تخطيط العناصر ضمن الصفحة، ثم إعادة رسم الشاشة، ولا بد من اكتمال هذه العمليات جميعها في إطار واحد، فإذا استغرق أمر معالجة هذا الإطار من قِبَل المتصفح وقتًا طويلًا، فستتوقف استجابة المتصفح مؤقتًا، وكذلك الأمر عندما تتنقل ضمن صفحة تتطلب العديد من عمليات التحديث المعقدة بحيث لا يتمكن المتصفح من المحافظة على معدل إطارات مقبول، وهنا ستلاحظ بطأً في تمرير الصفحة صعودًا أو نزولًا وقد تتجمد أثناء ذلك، فعندما يكون معدل الإطارات مرتفعًا وثابتًا عمومًا، فسيريح ذلك المستخدِم ويجعل تجربته أكثر متعةً. يقدِّم معدل 60 إطار في الثانية أداءً سلسلًا ويتيح فترةً زمنيةً مقدارها 16.7 ميلي ثانية لإنجاز كل التحديثات المطلوبة، وذلك بالتزامن مع الاستجابة إلى بعض الأحداث، ولا بد من الإشارة إلى الأهمية الخاصة للاستمرارية أو ثبات معدل الإطارات، فإذا لم تستطع تحقيق 60 إطار في الثانية، فحاول تحقيق معدل أقل لكن على فترات زمنية طويلة -أي الاستمرارية-، وتفادي أيّ انخفاض مفاجئ قد يسبب جمود الصفحة. المخطط البياني لمعدل الإطارات ستجد هذا المخطط البياني في قسم النظرة العامة للتسجيل ضمن الأداة Performance، حيث يلتقط المخطط اللحظة الزمنية التي ينهي فيها المتصفح إطارًا، ويستخدِمها ليتتبع معدل الإطارات أثناء فترة التسجيل. يمثِّل المحور X الزمن المنقضي أثناء تسجيل ملف الأداء، كما ستلاحظ وجود ثلاث مؤشرات هي معدل الإطارات الأعظمي ومعدل الإطارات الوسطي ومعدل الإطارات الأقل. طريقة استخدام المخطط البياني لمعدل الإطارات تدل قيمة معدل الإطارات على بعض المشاكل المحتملة التي قد تواجهها الصفحة، وبالتالي سيساعدك ذلك في استخدام أدوات أخرى لتحليل مشاكل الأداء بعمق أكثر، وإليك على سبيل المثال لقطة الشاشة التالية لملف أداء: ستجد أنّ معدل الإطارات الوسطي جيد، لكنك ستلاحظ أيضًا ثلاث لحظات ينهار فيها معدل الإطارات خلال أعشار من الميلي ثانية، إذ يسبب ذلك دون أي شك تعثرًا ملحوظًا في عرض الرسوم المتحركة ضمن الصفحة، كما يرتبط معدل الإطارات مع عرض مختصر لعلامات الأداة waterfall فوقه مباشرةً، ولاحظ كيف ترافق أول انهيارين في معدل الإطارات في اللقطة السابقة مع شريط برتقالي يدل على الوقت المستغرَق في تنفيذ شيفرة جافاسكربت، فإذا اخترنا شريحةً تضم إحدى هاتين اللحظتين، فسيعرض قسم التفاصيل في الأسفل تفاصيل هذه الشريحة، وسنتابع الدالة التي سببت المشكلة: لاحظ كيف أعاقت دالة جافاسكربت استُدعيت عند حدث النقر خيط التنفيذ الرئيسي عن العمل مدة 170 ميلي ثانية، ولمعرفة هذه الدالة تحديدًا انتقل إلى الأداة Flame Chart لمتابعة مكدّس الاستدعاءات في هذه النقطة: هذه الدالة إذًا ()doPointlessComputations وهي دالة معرفة في الملف "main.js"، وقد نضطر لحل المشكلة إلى تقسيم الدالة إلى أجزاء، وتنفيذ كل جزء على حدة ضمن requestAnimationFrame أو تنفيذها بالكامل ضمن عامل ويب، وسنتابع لاحقًا حلولًا لمشاكل مثل هذه عندما نحلل تأثير الاستخدام المكثف لجافاسكربت على الأداء. الأداة Call Tree تساعدك هذه الأداة على تحديد دوال جافاسكربت التي يقضي المتصفح الوقت الأطول في تنفيذها، كما تساعدك عند تحليل النتائج التي تعرضها على إيجاد الاختناقات في شيفرتك، أي الأماكن التي يقضي فيها المتصفح وقتًا طويلًا بلا مبرر، إذ تُعَدّ نقاط الاختناق هذه هي الهدف الأفضل للتحسينات التي يمكن إجراؤها على الشيفرة والأكثر تأثيرًا. تُعَدّ الأداة Call Tree أداةً لتجميع العينات، إذ تلتقط دوريًا عينات لتحديد حالة محرك جافاسكربت وتسجيل حالة المكدّس في زمن تنفيذ الشيفرة، في حين يتعلق عدد العينات المجمّعة إحصائيًا عند تنفيذ دالة محددة بالزمن الذي يستغرقه المتصفح في تنفيذها، ولشرح فائدة الأداة Call Tree، سنستخدم خرج برنامج بسيط على أساس مثال، فإذا أردت الحصول على هذا البرنامج لتجربه بنفسك، فيمكنك تنزيله من المستودع الخاص به على جيت هاب، كما يمكنك تنزيل ملف الأداء الذي سنشرحه من المستودع ذاته وإدراجه بعد ذلك ضمن الأداة Performance، وستجد كذلك شرحًا مختصرًا عن هيكلية البرنامج ضمن المستودع. تعرض لقطة الشاشة التالية خرج برنامج يوازن بين ثلاثة خوارزميات للفرز، وهي الفرز الفقاعي Bubble Sort والفرز الانتقائي Selection Sort والفرز السريع Quicksort؛ حيث يولِّد البرنامج لتنفيذ الاختبار بعض المصفوفات التي تضم أرقامًا صحيحةً عشوائيةً، ثم يفرزها باستعمال كل خوارزمية على حدة، وقد ركزنا في قسم الحالة العامة Recording overview على منطقة ظهرت فيها علامة طويلة لجافاسكربت: تعرض الأداة Call Tree النتائج ضمن جدول يمثِّل فيه كل صف دالةً التَقطت منها الأداة عينةً واحدةً على الأقل ورتبت أسطرها حسب عدد العينات المأخوذة أثناء تواجدها في الدالة، ترتيبًا تنازليًا، بينما تمثِّل الأعمدة القيم التالية: العينات Samples: تشير إلى عدد العينات التي التُقطت عند تنفيذ دالة معينة بما في ذلك الدوال الأبناء وهي الدوال التي تُستدعيها هذه الدالة. الوقت الكلي Tota lTime: يقدَّر بالميلي ثانية ويمثِّل الوقت الكلي الذي تستغرقه شريحةً محددةً من التسجيل، كما يماثل تقريبًا عدد العينات. التكلفة الكلية Total Cost: يمثِّل النسبة المئوية للعدد الكلي للعينات في القسم المختار من التسجيل. التوقيت الذاتي Self Time: يمثِّل الوقت الذي يستغرقه تنفيذ دالة محددة دون النظر إلى زمن تنفيذ أبنائها، حيث يُقدَّر هذا الزمن من حالة المكدسات المُلتقَطة في اللحظات التي تكون فيها الدالة آخر عناصر المكدس leafmost function. التكلفة الذاتية Self Cost: تُحسب من التوقيت الذاتي على أساس نسبة مئوية للعدد الكلي من عينات القسم المحدد من التسجيل. تمثِّل الأعمدة السابقة القيم الأهم في النسخة الحالية من الأداة Call Tree، ويُفضَّل إجراء تحسين للشيفرة انطلاقًا من الدوال التي تمتلك قيمة تكلفة ذاتية مرتفعة لأنها تستغرق وقتًا طويلًا في التنفيذ، أو لأنها تُستدعى بكثرة. تخبرنا أخيرًا لقطة الشاشة السابقة عن شيء ربما نعرفه بالفعل، وهو أنّ خوارزمية الفرز الفقاعي فعالة جدًا، فعدد العينات الملتقَطة في القسم الذي يسجل عملها يزيد 6 مرات عن عدد عينات القسم الذي يسجل عمل خوارزمية الفرز الانتقائي، و13 مرة عن خوارزمية الفرز السريع. التنقل ضمن الأداة Call Tree يوجد سهم صغير إلى جوار اسم كل دالة، وسترى عند النقر على هذا السهم مسار استدعاء هذه الدالة رجوعًا إلى بدايته، أي من الدالة التي التقطت العينة أثناء تنفيذها وحتى الدالة الجذرية، إذ يمكن مثلًا توسيع الدالة ()bubbleSort: سيظهر المخطط البياني للاستدعاء كما يلي: sortAll() -> sort() -> bubbleSort() التُقِطت 253 عينة داخل الدالة ()swap، لكن يوجد مساران مختلفان يقودان إلى هذه الدالة، إذ تستخدِمها كلتا الدالتَين ()bubbleSort و()selectionSort، كما يمكننا ملاحظة أنّ 252 عينة من أصل 253 ضمن الدالة ()swap قد التُقِطت من مسار الدالة ()bubbleSort وعينةً واحدةً فقط من المسار الآخر. تشير هذه النتيجة إلى أنّ خوارزمية الفرز الفقاعي أقل فعاليةً مما نظن، فهي تتحمل في الواقع مسؤولية 252 عينة إضافية، أي حوالي 10% أخرى من التكلفة الكلية، كما يمكننا الحصول على المخطط البياني الكامل مع عدد العينات الموافق بمتابعة طريقة التحليل هذه: sortAll() // 8 -> sort() // 37 -> bubbleSort() // 1345 -> swap() // 252 -> selectionSort() // 190 -> swap() // 1 -> quickSort() // 103 -> partition() // 12 بيانات منصة العمل سترى بعض الأسطر التي تحمل قيمًا مثل Gecko أو Input & Events وغيرها، بحيث تمثِّل الاستدعاءات الداخلية للمتصفح، كما تقدِّم لك بيانات هذه الأسطر معلومات قيمةً أيضًا، فقد لا تجد أية عينات تشير إلى مدى صعوبة تنفيذ شيفرة موقعك في المتصفح، وستجد في مثالنا 679 عينة مرتبطة بالقيمة Gecko، وهي ثاني أكبر مجموعة من العينات بعد عينات الدالة ()bubbleSort، فلنوسِّع إذًا معلومات Gecko: تشيرالنتائج إلى أنّ مصدر 614 عينة من هذه العينات أو حوالي 20% من التكلفة الكلية هي من استدعاء الدالة ()sort، فإذا نظرنا إلى شيفرة هذه الدالة، فسنلاحظ بوضوح أنّ سبب التكلفة المرتفعة لعمل المنصة -أو مجهود المتصفح- هو الاستدعاء المتكرر للتابع ()console.log: function sort(unsorted) { console.log(bubbleSort(unsorted)); console.log(selectionSort(unsorted)); console.log(quickSort(unsorted)); } إذًا من الأفضل التفكير بطريقة أخرى لتنفيذ الأمر. لاحظ أيضًا أنّ زمن الخمول idle time يصنَّف على أساس Gecko، أي أنّ أقسام ملف الأداء التي لم تُنفَّذ أثناء تسجيلها أيّ شيفرة جافاسكربت أثناء التنفيذ ستصنف على أنها عينات Gecko، ولا يتعلق هذا التصنيف طبعًا بأداء الصفحة. الصيغة المعكوسة (المقلوبة) للأداة Call Tree ببساطة هي صيغة ينعكس فيها ترتيب جميع المكدّسات، بحيث تكون آخر الدوال المستدعاة في قمة المكدس، وبهذه الطريقة سيركِّز عرض البيانات أكثر على معلومات التوقيت الذاتي للدوال Self Time، وبالتالي ستحصل على معلومات أكثر حساسية في شيفرتك، ولإظهار الأداة بالصيغة المعكوسة، انقر على أيقونة التبديل في أقصى يمين نافذة الأداء ثم اختر Invert Call Tree. الأداة Flame Char تعرض لك هذه الأداة حالة مكدس جافاسكربت كل ميلي ثانية أثناء تسجيل ملف الأداء، كما يتيح لك ذلك معرفة الدالة التي يجري تنفيذها في كل لحظة من لحظات التسجيل وزمن تنفيذها ومسار استدعائها، كما تُستخدَم الأداتين Call Tree وFlame Chart في تحليل شيفرة جافاسكربت في موقعك وتستخدمان البيانات ذاتها المتمثلة بعينات من المكدس الخاص بمحرك جافاسكربت الملتقَطة دوريًا أثناء تسجيل ملف الأداء. ستعرض لك الأداة Flame Chart متى يجري تنفيذ دالة معينة خلال زمن التسجيل بينما تنظم الأداة Call Tree البيانات لعرض الدوال التي يقضي البرنامج وقتًا أكبر في تنفيذها، كما تعرض لك بصورة أساسية حالة مكدس الاستدعاء في أية لحظة من لحظات زمن التسجيل، وإليك لقطة شاشة تُظهر بيانات الأداة Flame Chart لشريحة من ملف الأداء: يمكننا في البداية ملاحظة أننا اخترنا شريحةً صغيرةً من التسجيل لعرض بيانات Flame Chart لها ضمن لوحة النظرة العامة، لأن البيانات التي تقدمها هذه الأداة كثيرة وتصعب متابعتها، لذلك كان لا بد من تضييق نطاق العرض، كما يمثِّل المحور X في لقطة الشاشة السابقة الزمن، في حين تغطي هذه اللقطة الفترة الزمنية الممتدة بين اللحظة 1435 ميلي ثانية و1465 ميلي ثانية، وتتوزع أيضًا على امتداد المحور Y، الدوال الموجودة في مكدس الاستدعاء خلال هذه الفترة من الدوال الأعلى مستوى في الأعلى إلى أخفضها مستوًى في الأسفل، وصُنِّفت الدوال وفق نظام لوني ليسهُل التمييز بينها. يمنحك هذا الأسلوب في عرض البيانات إمكانية معرفة الدالة التي يجري تنفيذها في أية لحظة خلال فترة التسجيل، والوقت المستغرَق في تنفيذها ومسار استدعائها. تغيير مجال العرض وإزاحة الشرائح ينبغي أن تكون قادرًا على التنقل بين البيانات المعروضة بسهولة لكي تستطيع العمل بفعالية أكبر، حيث تقدِّم الأداة Flame Chart وسيلتَي تحكم لهذا الغرض: Zoom (تغيير مجال العرض): لزيادة أو إنقاص مجال الشريحة المختارة من ملف التسجيل الكامل الذي يُعرَض ضمن الأداة Flame Chart، حيث تستطيع استخدام هذه الوسيلة كما يلي: دحرجة دولاب الفأرة للأعلى والأسفل ضمن Flame Chart. تحريك إصبعَين معًا للأعلى أو للأسفل على لوحة اللمس ضمن Flame Chart. Pan (التنقل بين الشرائح): إزاحة الشريحة التي اخترتها من ملف التسجيل الكامل يمينًا أو يسارًا، إذ تستطيع استخدام هذه الوسيلة كما يلي: انقر على الشريحة المختارة ضمن لوحة النظرة العامة واسحبها بالاتجاه المطلوب. انقر واسحب في أي مكان ضمن Flame Chart. مثال تطبيقي سنلقي نظرةً على مثال بسيط هو نفسه البرنامج الذي استخدمناه سابقًا لتتابع كيف ستكشف الأداة Flame Chart سلوك برنامجك ولتوضيح عمل الأداة Call Tree، كما سنستخدم ملف الأداء نفسه، فقد وجدنا أنُ تسلسل الاستدعاءات وتعداد العينات المقابل لكل منها في ملف الأداء هو كما يلي: sortAll() // 8 -> sort() // 37 -> bubbleSort() // 1345 -> swap() // 252 -> selectionSort() // 190 -> swap() // 1 -> quickSort() // 103 -> partition() // 12 اخترنا في البداية المقطع الذي كان البرنامج فيه مشغولًا بأكمله: لاحظ وجود الدالة ()sortAll الملونة بالأرجواني في القمة، حيث يمتد زمن تنفيذها من بداية البرنامج حتى نهايته، وتأتي تحتها مباشرةً الدالة ()sort باللون الأخضر الزيتوني، ويليها تعاقب استدعاءات مثل أسنان المشط لكل خوارزمية من خوارزميات الفرز الثلاث، ولنقرِّب المشهد أكثر: تمتد الشريحة مدة 140 ميلي ثانية وتعرض تفاصيلًا أكثر عن الدوال التي تستدعيها الدالة ()sort. لاحظ الشيفرة التي تكوّن الدالة ()sort: function sort(unsorted) { console.log(bubbleSort(unsorted)); console.log(selectionSort(unsorted)); console.log(quickSort(unsorted)); } تمثِّل العلامة التي يبدو عنوانها "…bubb" وملوّنةً بالأخضر الزيتوني الدالة ()bubbleSort، في حين تمثل العلامة الأخرى التي تبدو باللون الأخضر الصرف بقية دوال الفرز. لاحظ أنّ كتلة دالة الفرز الفقاعي ()bubbleSort ذات عرض أكبر -أي تمتد لفترة أطول- من البقية، كما يمكن ملاحظة مجموعة دوال تستدعيها الدالة ()bubbleSort وتبدو باللون الأرجواني، ولنقرّب المشهد مرة أخرى: يساوي عرض الشريحة الأخيرة التي اخترناها حوالي 20 ميلي ثانية، وسنرى بوضوح أنّ العلامة الأرجوانية أسفل الدالة ()bubbleSort هي استدعاءات للدالة ()swap، فإذا أحصينا هذه الاستدعاءات، فسترى أنها 253 وفقًا للأداة Call Tree، كما تشير Call Tree إلى وقوع كل الاستدعاءات السابقة تحت الدالة ()bubbleSort ماعدا استدعاء وحيد يقع تحت الدالة ()selectionSort، كما يمكن أيضًا رؤية وجود علامتين خضراوين للدالتين ()selectionSort و()quickSort، وسنرى استدعاءات لمنصة العمل في الفترة ما بين استدعاءات دوال الفرز، كما من المرجح أن يكون سببها استدعاء الدالة ()console.log من داخل الدالة ()sort. الأداة Allocations تعرض لك الأداة Allocations الدوال التي تحجز مساحةً أكبر من الذاكرة خلال فترة تسجيل ملف الأداء، ويُعَدّ الأمر مهمًا من ناحية الأداء، لأن حجز مساحات كبيرة من الذاكرة أو إشغال مناطق كثيرة منها قد يؤدي إلى وقوع حدث تجميع الموارد المستهلكة garbage collection، والذي يؤثر بدوره سلبًا على استجابة الصفحة، كما تُعَدّ هذه الأداة جديدةً في فايرفوكس 46. عليك تفعيل خيار تسجيل حجوزات الذاكرة Record Allocations ضمن إعدادات الأداة Performance قبل تسجيل ملف الأداء لكي تشاهد هذه الأداة، وعند تسجيل الملف بعد ذلك سترى نافذةً فرعيةً جديدةً عنوانها Allocations في شريط الأدوات. تشريح لوحة الأداة Allocations تبدو اللوحة كما في الصورة التالية: تأخذ الأداة عينات دوريًا من مساحات الذاكرة المحجوزة K بحيث يمثِّل كل صف دالةً أُخذِت عينة واحدة على الأقل من مساحة الذاكرة التي تحجزها أثناء تسجيل ملف الأداء، كما يضم جدول عرض البيانات الأعمدة التالية: Self Count العداد الذاتي: عدد عينات الحجز التي التُقِطت ضمن الدالة المحددة، وتُعرَض أيضًا على أساس نسبة مئوية من عدد العينات الكلي. Self Byte عدد البايتات الذاتي: العدد الكلي للبايتات الموجودة في عينة ملتقَطة داخل دالة، تُعرَض أيضًا على أساس نسبة مئوية من الكمية الكلية، كما تُرتَّب الأعمدة وفقًا لعدد البايتات الذاتي. سنجد في مثالنا السابق ما يلي: تمثل العينات الملتقَطة في الدالة ()signalLater ما مقداره 28.57% من العدد الكلي للعينات البالغ 8904. تحجز هذه العينات 1102888 بايت، أي ما مقداره 30.01% من الذاكرة الكلية المحجوزة في جميع العينات. سترى الأماكن التي تُستدعى منها كل دالة بالنقر على السهم الصغير بجوار اسمها: لاحظ كيف استُدعيت الدالة ()signalLater من مكانين هما ()removeInner و()setSelectionInner،إذ ستتمكن الآن من الرجوع خطوةً ضمن المكدس لفهم سياق حجز الذاكرة بصورة أفضل. التكلفة الذاتية والتكلفة الكلية تعرض لك الأداة مجموعتين من الأعمدة يبدأ عنوان الأولى بالكلمة Self والثانية بالكلمة Total، إذ تسجِّل الأولى العينات الملتقَطة من دالة واحدة، بينما تسجِّل الثانية العينات الملتقَطة من هذه الدالة أو الدوال التي استدعتها هذه الدالة، كما ستتطابق هاتين المجموعتين بالنسبة للدوال التي تمثل قمة المكدس طالما أن الدوال الأخيرة في المكدس ستظهر في الأعلى، أي ما نراه هو صيغة معكوسة لمكدس الاستدعاء، لكن إذا بدأت التراجع إلى الخلف في المكدس، فسترى الفرق بين المجموعتين: التقَطت الأداة 8904 عينةً في الدالة ()signalLater، لكن هذه الدالة قد استُدعيت من مكانين ()removeInner و()setSelectionInner، وتبدو قيمة العداد الذاتي لهما 0 بمعنى أنهما لا تحجزان مباشرةً أية ذاكرة، لكن قيمة العداد الكلي للدالة ()removeInner هي 8901 وقيمته للدالة ()setSelectionInner هي 3 فقط، أي أنّ معظم الحجوزات التي نراها في الدالة ()signalLater مصدرها الدالة ()removeInner. حجز الذاكرة وتجميع الموارد المستهلكة إنّ أية معلومات عن الذاكرة التي يحجزها موقع جديرة بالاهتمام، لكن الرابط الحقيقي بين استجابة موقع وكمية الذاكرة المحجوزة هو موضوع تجميع الموارد المستهلكة GC، كما يتحقق محرك التنفيذ من وجود كائنات مستهلكة لا يمكن الوصول إليها في كومة الذاكرة الخاص بالبرنامج في جميع اللغات التي تعتمد أسلوب تجميع الموارد المستهلكة مثل جافاسكربت، ثم يحرر الذاكرة التي تحجزها تلك الكائنات، وعند تنفيذ هذه العملية يتوقف محرك جافاسكربت مؤقتًا ويُعلَّق عمل البرنامج وتتوقف استجابته. يُنفِّذ SpiderMonkey محرك جافاسكربت على فايرفوكس هذه العملية تصاعديًا بخطوات صغيرة لتخفيف هذا الأثر على الاستجابة، فيسمح باستمرارية عمل البرنامج ما بين هذه الخطوات، لكن لا بد في بعض الحالات من تنفيذ عملية تجميع موارد مستهلكة لا تصاعدي، وبالتالي على البرنامج الانتظار حتى نهاية العملية، كما تظهر أحداث تجميع الموارد المستهلكة باللون الأحمر في لوحة الأداة Waterfall، وقد تلاحظ رايات حمراء طويلة ترتبط بانعدام استجابة البرنامج قد تمتد فترة عدة مئات من الميلي ثانية. ما الذي تستطيع فعله عندما ترى أحداث GC في ملف أداء برنامجك؟ يستخدِم SpiderMonkey مجموعةً معقدةً من الطرق الاستدلالية لتقدير الحاجة إلى استخدام طريقة معينة في تجميع الموارد المستهلكة، لكن الضغوطات الناتجة عن حجز الذاكرة -أي حجز مساحات كبيرة أو معدل حجز مرتفع للذاكرة- ستدفع SpiderMonkey إلى تنفيذ عمليات GC، وعلى الأغلب التجميع الكامل اللاتصاعدي عوضًا عن التصاعدي. إذا وقعت أحداث تجميع الموارد المستهلكة بسبب ضغوطات ناتجة عن حجز الذاكرة، فسيظهر في هذه الحالة رابطًا بعنوان عرض المسببات الناتجة عن حجز الذاكرة Show Allocation Triggers ضمن الشريط الجانبي إلى يمين نافذة الأداة Waterfall، فإذا نقرت على هذا الرابط، فستنتقل أدوات التطوير إلى عرض النافذة Allocations وستختار الفترة الزمنية الممتدة من نهاية آخر دورة لتجميع الموارد المستهلكة حتى بداية الدورة التي نقرت عليها، وسيعرض لك ذلك كل حجوزات الذاكرة التي أدت بمجموعها إلى وقوع حدث تجميع الموارد المستهلكة، فإذا واجهتك هذه المشكلة، فحاول إذا استطعت اختزال عدد أو حجم حجوزات الذاكرة: هل يمكنك حجز ذاكرة محدودة؟ أي فقط عندما تحتاجها بدلًا من حجزها منذ البداية. هل تجري عملية الحجز ضمن حلقة؟ إذا كان الأمر كذلك، فهل بالإمكان إعادة استخدام الذاكرة المحجوزة نفسها عند كل تكرار للحلقة؟ ترجمة -وبتصرف- للمقالات التالية: Waterfall. Frame rate. Call Tree. Flame Chart. Allocations. اقرأ أيضًا مراقبة وتحليل أداء صفحات الويب باستخدام الأداة Performance تحسين الظهور في محركات البحث
  22. يصف هذا المقال أداةً غايةً في الأهمية لتحليل أداء صفحات ويب فيما يتعلق بتنفيذ شيفرات HTML وCSS وجافاسكربت، وبالتالي سيكون المطوِّر قادرًا على تحديد النقاط السلبية في أداء الموقع والتي ستكون الهدف المناسب لعمليات التحسين والتطوير التالية. نظرة سريعة على الأداة Performance تلقي هذه الأداة الضوء على استجابة صفحات ويب عمومًا، وعلى أداء جافاسكربت وتخطيط الصفحة، كما تتيح هذه الأداة إنشاء تسجيلات أو ملفات أداء لصفحتك خلال فترة زمنية معينة، وتعرض لك لمحةً عامةً عن الأشياء التي نفّذها المتصفح لتصيير الصفحة من خلال تلك الملفات، ورسمًا بيانيًا عن حالة مكدس جافاسكربت مع الزمن من خلال المكوّن frame rate. مكونات الأداة Performance تقدِّم لك الأداة أربع أدوات جزئية لفحص ميزات ملف الأداء بتفاصيل أكثر: الأداة Waterfall: تعرض لك العمليات المختلفة التي ينفذها المتصفح مثل تنفيذ مخطط العمل وأداء جافاسكربت ومهام إعادة رسم الصفحة repaint وتجميع الموارد التي انتهى استخدامها Garbage Collection، أي ستساعدك في فهم ما ينفِّذه المتصفح عندما يتفاعل مع موقع ويب. الأداة Call Tree: تعرض دوال جافاسكربت التي تستغرق من المتصفح أكبر وقت في تنفيذها محددةً الاختناقات التي تحدث عند تنفيذ شيفرة جافاسكربت الخاصة بموقع الويب. الأداة Flame Chart: تعرض حالة مكدس استدعاءات جافاسكربت والدوال التي يجري تنفيذها ومتى خلال فترة تسجيل ملف الأداء. الأداة Frame Rate: تعرض حالة التجاوب العامة لموقعك مع مختلف الشاشات. الأداة Allocations: تعرض حالة كومة الذاكرة الناتج عن تنفيذ شيفرة جافاسكربت خلال زمن تسجيل ملف الأداء، ولن تظهر هذه الأداة إلا عندما تفعِّل خيار سجل حجوزات الذاكرة Record Allocations من خلال إعدادات الأداة Performance. سيناريوهات استخدام الأداة Performance يمكن استخدام أدوات جزئية محددة أو أكثر لمراقبة حالات محددة نذكر منها: متابعة الحركات الناتجة عن استخدام خصائص CSS: استخدم أداة Waterfall لفهم الآلية التي يُحدِّث فيها المتصفح صفحة ويب وتأثير الحركات التي تنتج عن تغير قيم خصائص CSS على الأداء. الاستخدام المكثَّف لجافاسكربت: استخدام الأداتين frame rate وWaterfall وللاطلاع على مشاكل الأداء الناتجة عن تنفيذ شيفرة جافاسكربت وكيف يساعد استخدام عمّال ويب web workers في حالات مثل هذه. واجهة مستخدم الأداة Performance تتألف واجهة المستخدِم من أربعة أقسام رئيسية: شريط الأدوات Toolbar. لوحة التسجيلات Recordings pane. لوحة نظرة عامة Recording overview. لوحة التفاصيل Details pane والتي قد تعرض أيًا من: تفاصيل الأداة الجزئية Waterfall. تفاصيل الأداة الجزئية Call Tree. تفاصيل الأداة الجزئية Flame Chart. شريط الأدوات يضم شريط الأدوات أزررًا مهمتها ما يلي: تشغيل وإيقاف عملية تسجيل ملف الأداء. إدراج ملف أداء مخزن سابقًا. مسح لوحة السجلات، وانتبه أنك ستفقد أية سجلات غير مخزّنة إذا فعلت ذلك. ترشيح العلامات markers التي تُعرض عند استخدام الأداة الجزئية Waterfall. الانتقال بين تفاصيل الأدوات الجزئية في لوحة التفاصيل. الوصول إلى نافذة الإعدادات المنبثقة. لوحة التسجيلات تعرض هذه اللوحة جميع التسجيلات التي تحمّلها، بما في ذلك ما سجّلته في هذه الجلسة وما استوردته من تسجيلات سابقة. يُعرَض تسجيل واحد فقط وتصطف بقية التسجيلات على هيئة قائمة ضمن الأداة، كما يمكنك اختيار أيّ تسجيل لعرضه بالنقر عليه؛ أما لتخزين التسجيل على صورة ملف JSON، فانقر على زر حفظ Save. لوحة النظرة العامة تعرض لمحة عامة عن التسجيل بأكمله مع الزمن الذي يمثله المحور X، كما تحوي اللوحة عنصرين هما لمحة عامة عن الأداة Waterfall ورسم بياني لمعدل الإطارات frame rate. لمحة عامة عن الأداة Waterfall تتضمن عرضًا مصغّرًا عن الأداة Waterfall وتُمثِّل العمليات المُسجَّلة وفق نظام لوني مشابه للمخطط المعتمد في الأداة Waterfall الرئيسية. الرسم البياني لمعدل الإطارات تعطيك لمحةً عامةً عن استجابة المتصفح خلال فترة التسجيل، كما يمكنك الاطلاع على المقال المكوّنات الرئيسية للأداة Performance لمعلومات أكثر. ترابط الأحداث يمكنك ربط الأحداث بين العنصرين السابقين طالما أنهما متزامنان، فإذا نظرنا مثلًا إلى لقطة الشاشة التي نعرضها تاليًا، فستلاحظ أنّ عملية رسم مطوّلة لصفحة ويب، والتي تظهر على أساس شريط أخضر في العرض المصغّر للأداة Waterfall يقابلها انخفاض في معدل الإطارات. نظرة أقرب إلى العرض يمكنك اختيار شريحة من عرض النظرة العامة للتسجيل لتفحص تفاصيله، فعندما تختار شريحةً ما، فسيتغير محتوى لوحة التفاصيل لتحتوي فقط على تفاصيل عن الشريحة التي اخترتها، ولاحظ في لقطة الشاشة التالية انخفاض معدل الإطارات في الشريحة المقابل لعملية الرسم الطويلة لصفحة الويب بتفاصيل أكثر. لوحة التفاصيل تعرض تفاصيل الأداة الجزئية التي تختارها، كما يمكنك الانتقال من أداة إلى أخرى عبر الأزرار في شريط الأدوات. الأداة Waterfall تقدِّم الأداة عرضًا عن العمل الذي ينفذه المتصفح خلال فترة التسجيل مثل تنفيذ شيفرة جافاسكربت، وتحديث تنسيق الصفحة ومخططها وتنفيذ عمليات إعادة رسم الصفحة، كما يمثِّل المحور X زمن التسجيل وتتعاقب العمليات المسجلة على هيئة شلال لتعكس الطبيعة التسلسلية لآلية تنفيذ الشيفرة في المتصفح. الأداة Call Tree تمثِّل محللًا للعينات يأخذ بياناته من تنفيذ شيفرة جافاسكربت لصفحة الويب، إذ تأخذ الأداة عينات دوريًا من محرك جافاسكربت وتسجِّل بيانات عن الشيفرة التي تُنفّذ لحظة التقاط العينة، كما يتعلق عدد العينات التي تلتقط عند تنفيذ دالة محددة بالزمن المستغرق لتنفيذها، وبالتالي ستكون قادرًا على تحديد الاختناقات في شيفرتك. الأداة Flame chart تخبرك هذه الأداة عن حالة مكدس الاستدعاءات في كل لحظة من لحظات التسجيل. الأداة Allocations تشابه هذه الأداة الجديدة في فايرفوكس 46 الأداة Call Tree لكن لمواقع حجز الذاكرة، إذ تعرض لك الدوال التي تحجز ذاكرةً أكبر خلال فترة تسجيل ملف الأداء، ولن تظهر هذه الأداة إلا عندما تفعّل خيار سجِّل حجوزات الذاكرة Record Allocations من خلال إعدادات الأداة Performance. لمعلومات أكثر عن الأدوات الجزئية اطلع على المقال المكوّنات الرئيسية للأداة Performance. التعامل مع الأداة Performance إليك بعض الإرشادات للتعامل مع الأداة Performance تشغيل الأداة لتشغيل الأداة: اضغط المفتاحين Shift + F5 اختر Performance من القائمة الفرعية أدوات مطوري ويب Web Developer الموجودة ضمن قائمة فايرفوكس أو قائمة الأدوات Tools، وهذا إذا كنت تعرض شريط القائمة أو كانت على نظام ماك أو إس OS X. اختر Performance من زر الأدوات في شريط الأدوات إذا كان موجودًا على متصفحك. تسجيل ملف أداء انقر على أيقونة الساعة في لوحة التسجيلات لبدء التسجيل وانقرها مجددًا لإيقافه، كما يمكنك بدء وإيقاف التسجيل من خلال الأداة Web Console باستخدام الأمرَين ()console.profile و()console.profileEnd. حفظ ملف أداء انقر على الرابط الذي يحمل العنوان "حفظ Save" إلى جوار التسجيل في لوحة التسجيلات. تحميل ملف أداء انقر على زر استيراد Import ثم اختر الملف الذي تريد استيراده. حذف جميع الملفات المدرجة انقر على أيقونة سلة المهملات أو على زر Clear وانتبه إلى أنك ستفقد أية سجلات غير مخزّنة إذا فعلت ذلك. اختيار أداة انقر على الزر الموافق للأداة ضمن شريط الأدوات. اختيار العلامات المعروضة اضغط على زر المرشح Filter في شريط الأدوات لاختيار العلامات التي تريد إظهارها في الأداة Waterfall. إلقاء نظرة أقرب على الأداء اختر شريحةً محددةً من لوحة النظرة العامة للتسجيل وستعرض تفاصيل هذه الشريحة في لوحة التفاصيل. ترجمة -وبتصرف- للمقالات التالية: ?What tools are available to debug and improve website performance. ?How to Open the Performance tools. UI Tour. اقرأ أيضًا تحسين الظهور في محركات البحث HTML و CSS للمبتدئين: كيف تصمم أول صفحة ويب لك
  23. تحتوي كل متصفحات الويب الحديثة مجموعة أدوات فعّالة لمطوري الويب تنفذ مجالًا واسعًا من الوظائف من فحص ملفات HTML وCSS وجافاسكريبت إلى عرض ملف دعم طلبته الصفحة والمدة التي يستغرقها حتى يُحمّل. يشرح هذا المقال كيفية استعمال الوظائف الأساسية لأدوات مطوري ويب DevTools الخاصة بمتصفحك. فتح أدوات مطوري ويب في متصفح ستجد أدوات مطوري الويب ضمن نافذة فرعية تبدو نوعًا ما كالصورة التالية وذلك بحسب المتصفح الذي تستخدمه. لكن كيف ستظهر هذه الأدوات؟ باستخدام لوحة المفاتيح: اضغط على الأزرار CTRL + Shift + I، ماعدا المتصفحات التالية: مايكروسوفت إنترنت إكسبلورر وإيدج: اضغط على F12. ماك أو إس: اضغط على ⌘ + ⌥ +I. باستخدام شريط القائمة Menu Bar: اضغط زر القائمة ثم: فايرفوكس: اضغط على "مطوري ويب Web Developer" ثم اختر "تبديل الأدوات Toggle Tools" أو "أدوات Tools" ثم اختر "مطوري الويب Web Developer" ثم اختر "تبديل الأدوات Toggle Tools". كروم: اضغط على "المزيد من الأدوات More Tools" ثم اختر "أدوات المطورين Developer Tools". سفاري: اضغط على "تطوير Develop" ثم اختر "إظهار فاحص الويب Show Web Inspector"، وإن لم تجد قائمة "تطوير Develop" انتقل إلى المتصفح سفاري Safari ثم اختر "تفضيلات Preferences" ثم اختر "متقدم Advanced" ثم فعّل الخيار "أظهر قائمة التطوير في شريط القائمة Show Develop menu in menu bar". أوبرا: اضغط على "مطورون Developer" ثم اختر "أدوات المطورين Developer tools". باستخدام قوائم السياق Context Menu: اضغط باستمرار بالزر الأيمن للفأرة على أي عنصر في صفحة الويب (انقر CTRL في ماك) واختر "فحص عنصر Inspect Element" من قائمة السياق التي ستظهر (فائدة إضافية: تظلل هذه الطريقة مباشرة شيفرة العنصر الذي نقرت عليه). فحص شجرة العناصر DOM ومحرر تنسيقات CSS تُظهر أدوات مطوري ويب عند فتحها نافذة الفاحص Inspector افتراضيًا والذي يبدو تقريبًا كما تعرضه لقطة الشاشة التالية: تعرض هذه الأداة نتيجة شيفرة HTML في زمن التنفيذ، كما تعرض تنسيقات CSS المطبقة على كل عنصر من عناصر الصفحة. تتيح لك الأداة أن تعدّل عناصر HTML وCSS أيضًا، وأن تشاهد أثر التغييرات التي أحدثتها مباشرة في المتصفح. إن لم تظهر نافذة الفاحص مباشرة: اضغط أو المس النافذة "فاحص Inspector" في فايرفوكس. اضغط أو المس النافذة "مستكشف DOM Explore" أو اضغط CTRL + 1 في إنترنت إكسبلورر. اضغط أو المس النافذة "عناصر Elements" في مايكروسوفت إيدج وأوبرا وكروم. لا تظهر عناصر التحكم بصورة واضحة في سفاري، لكن من المفترض أن ترى نافذة HTML إن لم تختر شيئًا آخر. اضغط على زر "تنسيق Style" لعرض تنسيقات CSS. استكشاف عمل فاحص DOM انقر في البداية بالزر اليميني للفأرة (أو اضغط CTRL) على عنصر HTML ضمن فاحص شجرة DOM وتحقق من محتويات قائمة السياق التي ستظهر. تختلف عناصر القائمة من متصفح لآخر، لكن المهمة منها موجودة غالبًا في جميع المتصفحات. حذف عقدة Delete Node: (تجدها أحيانًا "حذف عنصر Delete Element") تحذف العنصر المحدد. تحرير كملف HTML: (تجده أحيانًا "إضافة سمة Add Attribute/‎ تحرير نص Edit Text") وذلك لتعديل ملف HTML ومشاهدة نتيجة التعديلات مباشرة، وهو مفيد جدًا للتنقيح والاختبار. تمرير Hover/‎ تفعيل Active/‎ تركيز Focus: تجبر العنصر على تغيير الحالة التي يُعرض بها، وبالتالي ستتمكن من مشاهدة تنسيقه في كل حالة. نسخ Copy/‎ نسخ بصيغة HTML: عنصر HTML الذي تختاره. يمكن أن تجد في بعض المتصفحات خيارات مثل "نسخ CSS" و"نسخ Xpath" لكي تنسخ مُحدِّد CSS أو تعبير Xpath المرتبط بعنصر HTML الحالي. حاول أن تعدّل في شجرة DOM، انقر نقرًا مزدوجًا على عنصر ثم انقر بالزر اليميني عليه واختر "تحرير كملف HTML" من قائمة السياق التي تظهر. يمكنك إحداث التغييرات التي تريد لكن ليس بالإمكان حفظ هذه التغييرات. استكشاف محرر تنسيقات CSS يعرض محرر CSS افتراضيًا القواعد المطبقة على العنصر المختار: تُساعدك هذه الميزات في النقاط التالية: عرض قواعد CSS المطبقة على العنصر الحالي مرتبة من الأكثر تخصيصًا لهذا العنصر إلى الأقل. عرض ما يحدث عند حذف تصريح ما، ولكن لا بدّ أن تنقر قبل ذلك على صندوق التحقق الموجود إلى جوار التصريح. عرض المكافئات الطويلة لخاصية محددة عند النقر على السهم الصغير الموجود إلى جوارها. عرض نتيجة تغيير التنسيق مباشرة بعد وضع قيم جديدة للخاصيات. انقر على اسم الخاصية أو قيمتها ليظهر لك مربع نص ثم اكتب ضمنه القيمة الجديدة. ستجد إلى جانب قاعدة اسم الملف ورقم السطر الذي عُرِّفت القاعدة ضمنه. انقر على القاعدة لتنتقل إلى عرض القاعدة في مكان وجودها الأصلي حيث يمكنك عادةً تعديلها وحفظ التعديلات التي أجريتها. يمكنك أن تنقر أيضًا على القوس المعقوص لأي قاعدة لإظهار مربع نص ضمن سطر جديد إذ يمكنك كتابة قاعدة جديدة كليًا وفقًا لما تحتاج. لاحظ وجود عدة نوافذ فرعية يمكن النقر عليها في نافذة CSS: التنسيق المحوسب Computed: وتعرض لك التنسيق كما هو مطبق على العنصر (القيم النهائية الجاهزة للتطبيق ضمن المتصفح). عرض المخطط Layout: تتكون هذه المنطقة في فايرفوكس من: نموذج صندوقي Box Model: ويعرض نموذجًا صندوقيًا بصريًا لخصائص العنصر لتتمكن مباشرة من الاطلاع على الهوامش والحدود والمساحات الفارغة حوله، وحجم المحتوى الذي يضمه. شبكة العرض Grid: إن استخدام تنسيق الشبكة CSS Grid ضمن الصفحة التي تتفحصها، سيساعدك هذا القسم على رؤية تفاصيل هذه الشبكة. الخطوط Fonts: وتظهر الخطوط المطبقة على العنصر. منقح جافاسكربت يتيح منقح جافاسكربت مراقبة قيمة المتغيرات وضبط نقاط إيقاف التنفيذ Breakpoints، وهي أماكن في شيفرتك تريد إيقاف تنفيذ الشيفرة عندها لتتحقق من المشاكل التي تمنع الشيفرة من العمل بالطريقة المطلوبة. للوصول إلى المنقح: فايرفوكس: اضغط على زر القائمة ثم اضغط على "أدوات المطور Web Developer" ثم اضغط على "المنقح Debugger". كما يمكن الضغط على المفاتيح CTRL + Shift + I معًا. وإن كانت أدوات تطوير ويب مفتوحة مسبقًا، اضغط فقط على نافذة "المنقح Debugger". إنترنت إكسبلورر 11 وإيدج: اضغط على المفتاح F12 ثم المفتاحين CTRL + 3. إن كانت أدوات مطوري ويب مفتوحة مسبقًا، انقر على نافذة "المنقح Debugger". سفاري: افتح نافذة أدوات مطور ويب ثم اضغط على نافذة "المنقح Debugger". استكشاف منقح جافاسكربت ستجد ثلاثة أقسام في منقح جافاسكربت على فايرفوكس قائمة الملفات وهو القسم الأول على يسار نافذة المنقح، يتضمن لائحة الملفات المرتبطة بالصفحة التي تنقحها. اختر الملف الذي ترغب بالعمل عليه بالنقر عليه لتظهر محتوياته ضمن القسم الأوسط من نافذة المنقح. الشيفرة المصدرية وتساعدك في وضع نقاط إيقاف التنفيذ أينما ترغب في ملف الشيفرة. لاحظ نقطة الإيقاف في الصورة التالية، وهي عند السطر المظلل الذي يحمل الرقم 18: مراقبة التعابير ونقاط إيقاف التنفيذ تعرض لك هذه الأداة العبارات التي أضفتها ونقاط إيقاف التنفيذ التي وضعتها، وهي موجودة على يمين نافذة المنقح. تُرتّب نقاط إيقاف التنفيذ في القسم الثاني "نقاط إيقاف التنفيذ Breakpoints". لاحظ كيف وضعت نقطة إيقاف في ملف الشيفرة "example.js" قبل السطر البرمجي: listItems.push(inputNewItem.value); يظهر القسمان الأخيران فقط عند تنفيذ الشيفرة. إذ يعرض القسم "مكدس الاستدعاءات Call Stack" الشيفرة التي جرى تنفيذها حتى الوصول إلى السطر الحالي. لاحظ أنّ الشيفرة موجودة في الدالة التي تعالج نقرة الفأرة، وأن الشيفرة في حالة إيقاف بفعل نقطة الإيقاف. وأخيرًا يعرض القسم "المجالات Scopes" القيم المختلفة التي تنتجها الشيفرة وبالإمكان متابعتها. تعرض الصورة التالية على سبيل المثال الكائنات الموجودة في الشيفرة إضافة إلى الدالة "AddItemClick". طرفية جافاسكربت وهي أداة غاية في الفائدة لتنقيح ملفات جافاسكربت التي لا تعمل كما هو متوقع. تسمح لك الأداة بتنفيذ أسطر الشيفرة بالموازنة مع الصفحة التي يعرضها المتصفح، وتبلغك بالأخطاء التي يصادفها المتصفح عندما يحاول تنفيذ الشيفرة. للولوج إلى طرفية جافاسكربت المدمجة مع المتصفح: إن كانت نافذة أدوات مطوري ويب مفتوحة انقر على نافذة "طرفية Console"، وإن لم تكن مفتوحة يمكنك في فايرفوكس الضغط على المفاتيح CTRL + SHIFT + K معًا أو من خلال: اضغط على رمز القائمة ثم اضغط على "أدوات المطورين Web Developer" ثم اضغط على "طرفية ويب Web Console" أو "أدوات Tools" ثم اضغط على "أدوات المطورين Web Developer" ثم اضغط على "طرفية الويب Web Console". في بقية المتصفحات، افتح نافذة أدوات مطوري ويب ثم انقر على نافذة الطرفية. ينتج عن فتح الطرفية نافذة مشابهة للنافذة في الصورة التالية: لتختبر الطرفية، حاول كتابة مقطع الشيفرة التالي ضمنها مقطعًا مقطعًا (ثم اضغط "Enter"). لنكتب أولًا التعليمة التالية: alert('hello!'); ثانيًا جرب كتابة الشيفرة التالية: document.querySelector('html').style.backgroundColor = 'purple'; const myWordmark = document.createElement('img'); myWordmark.setAttribute('src','https://blog.mozilla.org/press/wp-content/themes/OneMozilla/img/mozilla-wordmark.png'); document.querySelector('h1').appendChild(myWordmark); حاول الآن إدخال نسخة خاطئة من الشيفرة وراقب ما يحدث: alert('hello!); أو هكذا: document.cheeseSelector('html').style.backgroundColor = 'purple'; const myWordmark = document.createElement('img'); myBanana.setAttribute('src','https://blog.mozilla.org/press/wp-content/themes/OneMozilla/img/mozilla-wordmark.png'); document.querySelector('h1').appendChild(myWordmark); ستبدأ بملاحظة طبيعة الأخطاء التي يعيدها المتصفح. يمكن أن تكون هذه الأخطاء مشفرة، ولكن من السهل تحليلها وإيجاد المشاكل الكامنة وراءها. ترجمة -وبتصرف- للمقال How does the Internet work. اقرأ أيضًا مدخل إلى أدوات التطوير في متصفح الويب DevTools أدوات المطور ما هي الأدوات المستخدمة في بناء مواقع ويب؟ كيف تستخدم أدوات المطوِّر في المتصفحات الحديثة كيف تستخدم أدوات المطور DevTools في Chrome
  24. سنلقي في هذا المقال نظرة على مدير الحزم Package Manager بشيء من التفصيل لفهم كيفية استخدامه في مشاريعنا لتثبيت اعتماديات Dependencies أدوات المشروع وتحديثها وغير ذلك. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت. الهدف: فهم مدراء ومستودعات الحزم، وسبب حاجتنا لها، وأساسيات استخدامها. اعتماديات المشروع تعدّ الاعتمادية Dependency جزءًا من برنامج تابع لجهة خارجية كتبه شخص ما غيرك لحل مشكلة نيابة عنك. يمكن أن يحتوي مشروع الويب على أي عدد من الاعتماديات، تتراوح من لا شيء إلى اعتماديات متعددة، ويمكن أن تتضمن اعتمادياتك اعتماديات فرعية لم تثبّتها صراحة، إذ يمكن لاعتمادياتك أن يكون لها اعتمادياتها الخاصة. من الأمثلة البسيطة على الاعتماديات المفيدة التي يمكن أن يحتاجها مشروعك الشيفرة البرمجية لحساب التواريخ النسبية بوصفها نصًا قابلًا للقراءة. يمكنك بالتأكيد كتابة هذه الشيفرة بنفسك، ولكن يمكن أن يحل شخص آخر هذه المشكلة بدلًا عنك، فلا داعي لنضيع الوقت في إعادة كتابة هذه الشيفرة (ليس علينا إعادة اختراع العجلة). يُحتمَل اختبار اعتمادية موثوقة لطرف ثالث في مواقف متعددة مختلفة، مما يجعلها أقوى وأكثر توافقًا من حلك مع المتصفحات. يمكن أن تكون اعتمادية المشروع عبارة عن مكتبة أو إطار عمل جافاسكربت كامل مثل React وVue أو أداة مساعدة صغيرة جدًا مثل مكتبة التاريخ القابلة للقراءة، أو يمكن أن تكون أداة سطر أوامر مثل Prettier أو Eslint التي تحدثنا عنها في المقال السابق. إن لم نستخدم أدوات البناء الحديثة، فيمكن تضمَّين مثل هذه الاعتماديات في مشروعك باستخدام عنصر <script> بسيط، ولكن يمكن ألا تعمل هذه الطريقة مباشرة، وبالتالي ستحتاج بعض الأدوات الحديثة لتجميع الشيفرة والاعتماديات مع بعضها بعضًا عند إصدارها على الويب. الحزمة Bundle هي المصطلح المُستخدَم للإشارة إلى ملف على خادم الويب الخاص بك الذي يحتوي على شيفرة جافاسكربت الخاصة ببرنامجك، وتُضغَطُ الحزمة قدر الإمكان للمساعدة في تقليل الوقت الذي يستغرقه تنزيل البرنامج وعرضه في متصفح زوار موقعك. لا يُعَد تتبّع بعض الاعتماديات أمرًا صعبًا إذا وجدت أداة أفضل تريد استخدامها بدلًا من الأداة الحالية، أو أُصدِر إصدار جديد من الاعتمادية تريد التحديث إليه، ولكن يمكن أن يصبح هذا النوع من الأشياء صعبًا في المشاريع الكبيرة التي تحتوي على اعتماديات متعددة، لذلك يمكنك استخدام مدير حزم Package Manager مثل npm، لأنه سيضمن إضافة الشيفرة وإزالتها بطريقة نظيفة، بالإضافة إلى مجموعة من المزايا الأخرى. مدير الحزم مدير الحزم هو نظام يدير اعتماديات مشروعك، ويوفّر طريقة لتثبيت اعتماديات جديدة يُشار إليها باسم "الحزم Packages"، وإدارة مكان تخزين الحزم على نظام ملفاتك، وتقديم إمكانيات لنشرها. يمكنك تنزيل اعتماديات مشروعك وتخزينها يدويًا دون استخدام مدير حزم، ولكن مدير الحزم سيتعامل بسلاسة مع تثبيت الحزم وإلغاء تثبيتها. إن لم تستخدم مدير حزم، فسيتعين عليك التعامل مع ما يلي يدويًا: العثور على كافة ملفات جافاسكربت الصحيحة الخاصة بالحزمة. التحقق منها للتأكد من عدم وجود أي ثغرات أمنية. تنزيلها ووضعها في الأماكن الصحيحة في مشروعك. كتابة الشيفرة لتضمين الحزمة أو الحزم في تطبيقك، إذ يمكن تطبيق ذلك باستخدام وحدات جافاسكربت. تطبيق الخطوات نفسها مع جميع اعتماديات الحزم الفرعية التي يُحتمَل وجود عشرات أو مئات منها. إزالة جميع الملفات مرة أخرى إذا أردت إزالة الحزم. كما يتعامل مدير الحزم مع الاعتماديات المكرَّرة، وهو أمر مهم وشائع في تطوير الواجهة الأمامية. لديك خياران لمكان تثبيت اعتمادياتك في حالة npm ومدير الحزم المستند إلى جافاسكربت وNode، ويمكن تثبيت الاعتماديات بطريقة عامة أي لكل المشاريع أو محليًا في مشروع معين فقط. بالرغم من وجود كثير من إيجابيات التثبيت العام، إلّا أن إيجابيات التثبيت المحلي لكل مشروع مهمة مثل قابلية نقل الشيفرة وقفل الإصدار. إذا اعتمد مشروعك مثلًا على Webpack مع إعداد معين، فيجب التأكد من عمل هذا الإعداد عند تثبيت المشروع على جهاز آخر أو العودة إليه لاحقًا. إذا ثُبِّت إصدار مختلف من Webpack، فيمكن ألّا يكون متوافقًا، لذلك يجب تثبيت الاعتماديات محليًا في المشروع. حاول تنزيل وتشغيل مشروع قائم، فإذا نجح وعملت جميع الاعتماديات مباشرة، فعندئذٍ لديك اعتماديات محلية يجب أن تشكرها على حقيقة أن الشيفرة قابلة للنقل. سجلات الحزم Package Registries يحتاج مدير الحزم ليعمل إلى معرفة المكان الذي نثبّت الحزم منه، وهو ما يدعَى بسجل الحزمة Package Registry. السجل هو مكان مركزي تُنشَر الحزمة عليه وبالتالي يمكن تثبيتها منه. يُعدُّ npm اسم سجل الحزم الأكثر استخدامًا لحزم جافاسكربت. ليس npm الخيار الوحيد لإدارة سجل حزمتك، إذ تتيح لك منتجات مثل Microsoft Azure إنشاء وكلاء لسجل npm إذ يمكنك تجاوز أو قفل حزم معينة، كما يقدم GitHub خدمة تسجيل حزمة، ويُحتمَل ظهور مزيد من الخيارات مع مرور الوقت. المهم هو التأكد من اختيارك لأفضل سجل مناسب لك، ولكن ستستخدم العديد من المشاريع مدير الحزم npm، وسنستخدمها في أمثلتنا. استخدام نظام الحزم المجتمعي لنعرض مثالًا باستخدام مدير وسجل الحزم لتثبيت أداة مساعدة لسطر الأوامر. Parcel هي أداة ذكية يستخدمها المطورون لمراقبة محتويات الشيفرة لاستدعاء الاعتماديات، وتثبّتها تلقائيًا أيّ اعتماديات ترى أن شيفرتنا بحاجة إليها، ويمكنها إنشاء الشيفرة تلقائيًا. ثبّتنا الأداة Prettier تثبيتًا عامًا لكل المشاريع في المقال السابق، ولكن سنستخدم الآن npm لتثبيت Parcel محليًا باستخدام أفضل الممارسات، وسنثبّتها بوصفها جزءًا من تطبيق تجريبي. إعداد التطبيق بوصفه حزمة npm أنشئ أولًا مجلدًا جديدًا لتخزين تطبيقنا التجريبي ضمنه في مكان يمكنك إيجاده بسهولة مرة أخرى، وسنسميه parcel-experiment، ولكن بالطبع يمكنك تسميته ما تشاء: mkdir parcel-experiment cd parcel-experiment لنهيّئ بعد ذلك تطبيقنا كحزمة npm التي تُنشئ ملف الإعداد package.json الذي يسمح بحفظ تفاصيل الإعداد الخاصة بنا في حال أردنا إعادة إنشاء هذه البيئة لاحقًا، أو نشر الحزمة على سجل npm بالرغم من أن ذلك إلى حد ما خارج نطاق هذا المقال. اكتب الأمر التالي، وتأكد من أنك داخل الدليل parcel-experiment: npm init ستُطرَح بعض الأسئلة عليك الآن، ثم سينشئ npm ملف package.json افتراضي بناءً على إجاباتك: name: الاسم الذي يعرّف التطبيق. اضغط على زر Enter لقبول الاسم parcel-experiment الافتراضي. version: رقم إصدار البداية الخاص بالتطبيق. اضغط على زر Enter لقبول الإصدار 1.0.0 الافتراضي. description: وصف سريع للغرض من التطبيق. اكتب شيئًا بسيطًا مثل "حزمة npm بسيطة للتعرف على كيفية استخدام npm"، ثم اضغط على زر Enter. entry point: يمثل ملف جافاسكربت ذي المستوى الأعلى في التطبيق. الملف index.js الافتراضي مناسب حاليًا، ثم اضغط على زر Enter. test command وgit repository وkeywords: اضغط على زر Enter لترك هذه الخيارات فارغة حاليًا. author: مؤلف المشروع. اكتب اسمك، ثم اضغط على زر Enter. license: ترخيص نشر الحزمة. اضغط على زر Enter لقبول الإعداد الافتراضي حاليًا. اضغط على زر Enter مرة أخرى لقبول هذه الإعدادات. انتقل إلى المجلد parcel-experiment وستجد أن لديك الملف package.json. افتحه ويجب أن يبدو كما يلي: { "name": "parcel-experiment", "version": "1.0.0", "description": "A simple npm package to learn about using npm", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Chris Mills", "license": "ISC" } إذ يمثل ملف الإعداد الذي يحدد حزمتك. تثبيت Parcel شغّل الأمر التالي لتثبيت Parcel محليًا: npm install parcel-bundler سنكون بعد ذلك جاهزين للتطوير الحديث من طرف العميل الذي يعني استخدام أدوات البناء لتسهيل العمل على المطور. ألقِ نظرة أخرى على ملف package.json أولًا، ستلاحظ أن npm أضاف حقلًا جديدًا وهو الاعتماديات dependencies: "dependencies": { "parcel-bundler": "^1.12.4" } إذا نقلتَ في المستقبل قاعدة شيفرتك إلى موقع آخر على جهاز آخر، فيمكنك إعادة إنشاء الإعداد نفسه عن طريق تشغيل الأمر npm install، وسينظر npm في الاعتماديات ثم يثبّتها لك. لكن أحد مساوئ npm هو أن Parcel متاح فقط ضمن التطبيق parcel-experiment، إذ لن تتمكن من تشغيله في مجلد مختلف. إعداد التطبيق يتوقّع Parcel أن يعمل ملف index.html وملف index.js، ولكن بخلاف ذلك ليست بنية مشروعك مُعلَنة. يمكن أن تكون الأدوات الأخرى مختلفة، ولكن تسهّل الأداة Parcel على الأقل من تجربتنا الأولية. يجب إضافة ملف index.html إلى دليل عملنا. أنشئ الملف index.html في دليل الاختبار الخاص بك، وضع فيه المحتويات التالية: <!DOCTYPE html> <html lang="en-US"> <head> <meta charset="utf-8"> <title>My test page</title> </head> <body> <script src="./index.js"></script> </body> </html> يجب بعد ذلك إضافة ملف index.js في دليل الملف index.html نفسه. يمكن أن يكون الملف index.js فارغًا حاليًا، ولكن يجب أن يكون موجودًا لذلك أنشئه الآن. تطبيق Parcel سنشغّل الآن أداة Parcel المثبَّتة حديثًا. شغّل الأمر التالي في طرفيتك: parcel index.html يجب أن ترى شيئًا كالتالي مطبوعًا على طرفيتك: Server running at http://localhost:1234 ✨ Built in 193ms. إذا واجهتَ مشكلةً في الطرفية التي تعرض خطأ من النوع "الأمر غير موجود command not found"، فحاول تشغيل الأمر السابق باستخدام الأداة npx مثل الأمر npx parcel index.html. يمكننا الآن الاستفادة من نظام حزمة جافاسكربت المجتمعي الكامل. يوجد حاليًا خادم ويب محلي يعمل على العنوان http://localhost:1234، فإذا انتقلت إليه، فلن ترى أي شيء حاليًا، ولكن ستعيد Parcel بنائه وستعمل على تحديث الخادم تلقائيًا عند إجراء تعديلات على تطبيقك لتتمكن من رؤية تأثير التحديث مباشرة. لنفترض أننا نريد عرض التواريخ النسبية التي يمكن قراءتها، مثل "منذ ساعتين 2‎ hours ago" و"قبل 4 أيام 4‎ days ago" وما إلى ذلك. يُعَد التابع formatDistanceToNow()‎ الخاص بالحزمة date-fns مفيدًا لذلك، وهناك حزم أخرى تطبّق الشي نفسه. أضف الشيفرة التالية في ملف index.js واحفظها: import { formatDistanceToNow } from 'date-fns' const date = '1996-09-13 10:00:00'; document.body.textContent = formatDistanceToNow(new Date(date)) + ' ago'; ارجع إلى http://localhost:1234 وسترى كم مضى منذ بلوغ المؤلف 18 عامًا. ما يميز الشيفرة السابقة هو أنها تستخدم الدالة formatDistanceToNow()‎ من الحزمة date-fns التي لم نثبّتها. اكتشفت الأداة Parcel أنك بحاجة الوحدة، وبحثت عنها في سجل الحزمة npmjs.com، وثبّتتها محليًا تلقائيًا. يمكنك إثبات ذلك من خلال البحث في ملف package.json مرة أخرى، وسترى تحديث حقل الاعتماديات dependencies كما يلي: "dependencies": { "date-fns": "^2.12.0", "parcel-bundler": "^1.12.4" } كما أضافت Parcel الملفات المطلوبة لشخص آخر لأخذ هذا المشروع وتثبيت أي اعتماديات استخدمناها. إذا ألقيت نظرة على الدليل الذي شغّلتَ أمر parcel فيه، فستجد عددًا من الملفات الجديدة مثل: node_modules: ملفات اعتماديات Parcel وdate-fns. dist: دليل التوزيع distribution directory، وهو الملفات المُجمَّعة والمُصغَّرة تلقائيًا التي أنشتأها Parcel، والملفات المُخدَّمة على العنوان localhost:1234، وهي الملفات التي ستحمّلها إلى خادم الويب الخاص بك عند إطلاق الموقع عبر الإنترنت ليراه المستخدمين. طالما أننا نعرف اسم الحزمة، فيمكننا استخدامها في شيفرتنا وستوقِف Parcel تشغيل الحزمة -أو نسخة منها- وتجلبها وتثبّتها في دليلنا المحلي ضمن node_modules. بناء الشيفرة للإنتاج لا تعدّ الشيفرة السابقة جاهزة للإنتاج، إذ يكون لمعظم أنظمة أدوات البناء وضع تطوير Development Mode ووضع إنتاج Production Mode. الاختلاف المهم هو أن الكثير من الميزات المفيدة التي ستستخدمها في التطوير ليست ضرورية في الموقع النهائي، لذلك ستُستبعَد للإنتاج مثل استبدال الوحدة وإعادة التحميل المباشر والشيفرة المصدرية غير المضغوطة والمُعلَّقة. هذه بعض ميزات تطوير الويب الشائعة المفيدة جدًا في مرحلة التطوير، ولكنها ليست مفيدة في مرحلة الإنتاج. أوقِف أمر Parcel السابق باستخدام الاختصار Ctrl + C. يمكننا الآن تجهيز أساسات موقع لنشره وهميًا، إذ توفّر Parcel أمرًا إضافيًا لإنشاء ملفات مناسبة للنشر، وإنشاء حزم Bundles مع خيار البناء. شغّل الأمر التالي: parcel build index.html ويجب أن ترى الناتج التالي: ✨ Built in 9.35s. dist/my-project.fb76efcf.js.map 648.58 KB 64ms dist/my-project.fb76efcf.js 195.74 KB 8.43s dist/index.html 288 B 806ms وجهة ملفات الإنتاج هي الدليل dist. تقليل حجم ملف التطبيق حجم حزمة جافاسكربت my-project.fb76efcf.js هو 195K وهو حجم كبير جدًا، بالرغم من أن كل ما تفعله هو طباعة سطر نصي. هناك بالتأكيد بعض الحسابات، ولكننا لسنا بحاجة إلى 195K من شيفرة جافاسكربت لذلك. يجدر بك التساؤل عما إذا كانت أدوات التطوير تطبّق الشيء الصحيح عن استخدامها، إذ يبلغ حجم الحزمة 200K تقريبًا في مثالنا لأنها تضمّنت مكتبة date-fns الكاملة، وليس الدالة التي نستخدمها فقط. إذا تجنّبنا استخدام أي أدوات تطوير واستخدمنا العنصر <script src=""‎> مع إصدار مستضاف من date-fns، فسيحدث الشيء نفسه تقريبًا، وستُنزَّل المكتبة عند تحميل صفحة مثالنا في المتصفح. يمكننا أن نطلب من البرنامج أثناء وجود الأدوات على أجهزتنا فحص استخدامنا للشيفرة وتضمين الدوال التي نستخدمها في مرحلة الإنتاج فعليً فقط، وهي عملية تُعرف باسم هز الشجرة Tree Shaking. يُعَد ذلك منطقيًا لأننا نريد تقليل حجم الملف وبالتالي تحميل تطبيقنا في أسرع وقت ممكن، وتتيح الأدوات المختلفة تطبيق تقنية هز الشجرة بطرق مختلفة. هناك ثلاثة عروض رئيسية للأدوات التي تنشئ حزمًا من الشيفرة المصدرية هي: Webpack وRollup وParcel. سيكون هناك مزيد من الخيارات المتاحة، لكن تُعَد الأنواع التالية شائعة الاستخدام: تقدم أداة RollUp تقنية هز الشجرة وتقسيم الشيفرة. تتطلب أداة Webpack إعدادًا، بالرغم من أن البعض يقلل من تعقيد إعداد Webpack الخاص بالمطورين. هناك في حالة Parcel -قبل الإصدار Parcel 2- راية خاصة مطلوبة هي ‎--experimental-scope-hoisting التي ستهز الشجرة أثناء البناء. لنستخدم Parcel حاليًا، بما أننا ثبّتناها سابقًا. لنشغّل الأمر التالي: parcel build index.html --experimental-scope-hoisting سترى أن هذا الأمر يحدث فرقًا كبيرًا: ✨ Built in 7.87s. dist/my-project.86f8a5fc.js 10.34 KB 7.17s dist/index.html 288 B 753ms أصبح حجم الحزمة حوالي 10K، وهذا أفضل بكثير. إذا أردنا إصدار هذا المشروع إلى خادم، فسنعدّل الملفات الموجودة في مجلد dist، إذ تتعامل Parcel تلقائيًا مع جميع تغييرات اسم الملف. نوصي بإلقاء نظرة على الشيفرة المصدرية في dist/index.html لتتمكن من رؤية التغييرات التي أجرتها Parcel تلقائيًا. توجد الكثير من الأدوات المتاحة، وينمو نظام حزمة جافاسكربت المجتمعي بمعدل غير مسبوق، وهو أمر له إيجابيات وسلبيات. كما تُجرَى تحسينات طوال الوقت، لكن يجب معرفة قدرة الأداة قبل اختيارها. دليل عملاء مدير الحزم ثبّتنا في مثالنا حزمة Parcel باستخدام npm، ولكن هناك بعض البدائل كما ذكرنا سابقًا، لذلك لنلقِ نظرة عليها. سنذكر بعض مدراء الحزم وهي: npm في npmjs.org. pnpm في pnpm.js.org. yarn في yarnpkg.com. يتشابه npm وpnpm من وجهة نظر سطر الأوامر، ويهدف pnpm إلى الحصول على تكافؤ كامل مع خيارات الوسطاء التي يقدمها npm، ولكنهما يختلفان إذ يستخدم pnpm طريقة مختلفة لتنزيل الحزم وتخزينها على حاسوبك بهدف تقليل المساحة الإجمالية المطلوبة على القرص الصلب. سنستخدم npm في الأمثلة التالية، ويمكنك استخدام pnpm وسيعمل الأمر بطريقة صحيحة. يُعتقَد أن yarn أسرع من npm من ناحية عملية التثبيت في أغلب الأحيان، وهذا مهم للمطورين بسبب وجود قدر كبير من الوقت الضائع في انتظار تثبيت الاعتماديات والنسخ إلى الحاسوب. لنراجع الآن الإجراءات الشائعة التي يجب تنفيذها مع مدير الحزم. بدء مشروع جديد npm init yarn init ستوجهك الأوامر السابقة وترشدك عبر سلسلة من الأسئلة لوصف مشروعك مثل الاسم والترخيص والوصف وما إلى ذلك، ثم إنشاء ملف package.json الذي يحتوي على معلومات وصفية حول مشروعك واعتمادياته. تثبيت الاعتماديات npm install date-fns yarn add date-fns رأينا أمر التثبيت install قيد التنفيذ أعلاه الذي سيؤدي إلى إضافة حزمة date-fns مباشرة إلى دليل العمل ضمن دليل فرعي يسمى node_modules مع اعتماديات date-fns. سيثبّت الأمر السابق افتراضيًا أحدث إصدار من date-fns، ولكن يمكنك التحكم في ذلك، إذ يمكنك استخدام الأمر date-fns@1 الذي يمنحك أحدث إصدار من 1‎.x وهو 1.30.1، أو يمكنك تجربة الأمر date-fns@^2.3.0 الذي يعطي أحدث إصدار يتضمن الإصدار 2.3.0 أو بعده (2.8.1 في وقت كتابة النسخة الأجنبية من هذا المقال). تحديث الاعتماديات npm update yarn upgrade سينظر الأمر السابق في الاعتماديات المثبَّتة حاليًا ويحدّثها عند وجود تحديث متاح ضمن المجال المحدّد في الحزمة. حُدِّد المجال في إصدار الاعتمادية في ملف package.json الخاص بك مثل date-fns^2.0.1، إذ يعني المحرف ^ جميع الإصدارات الثانوية وإصدارات تصحيح الأخطاء التي تتضمن الإصدار 2.0.1 والإصدارات التي بعدها باستثناء الإصدار 3.0.0. يُحدَّد الإصدار باستخدام نظام يسمى semver الذي يبدو معقدًا بعض الشيء في توثيقه، ولكن يمكن تبسيطه من خلال النظر فقط في المعلومات الموجزة، ويُمثّل الإصدار بالشكل MAJOR.MINOR.PATCH مثل 2.0.1، إذ يمثل الرقم 2 الإصدار الرئيسي Major Version ويمثل الرقم 1 إصدار تصحيح الأخطاء Patch Version. يمكنك تجربة قيم Semver باستخدام حاسبة semver. يجب أن نتذكر أن الأمر npm update لن يرقّي الاعتماديات خارج المجال المحدَّد في ملف package.json، وإنما يثبت الإصدار الموجود على وجه التحديد. تدقيق الثغرات npm audit yarn audit سيؤدي الأمر السابق إلى التحقق من كامل شجرة اعتمادية مشروعك وتشغيل الإصدارات المحدَّدة التي تستخدمها لتلافي ثغرات قاعدة البيانات وإعلامك إذا كانت هناك حزم ذات ثغرات محتمَلة في مشروعك. يُعَد مشروع Snyk نقطة انطلاق جيدة للتعرف على الثغرات، والذي يغطي كلًا من حزم جافاسكربت ولغات البرمجة الأخرى. التحقق من الاعتمادية npm ls date-fns yarn why date-fns يظهِر الأمر السابق إصدار الاعتمادية المُثبَّتة وكيفية تضمينها في مشروعك، ويُحتمَل سحب حزمة أخرى ذات مستوى أعلى في date-fns. كما يمكن أن يكون لديك إصدارات متعددة من حزمة معينة في مشروعك، ولوحظ هذا الأمر عدة مرات مع حزمة lodash أنها مفيدة جدًا لضمان عمل كافة الشيفرة البرمجية. يمكن أن ترغب في التحقق بالضبط من الإصدار المثبت، بالرغم من أن مدير الحزم سيبذل قصارى جهده لإزالة تكرار الحزم. إنشاء أوامرك الخاصة يدعم مدير الحزم إنشاء أوامرك الخاصة وتنفيذها من سطر الأوامر، إذ يمكننا استخدام الأمر التالي مثلًا: npm run dev # ‫ أو yarn run dev سيؤدي الأمر السابق إلى تشغيل سكربت مخصص لبدء مشروعنا في وضع التطوير، ويمكننا تضمينه بانتظام في جميع المشاريع إذ يميل إعداد التطوير المحلي إلى العمل بطريقة مختلفة عن كيفية تشغيله في عملية الإنتاج. إذا حاولت تشغيل هذا السكربت في مشروع اختبار Parcel الخاص بك، فيُحتمَل أن يدّعي أن السكربت dev مفقود، لأن npm وyarn يبحثان عن خاصية تسمى dev ضمن الخاصية scripts لملف package.json الخاص بك. يمكن أن تشغّل Parcel خادم تطوير باستخدام الأمر parcel serve filename.html، لأننا سنرغب في استخدامه كثيرًا أثناء عملية التطوير، لذلك لننشئ أمر مخصَّصًا مختصرًا هو "dev" في ملف package.json. يجب أن يكون لديك ملف package.json ضمن الدليل parcel-experiment. افتحه ويجب أن تبدو الخاصية scripts كما يلي: "scripts": { "test": "echo \"Error: no test specified\" && exit 1", }, حدّثها لتبدو كما يلي، ثم احفظ الملف: "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "parcel serve index.html" }, أضفنا الأمر dev المخصص بوصفه سكربت npm. حاول الآن تشغيل الأمر التالي في طرفيتك، وتأكد من أنك ضمن الدليل parcel-experiment: npm run dev يجب أن يبدأ الأمر السابق Parcel ويقدم ملف index.html الخاص بك على خادم التطوير المحلي كما رأينا سابقًا: Server running at http://localhost:1234 ✨ Built in 5.48s. كما تُعَد أوامر npm وyarn ذكية من لأنها ستبحث عن أدوات سطر الأوامر المثبَّتة محليًا للمشروع قبل محاولة العثور عليها من خلال الطرق التقليدية، ويخزّن حاسوبك عادةً البرامج ويسمح بإيجادها. يمكنك معرفة المزيد حول تعقيدات الأمر run، بالرغم من أن سكربتاتك ستعمل بصورة جيدة في معظم الحالات. يمكنك إضافة جميع الأنواع إلى الخاصية scripts التي تساعدك على أداء عملك. إلى هنا تنتهي جولتنا في التعرف على مدير الحزم، وخطوتنا التالية هي بناء نموذج لسلسلة أدوات، ووضع كل ما تعلمناه حتى الآن موضع التنفيذ. هذا المقال جزء من سلسلة مقالات بعنوان تعلم تطوير الويب والتي تشرح كامل عملية تطوير الويب من واجهات أمامية وخلفية بالكامل. ترجمة -وبتصرُّف- للمقال Package management basics. اقرأ أيضًا المقال التالي: بناء نموذج كامل لسلسلة أدوات تطوير الويب من طرف العميل المقال السابق: أدوات مطوري الويب المدمجة في المتصفحات فهم أدوات تطوير الويب من طرف العميل مدخل إلى Helm: مدير حزم Kubernetes نظام إدارة الحزم NuGet في dot NET كيف تدير الحزم على نظام FreeBSD 10.1 بواسطة Pkg
  25. سيُطلَب منك بلا شك خلال عملية التطوير تشغيل بعض الأوامر في الطرفية Terminal أو على سطر الأوامر Command Line، لذلك سنقدّم من خلال هذا المقال مقدمة عن الطرفية، والأوامر الأساسية التي ستحتاج إلى إدخالها، وكيفية ربط الأوامر مع بعضها بعضًا، وإضافة أدوات واجهة سطر الأوامر Command Line Interface -أو CLI اختصارًا- الخاصة بك. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت. الهدف: فهم سطر الأوامر أو الطرفية، والأوامر الأساسية التي يجب تعلمها، وكيفية تثبيت أدوات سطر أوامر جديدة. الطرفية Terminal الطرفية هي واجهة نصية تُستخدَم لتنفيذ البرامج النصية. إذا شغّلتَ أدوات تطوير الويب، فهناك فرصة كبيرة لفتح سطر الأوامر وتشغيل بعض الأوامر لاستخدام الأدوات التي اخترتها، إذ سيُشار إلى هذه الأدوات باسم أو أدوات واجهة سطر الأوامر Command Line Interface أو أدوات CLI اختصارًا. يمكن استخدام عدد كبير من الأدوات عن طريق كتابة الأوامر في سطر الأوامر، ويكون العديد منها مثبتًا مسبقًا على نظامك، ويمكن تثبيت أدوات أخرى من سجلات الحزم التي تشبه متاجر التطبيقات، ولكن تُستخدَم غالبًا للأدوات والبرامج المستندة إلى سطر الأوامر. يفتقر سطر الأوامر إلى تجربة المستخدم، بل إن التجربة الأولى مع سطر الأوامر غالبًا ما تكون متعبة وصعبة الاستخدام نسبيًا، إذ يحتوي على شاشة فارغة ومؤشر وامض مع قليل من المساعدة الواضحة المتاحة بشأن ما يجب تطبيقه، لكننا نعدك أن استخدامه سيصبح أسهل مع قليل من التوجيه والممارسة، وسنُساعدك في هذا المقال للبدء في هذه البيئة. نشأت الطرفية في الخمسينات والستينات من القرن الماضي، ولكن شكلها الأصلي لا يشبه ما نستخدمه اليوم. بقيت الطرفية منذ ذلك الحين ميزة ثابتة في جميع أنظمة التشغيل من أجهزة سطح المكتب إلى خوادم السحابة إلى الحواسيب الصغيرة مثل Raspberry PI Zero وحتى الهواتف المحمولة. توفر الطرفية وصولًا مباشرًا إلى نظام ملفات الحاسوب الأساسي والميزات منخفضة المستوى، وبالتالي تُعَد مفيدة لأداء المهام المعقدة بسرعة. كما أنها مفيدة في عملية الأتمتة مثل كتابة أمر لتحديث أسماء مئات الملفات فوريًا كتعديل اسم الملف "ch01-xxxx.png" إلى "ch02-xxxx.png"، فإذا حدّثتَ أسماء الملفات باستخدام تطبيق Finder أو Explorer GUI، فسيستغرق الأمر وقتًا طويلًا. يمكنك من خلال الشكل الآتي رؤية بعض الأشكال المختلفة للبرامج المتوفرة التي يمكن أن تنقلك إلى الطرفية، وتظهَر موجِّهات الأوامر المتوفرة في ويندوز مثل برامج Cmd و Powershell التي يمكن تشغيلها من قائمة ابدأ عن طريق كتابة اسم البرنامج. كما يوضّح الشكل التالي تطبيق طرفية نظام ماك macOS: كيفية الوصول إلى الطرفية يستخدم العديد من المطورين حاليًا أدوات تستند إلى يونيكس مثل الطرفية والأدوات التي يمكنك الوصول إليها من خلالها، إذ تدعم العديد من الأدوات الموجودة على الويب الأنظمة التي تستند إلى يونيكس، ولكن لا داعي للقلق، فهي متوفرة في معظم الأنظمة. سنوضّح فيما يلي كيفية الوصول إلى الطرفية على النظام الذي تريده. لينكس أو يونيكس: تحتوي أنظمة لينكس أو يونيكس طرفية متاحة افتراضيًا مدرجَة ضمن تطبيقاتك. نظام ماك macOS: يحتوي نظامًا يسمى داروين Darwin يقع أسفل واجهة المستخدم الرسومية ويشبه نظام يونيكس، ويوفر الطرفية والوصول إلى الأدوات ذات المستوى المنخفض. الطرفية متاحة على نظام ماك من خلال المسار Applications/Utilities/Terminal. ويندوز: لم يكن استخدام الطرفية أو سطر الأوامر على ويندوز بسيطًا أو سهلًا كما هو الحال في أنظمة التشغيل الأخرى، ولكن الأمور تتحسن مع مرور الوقت. لطالما امتلك ويندوز برنامجًا شبيهًا بالطرفية يسمى Cmd أو موجه الأوامر، ولكنه ليس متكافئًا مع أوامر يونيكس، بل يكافئ موجه ويندوز دوز Windows DOS ذي النمط القديم. توجد برامج أفضل لتوفير طرفية على ويندوز مثل PowerShell وGitbash الذي يكون جزءًا من مجموعة أدوات git for Windows. لكن أفضل خيار لنظام ويندوز حاليًا هو نظام ويندوز الفرعي لنظام لينكس Windows Subsystem for Linux -أو WSL اختصارًا، وهو طبقة توافق لتشغيل أنظمة تشغيل لينكس مباشرة من داخل الإصدار رقم 10 من ويندوز، مما يتيح لك تشغيل طرفية حقيقية مباشرة على ويندوز دون الحاجة إلى آلة افتراضية، ويمكن تثبيت نظام WSL مباشرة من متجر ويندوز مجانًا. نوصي بشدة بتثبيت نظام WSL، ويمكنك التمسك باستخدام موجّه الأوامر الافتراضي cmd إذ ستعمل أدوات متعددة بطريقة صحيحة، ولكنك ستجد الأمور أسهل إذا كان لديك تكافؤ أفضل مع أدوات يونيكس. هناك فرق بين الطرفية وسطر الأوامر، فالطرفية تقنيًا هي برنامج يبدأ ويتصل بالصدفة Shell التي تُعَد بيئة جلستك إذ يمكنك تخصيص أشياء مثل الموجه والاختصارات، بينما سطر الأوامر هو السطر الحرفي الذي تكتب فيه الأوامر ويومض المؤشر. إذا استخدمت أدواتٍ مثل Visual Studio Code، فهناك مجموعة كبيرة من الامتدادات التي يمكن استخدامها كوكيل Proxy لاستخدام أوامر الطرفية دون الحاجة إلى استخدامها مباشرة، وبالرغم من وجود الكثير من الأدوات المتاحة في سطر الأوامر. لكنك لن تجد امتدادًا لمحرر الشيفرات لكل ما تريده، لذلك سيتعين عليك اكتساب خبرة في استخدام الطرفية في النهاية. أوامر المدمجة الأساسية في الطرفية إليك بعض الأشياء التي يمكن لسطر الأوامر تطبيقها، بالإضافة إلى أسماء الأدوات ذات الصلة بكل حالة: انقل نظام ملفات حاسوبك إلى جانب مهام المستوى الأساسي مثل الإنشاء والنسخ وإعادة التسمية والحذف: التنقل بين المجلدات باستخدام الأمر cd. إنشاء مجلد باستخدام الأمر mkdir. إنشاء ملفات وتعدّيل بياناتها الوصفية باستخدام الأمر touch. نسخ الملفات باستخدام الأمر cp. نقل الملفات باستخدام الأمرmv. حذف الملفات أو المجلدات باستخدام الأمر rm. تنزيل الملفات الموجودة في عناوين URL محدَّدة باستخدام الأمر curl. البحث عن أجزاء نصية ضمن مجموعات نصية أكبر باستخدام الأمر grep. عرض محتويات ملف بمقدار صفحة تلو الأخرى باستخدام الأمر less وcat. معالجة وتحويل مجاري النصوص مثل تغيير جميع وسوم <div> في ملف HTML إلى <article> باستخدام الأوامر awk وtr وsed. التنقل في سطر الأوامر ستحتاج حتمًا إلى الانتقال إلى مسار معين عندما تزور سطر الأوامر. تشغّل جميع أنظمة التشغيل برنامجها الطرفي في المسار الموجود فيه سطر الأوامر، وغالبًا سترغبُ بالانتقال إلى لمسار مختلف. يتيح لك الأمر cd تغيير الدليل Change Directory، وليس هذا الأمر برنامجًا وإنما مبني مسبقًا، إذ لا يمكنك حذفه عن طريق الخطأ. لا داعي للقلق كثيرًا بشأن ما إذا كان الأمر مبنيًا مسبقًا أم لا، ولكن ضع في بالك أن الأوامر المبنية مسبقًا تظهر في جميع الأنظمة القائمة على نظام يونيكس. يمكنك تغيير المسار من خلال كتابة الأمر cd في الطرفية متبوعًا بالمجلد الذي تريد الانتقال إليه. يمكنك استخدام الأمر cd Desktop بافتراض وجود المجلد ضمن المسار الرئيسي كما يلي: اكتب الأمر التالي في طرفية نظامك: cd Desktop إذا أردتَ الرجوع إلى المسار السابق، فيمكنك استخدام نقطتين كما يلي: cd.. هناك اختصار في الطرفية مفيد جدًا وهو استخدام مفتاح tab لإكمال الأسماء تلقائيًا التي تعرف أنها موجودة بدلًا من كتابة اسم المجلد بالكامل فمثلًا كتابة الأمر cd D ثم الضغط على مفتاح tab، سيكمل سطر الأوامر كتابة اسم المجلد Desktop نيابةً عنك، بشرط وجوده في المجلد الحالي. إذا كان المجلد الذي تريد الانتقال إليه متداخلًا، فيجب أن تعرف المسار للوصول إليه، إذ يصبح ذلك أسهل عندما تصبح أكثر دراية بمعمارية نظام ملفاتك، ولكن إن لم تكن متأكدًا من المسار، فيمكنك اكتشافه باستخدام الأمر ls، ومن خلال النقر في نافذة المستكشف Explorer أو Finder لمعرفة مكان المجلد بناءً على مكان وجودك حاليًا. إذا أردت الانتقال مثلًا إلى مجلد يسمى src يقع ضمن مجلد اسمه project موجود على سطح المكتب Desktop، فيمكنك كتابة هذه الأوامر الثلاثة للوصول إلى هناك من مجلدك الرئيسي كما يلي: cd Desktop cd project cd src ولكن يمكنك كتابة أمر واحد مع العناصر المختلفة في المسار مفصول بينها بشرطة مائلة للأمام، تمامًا كما تفعل عند تحديد مسارات للصور أو الأصول الأخرى في شيفرة CSS أو HTML أو جافاسكربت: cd Desktop/project/src لاحظ أن تضمين شرطة مائلة في المسار يجعله مسارًا مطلقًا مثل ‎/Users/your-user-name/Desktop، إذ يؤدي حذف الشرطة المائلة في المقدمة كما فعلنا سابقًا إلى جعل المسار مسارًا نسبيًا متعلقًا بمجلد العمل الحالي، وهذا هو بالضبط ما تراه مع عناوين URL في متصفح الويب، إذ تعني الشرطة المائلة في البداية جذر موقع الويب، بينما يعني حذف الشرطة المائلة أن عنوان URL متعلق بصفحتي الحالية. يستخدم نظام ويندوز الشرطة المائلة للخلف بدلًا من الشرطة المائلة للأمام مثل الأمر التالي: cd Desktop\project\src سرد محتويات مجلد الأمر ls مبني مسبقًا في نظام التشغيل يونيكس وهو اختصار للكلمة الأجنبية List، ووظيفته سرد محتويات المجلد الذي تتواجد فيه حاليًا. لاحظ أن هذا الأمر لن ينجح إذا استخدمتَ موجّه أوامر ويندوز الافتراضي cmd، فالأمر المكافئ له هو dir. شغّل الأمر التالي في الطرفية: ls يمنحك الأمر ls قائمة بالملفات والمجلدات الموجودة في مجلد العمل الحالي، ولكنها معلومات أساسية، وستحصل فقط على اسم كل عنصر موجود دون معرفة نوعه. يمكن أن يمنحك تغيير بسيط في استخدام هذا الأمر كثيرًا من المعلومات. خيارات الأوامر تحتوي معظم أوامر الطرفية على خيارات، هي المعدّلات Modifiers التي تضيفها إلى نهاية الأمر، مما يجعله يتصرف بطريقة مختلفة. تتكون هذه الخيارات عادةً من مسافة بعد اسم الأمر، متبوعة بشرطة، متبوعة بحرف واحد أو أكثر. جرب الأمر التالي على سبيل المثال، وشاهد النتيجة: ls -l يمنحك الخيار ‎-l في الأمر ls قائمة بملف أو مجلد واحد في كل سطر ومعلومات أخرى. يمكن التعرف على المجلدات من خلال البحث عن الحرف "d" على الجانب الأيسر من السطور، إذ يمكننا استخدام الأمر cd مع هذه المجلدات. يوضّح الشكل الآتي طرفية نظام ماك الصرفة في الجزء العلوي، وطرفية مخصّصة مع بعض الرموز والألوان الإضافية في الجزء السفلي، وكلاهما يعرض نتائج تشغيل الأمر ls -l: الإنشاء والنسخ والنقل والإزالة هناك عدد من الأوامر الأساسية الأخرى التي يُحتمَل أن تستخدمها كثيرًا أثناء عملك مع الطرفية. هذه الأوامر بسيطة جدًا، لذا لن نشرحها بقدر كبير من التفاصيل مثل الأمرين السابقين. استخدم أمثلة الأوامر التالية لفهمها: mkdir: يؤدي هذا الأمر إلى إنشاء مجلد جديد ضمن المجلد الحالي الذي تتواجد فيه، مع الاسم الذي تقدمه بعد الأمر، فمثلًا سينشئ الأمر mkdir my-awesome-website مجلدًا جديدًا اسمه my-awesome-website. rmdir: يزيل هذا الأمر المجلد المحدد إذا كان فارغًا فقط، فمثلًا سيزيل الأمر rmdir my-awesome-website المجلد الذي أنشأناه سابقًا. إذا أردتَ إزالة مجلد غير فارغ وإزالة كل ما يحتويه، فيمكنك استخدام الخيار ‎-r وهو اختصار recursive تكراري، ولكن يجب الانتباه عند استخدامه، إذ يجب أن تتأكد من عدم وجود أيّ شيء يمكن أن تحتاجه داخل المجلد، إذ سيختفي إلى الأبد. touch: ينشئ هذا الأمر ملفًا فارغًا جديدًا ضمن المجلد الحالي، فمثلًا ينشئ الأمر touch mdn-example.md ملفًا فارغًا جديدًا اسمه mdn-example.md. mv: ينقل هذا الأمر ملفًا من موقع الملف الأول المحدَّد إلى موقع الملف المحدد الثاني، وينقل الأمر mv mdn-example.md mdn-example.txt الملف mdn-example.md في المجلد الحالي إلى الملف mdn-example.txt في المجلد الحالي (تُكتَب المواقع كمسارات ملفات)، إذ يُعَد الملف منقولًا، ولكن هذا الأمر يعيد تسمية الملف فعليًا. cp: يشبه الأمر mv في الاستخدام، إذ ينشئ الأمر cp نسخة من الملف الموجود ضمن الموقع الأول المحدد في الموقع الثاني المحدَّد، إذ يُنشئ الأمر cp mdn-example.txt mdn-example.txt.bak نسخة من الملف mdn-example.txt بالاسم mdn-example.txt.bak، ويمكنك بالطبع تسمية هذه النسخة باسم آخر إذا أدرتَ ذلك. rm: يزيل هذا الأمر الملف المحدَّد، فمثلًا يحذف الأمر rm mdn-example.txt ملفًا اسمه mdn-example.txt. لاحظ أن هذا الحذف نهائي ولا يمكن التراجع عنه عبر سلة المحذوفات الموجودة غالبًا على واجهة مستخدم سطح المكتب. تسمح لك العديد من أوامر الطرفية باستخدام العلامات النجمية بوصفها محارف بدل Wild Card مع أيّ تسلسل من المحارف، مما يسمح بتشغيل عملية على عدد كبير من الملفات في الوقت نفسه، وتتطابق جميعها مع النمط المحدد، إذ يحذف الأمر rm mdn-*‎ جميع الملفات التي تبدأ بسلسلة المحارف mdn-‎، بينما يحذف الأمر rm mdn-*.bak جميع الملفات التي تبدأ بسلسلة المحارف mdn-‎ وتنتهي بسلسلة المحارف ‎.bak. هل تعد الطرفية خطيرة؟ عليك توخي الحذر عند استخدام الطرفية. لا تحمل الأوامر البسيطة الكثير من المخاطر، ولكن إذا جمّعتَ أوامرًا أكثر تعقيدًا، فعليك التفكير مليًا فيما ستفعله هذه الأوامر، ومحاولة اختبارها أولًا قبل تشغيلها في المجلد المحدَّد. لنفترض أن لديك 1000 ملف نصي في مجلد، وأردت الاطلاع عليها جميعًا وحذف الملفات التي يحتوي اسمها على سلسلة فرعية معينة. إن لم تكن حذرًا، يمكن أن ينتهي بك الأمر بحذف شيء مهم، وبالتالي تفقد عملك خلال هذه العملية. إحدى العادات الجيدة التي يجب عليك اتباعها هي كتابة الأمر ضمن محرر نصوص، ثم إنشاء نسخة احتياطية من مجلدك وتشغيل الأمر على هذه النسخة أولًا لاختباره. إن لم تكن مرتاحًا لتجربة أوامر الطرفية على جهازك الخاص، فيمكنك تجربتها في مكان آمن هو Glitch.com الذي يُعَد مكانًا رائعًا لتجربة شيفرة تطوير الويب، بالإضافة إلى إمكانية الوصول إلى طرفية، إذ يمكنك تشغيل كل هذه الأوامر مباشرة فيها. يُعَد tldr.sh من الموارد الرائعة للحصول على نظرة عامة وسريعة على أوامر طرفية معينة، وهي خدمة توثيق مقادَة من المجتمع على غرار MDN، ولكنها خاصة بالأوامر الطرفية. لنتعلّم الآن كيفية توصيل الأدوات مع بعضها في سطر الأوامر. ربط الأوامر باستخدام الأشرطة العمودية تعرّفنا سابقًا على الأمر ls الذي يعرض محتويات المجلد الحالي: ls ولكن إذا أردنا حساب عدد الملفات والمجلدات بسرعة ضمن المجلد الحالي، فلن يستطيع الأمر ls ذلك من تلقاء نفسه. هناك أداة أخرى متاحة من يونيكس تسمى wc تحسب عدد الكلمات أو الأسطر أو المحارف أو البايتات لكل ما يُوضَع فيها، إذ يمكن أن يكون ذلك ملفًا نصيًا، إذ ينتج عن تنفيذ الأمر التالي عدد الأسطر في الملف myfile.txt: wc -l myfile.txt كما يمكنها حساب عدد الأسطر لخرج أي أمر يُربَط معها من خلال رمز الشريط العمودي |، وسيحسبُ الأمر التالي عدد الأسطر الناتجة عن الأمر ls -أي ما سيطبعه إلى الطرفية إذا شُغِّل بمفرده- ويخرج عدد الأسطر في الطرفية: ls | wc -l بما أن الأمر ls يطبع كل ملف أو مجلد على سطر خاص به، فهذا يعطينا عدد الأدلة والملفات بفعالية. تطبع أدوات سطر أوامر يونيكس النص إلى الطرفية، ويشار إليها بالطباعة إلى الخرج المعياري Printing To Standard Output أو STDOUT اختصارًا، إذ يمكن لعدد كبير من الأوامر قراءة المحتوى من مجرى الدخل المعروف باسم الدخل المعياري Standard Input أو STDIN اختصارًا. يربط المعامل | هذه المدخلات والمخرجات مع بعضها، مما يسمح ببناء عمليات أكثر تعقيدًا لتناسب احتياجاتنا، إذ يمكن أن يصبح خرج أحد الأوامر دخلًا للأمر الذي بعده. يطبع الأمر ls عادةً خرجه إلى STDOUT، ولكن يُربَط خرجه مع الأمر wc من خلال الرمز | سيأخذ الأمر wc هذا الخرج بوصفه دخلًا له، ويحسب عدد الأسطر، ويطبعها إلى STDOUT. مثال أكثر تعقيدا سنحاول أولًا جلب محتويات صفحة "fetch" من MDN باستخدام الأمر curl الذي يمكن استخدامه لطلب المحتوى من عناوين URL مثل الرابط الآتي: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch كما يلي: curl https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch لن تحصل على خرج بسبب إعادة توجيه الصفحة إلى ‎/Web/API/fetch، إذ يجب إعلام الأمر curl صراحةً باتباع عمليات إعادة التوجيه باستخدام الراية ‎-L. لنلقِ نظرةً على الترويسات التي يعيدها developer.mozilla.org باستخدام الراية ‎-I الخاصة بالأمر curl، ونطبع جميع عمليات إعادة التوجيه التي يرسلها الموقع إلى الطرفية عن طريق ربط خرج الأمر curl مع الأمر grep باستخدام الرمز |، وسنطلبُ من الأمر grep إعادة جميع الأسطر التي تحتوي على الكلمة "location". حاول تشغيل الأمر التالي (لاحظ أن هناك إعادة توجيه واحدة فقط قبل أن نصل إلى الصفحة الأخيرة): curl https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch -L -I | grep location يجب أن يبدو الخرج كما يلي، إذ سينتج الأمر curl أولًا بعض عدّادات التنزيل أو ما شابه ذلك: location: /en-US/docs/Web/API/fetch كما يمكننا تحويل محتويات سطور الموقع location:‎، وإضافة الأصل الأساسي إلى بداية كل منها لنحصل على عناوين URL كاملة مطبوعة، لذلك سنضيف الأمر awk، وهي لغة برمجة تشبه جافاسكربت أو روبي Ruby أو بايثون Python، ولكنها أقدم منها. شغّل الأمر التالي: curl https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch -L -I | grep location | awk '{ print "https://developer.mozilla.org" $2 }' ويجب أن يكون الخرج النهائي كما يلي: https://developer.mozilla.org/en-US/docs/Web/API/fetch خصّصنا الخرج من خلال دمج هذه الأوامر لإظهار عناوين URL الكاملة التي يعيد خادم Mozilla التوجيه من خلالها عندما نطلب العنوان ‎/docs/Web/API/WindowOrWorkerGlobalScope/fetch. إضافة مزايا ألقينا نظرة على بعض الأوامر المبنية مسبقًا التي يأتي نظامك مزودًا بها، ولنلقِ نظرة الآن على كيفية تثبيت أداة CLI لجهة خارجية والاستفادة منها. يتوفر حاليًا النظام المجتمعي الواسع للأدوات القابلة للتثبيت لتطوير واجهة الويب الأمامية ضمن npm، وهي خدمة استضافة حزم يملكها القطاع الخاص، وتعمل بصورة وثيقة مع Node.js، ولكن يمكنك أن تتوقع رؤية مزيد من مزودي الحزم مع مرور الوقت. يؤدي تثبيت Node.js إلى تثبيت أداة سطر أوامر npm وأداة إضافية تعتمد على npm تسمى npx، وتوفر أداة npm بوابة لتثبيت أدوات سطر أوامر إضافية. يعمل Node.js وnpm بالطريقة نفسها في جميع الأنظمة: ماك macOS وويندوز Windows ولينكس Linux. ثبّت أداة npm على نظامك الآن مع تنزيل وتشغيل مثبّت Node.js المناسب لنظام تشغيلك. إذا طُلب منك ذلك، فتأكد من تضمين npm كجزء من عملية التثبيت. كما سنتعرّف على أداة Prettier، وهي أداة لتنسيق الشيفرات البرمجية وتحتوي على عدة خيارات بسيطة. مكان تثبيت أدوات CLI يمكننا تثبيت الأدوات بطريقة عامة باستخدام npm لنتمكن من الوصول إليها في أي مكان، أو محليًا في مجلد المشروع الحالي. هناك إيجابيات وسلبيات في كل طريقة، وهذه القائمة توضح إيجابيات وسلبيات التثبيت العام للأدوات: table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } إيجابيات التثبيت العام سلبيات التثبيت العام يمكن الوصول إليها من أي مكان في الطرفية يمكن ألّا تكون متوافقة مع شيفرة مشروعك تُثبَّت مرة واحدة فقط لن يتمكن المطورون الآخرون في فريقك من الوصول إلى هذه الأدوات، مثل مشاركة قاعدة شيفرتك عبر أداة مثل git. يستهلك مساحة أقل على القرص الصلب تتعلق هذه النقطة بالنقطة السابقة، إذ يصعب نسخ شيفرة المشروع (إذا ثبّت أدواتك محليًا، فيمكن إعدادها كاعتماديات وتثبيتها باستخدام الأمر npm install). الإصدار نفسه دائمًا يبدو مثل أي أمر يونيكس آخر يُحتمَل أن يكون التأثير السلبي للتثبيت العام أكبر بكثير من الفوائد بالرغم من أن قائمة السلبيات أقصر، ولكننا سنثبّت الأدوات بطريقة عامة لإبقاء الأمور بسيطة. سنلقي نظرة أكثر على عمليات التثبيت المحلية وفوائدها في المقال التالي. تثبيت Prettier سنثبّت أداة Prettier بوصفها أداة مساعدة عامة لسطر الأوامر، وهي أداة تنسيق شيفرة لمطوري الواجهة الأمامية، وتركز على اللغات المستندة إلى لغة جافاسكربت بالإضافة إلى دعمها لغات HTML و CSS و SCSS و JSON وغير ذلك. يمكن أن تطبّق أداة Prettier ما يلي: حفظ العبء المعرفي المتمثل في جعل النمط متسقًا يدويًا عبر جميع ملفات شيفرتك، إذ يمكن أن تطبّق Prettier ذلك تلقائيًا. مساعدة المتعلمين الجدد في تطوير الويب على تنسيق شيفرتهم من خلال تطبيق أفضل الممارسات. يمكن تثبيتها على أي نظام تشغيل وكجزء مباشر من أدوات المشروع، مما يضمن لزملائك وأصدقائك الذين يعملون على شيفرتك أن يستخدموا نمط الشيفرة الذي تستخدمه. يمكن تهيئتها للتشغيل عند الحفظ، أو أثناء الكتابة، أو حتى قبل نشر الشيفرة. افتح الطرفية بعد تثبيت نود وشغّل الأمر التالي لتثبيت Prettier: npm install --global prettier تصبح بعد ذلك أداة Prettier متاحة في الطرفية وفي أيّ مكان في نظام ملفاتك. سيؤدي تشغيل الأمر بدون أي وسطاء -كما هو الحال مع العديد من الأوامر الأخرى- إلى تقديم معلومات حول كيفية الاستخدام والمساعدة. جرب الأمر التالي وشاهد النتيجة: prettier يجب أن يبدو الخرج كما يلي: Usage: prettier [options] [file/glob ...] By default, output is written to stdout. Stdin is read if it is piped to Prettier and no files are given. … يجب دائمًا الاطلاع على معلومات الاستخدام على الأقل، حتى لو كانت طويلة، لأنها ستساعدك في فهم كيفية استخدام الأداة بطريقة أفضل. لنجرب استخدام الأداة Prettier لنتمكن من معرفة كيفية عملها. أنشئ أولًا مجلدًا جديدًا في مكان ما على نظام ملفاتك يسهل العثور عليه مثل مجلد اسمه prettier-test على سطح المكتب Desktop، ثم احفظ الشيفرة التالية في ملف جديد يسمى index.js ضمن مجلد الاختبار: const myObj = { a:1,b:{c:2}} function printMe(obj){console.log(obj.b.c)} printMe(myObj) يمكننا أن نشغّل prettier في قاعدة الشيفرة للتحقق من حاجة شيفرتنا لتعديلٍ ما. شغّل الأمر cd للانتقال إلى مجلدك، وشغّل الأمر التالي: prettier --check index.js ويجب أن تحصل على الخرج التالي: Checking formatting... index.js Code style issues found in the above file(s). Forgot to run Prettier? لذلك هناك بعض أنماط الشيفرات التي يمكن إصلاحها، سيؤدي إضافة الخيار ‎--write إلى الأمر prettier إلى إصلاح هذه المشاكل، مما يجعلنا نركز على كتابة شيفرة مفيدة فقط. شغّل إصدار الأمر التالي: prettier --write index.js وستحصل على الخرج التالي: Checking formatting... index.js Code style issues fixed in the above file(s). ولكن إذا نظرت إلى ملف جافاسكربت الخاص بك، فستجد أنه منسَّق مثل الشيفرة التالية: const myObj = { a: 1, b: { c: 2 }, }; function printMe(obj) { console.log(obj.b.c); } printMe(myObj); يمكنك جعل هذا التنسيق جزءًا تلقائيًا من عمليتك بناءً على طريقة سير عملك. الأتمتة هي المكان الذي تتفوق فيه الأدوات، وأفضل أتمتة هي الأتمتة الذي "يحدث على الفور" دون الحاجة إلى إعداد أي شيء. هناك عدد من الطرق التي يمكن من خلالها تحقيق الأتمتة مع أداة Prettier. هناك بعض الموارد الممتازة عبر الإنترنت للمساعدة، على الرغم من أن ذلك خارج نطاق هذا المقال. يمكنك استدعاء أداة Prettier: قبل تأكيد شيفرتك في مستودع git باستخدام Husky. عندما تضغط على "حفظ" في محرر الشيفرة، سواء أكان محرر VS Code أو Atom أو Sublime Text. بوصفها جزءًا من فحوصات التكامل المستمرة باستخدام أدوات مثل Github Actions. تفضيلنا الشخصي هو الخيار الثاني -أثناء استخدام محرر VS Code، وتعمل Prettier على تشغيل وتنظيف أي تنسيق تحتاجه في كل عملية حفظ. أدوات أخرى إليك قائمة مختصرة ممتعة من الأدوات لتجربتها: bat: النسخة الأفضل من الأداة cat (تُستخدم cat لطباعة محتويات الملفات). prettyping: نفّذ الأمر ping على سطر الأوامر. تُعَد أداة ping مفيدة للتحقق مما إذا كان الخادم يستجيب. htop: عارض للعمليات، مفيد عندما يكون هناك شيء ما يجعل مروحة وحدة المعالجة المركزية الخاصة بك تتصرف مثل محرك نفاث وتريد تحديد البرنامج المخالف. tldr: متاح بوصفه أداة لسطر الأوامر. لاحظ أن بعض الاقتراحات السابقة يمكن أن تحتاج إلى التثبيت باستخدام npm، كما فعلنا مع Prettier. هذا المقال جزء من سلسلة مقالات بعنوان تعلم تطوير الويب والتي تشرح كامل عملية تطوير الويب من واجهات أمامية وخلفية بالكامل. ترجمة -وبتصرُّف- للمقال Command line crash course. اقرأ أيضًا المقال التالي: أدوات مطوري الويب المدمجة في المتصفحات المقال السابق: فهم أدوات تطوير الويب من طرف العميل كيف تستخدم أدوات المطوِّر في المتصفحات الحديثة الفرق بين صفحة الويب وموقع الويب وخادم الويب ومحرك البحث
×
×
  • أضف...