لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 10/23/22 في كل الموقع
-
أفضل لغات البرمجة الخلفية لبناء تطبيق دردشة الآن . نظرًا لأن التطوير الخلفي لا يعتمد على النظام الأساسي أو نظام التشغيل ، فإن هذا يؤدي إلى خفض المتغيرات. 1. Erlang هي لغة مصممة للعديد من العمليات التي تعمل بشكل متوازي. هذا يجعله مناسبًا جدًا عند البحث عن أفضل تطبيق دردشة. فوائده هي: قابل للتطوير عن طريق إضافة المزيد من النوى أو الآلات الجديدة. تصميمه المتزامن بفضل الخيوط خفيفة الوزن يجعله موثوقًا للغاية. 2. جافا سكريبت عند استخدامها للتطوير الخلفي ، تعمل Javascript عبر بيئة تسمى Node. كما هو الحال في تطوير الواجهة الأمامية ، هناك فوائد لاستخدام جافا سكريبت هنا أيضًا. المعالجة سريعة جدًا سهل التعلم نسبيًا شائع جدًا مع وصول واسع إلى دعم المطورين يوفر الإدخال / الإخراج غير المحظور ومعالجة الطلبات غير المتزامنة 3. GO تم إنشاء Go ، المعروف أيضًا باسم Golang بواسطة Google. بعض مزاياها هي: سريع جدا يقدم التزامن مفتوح المصدر 3. بايثون مع django channels سريع جدا مفتوح المصدر2 نقاط
-
2 نقاط
-
الإصدار 1.0.0
116454 تنزيل
سطع نجم لغة البرمجة بايثون في الآونة الأخيرة حتى بدأت تزاحم أقوى لغات البرمجة في الصدارة وذاك لمزايا هذه اللغة التي لا تنحصر أولها سهولة كتابة وقراءة شيفراتها حتى أصبحت الخيار الأول بين يدي المؤسسات الأكاديمية والتدريبية لتدريسها للطلاب الجدد الراغبين في الدخول إلى مجال علوم الحاسوب والبرمجة. أضف إلى ذلك أن بايثون لغةً متعدَّدة الأغراض والاستخدامات، لذا فهي دومًا الخيار الأول في شتى مجالات علوم الحاسوب الصاعدة مثل الذكاء الصنعي وتعلم الآلة وعلوم البيانات وغيرها، كما أنَّها مطلوبة بشدة في سوق العمل وتعتمدها كبرى الشركات التقنية. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن بني هذا العمل على كتاب «How to code in Python» لصاحبته ليزا تاغليفيري (Lisa Tagliaferri) وترجمه إلى العربية محمد بغات وعبد اللطيف ايمش، وحرره جميل بيلوني، ويأتي شارحًا المفاهيم البرمجية الأساسية بلغة بايثون، ونأمل في أكاديمية حسوب أن يكون إضافةً نافعةً للمكتبة العربيَّة وأن يفيد القارئ العربي في أن يكون منطلقًا للدخول إلى عالم البرمجة من أوسع أبوابه. رُبط هذا الكتاب مع توثيق لغة بايثون في موسوعة حسوب لتسهيل عملية الاطلاع على أي جزء من اللغة مباشرة وقراءة التفاصيل باللغة العربية. هذا الكتاب مرخص بموجب رخصة المشاع الإبداعي Creative Commons «نسب المُصنَّف - غير تجاري - الترخيص بالمثل 4.0». يمكنك قراءة فصول الكتاب على شكل مقالات من هذه الصفحة، «المرجع الشامل إلى تعلم لغة بايثون»، أو مباشرةً من الآتي: المقال الأول: دليل تعلم بايثون اعتبارات عملية للاختيار ما بين بايثون 2 و بايثون 3 المقال الثاني: تثبيت بايثون 3 وإعداد بيئتها البرمجية المقال الثالث: كيف تكتب أول برنامج لك المقال الرابع: كيفية استخدام سطر أوامر بايثون التفاعلي المقال الخامس: كيفية كتابة التعليقات المقال السادس: فهم أنواع البيانات المقال السابع: مدخل إلى التعامل مع السلاسل النصية المقال الثامن: كيفية تنسيق النصوص المقال التاسع: مقدمة إلى دوال التعامل مع السلاسل النصية المقال العاشر: آلية فهرسة السلاسل النصية وطريقة تقسيمها المقال الحادي عشر: كيفية التحويل بين أنواع البيانات المقال الثاني عشر: كيفية استخدام المتغيرات المقال الثالث عشر: كيفية استخدام آلية تنسيق السلاسل النصية المقال الرابع عشر: كيفية إجراء العمليات الحسابية المقال الخامس عشر: الدوال الرياضية المضمنة المقال السادس عشر: فهم العمليات المنطقية المقال السابع عشر: مدخل إلى القوائم المقال الثامن عشر: كيفية استخدام توابع القوائم المقال التاسع عشر: فهم كيفية استعمال List Comprehensions المقال العشرون: فهم نوع البيانات Tuples المقال الحادي والعشرين: فهم القواميس المقال الثاني والعشرين: كيفية استيراد الوحدات المقال الثالث والعشرين: كيفية كتابة الوحدات المقال الرابع والعشرين: كيفية كتابة التعليمات الشرطية المقال الخامس والعشرين: كيفية إنشاء حلقات تكرار while المقال السادس والعشرين: كيفية إنشاء حلقات تكرار for المقال السابع والعشرين: كيفية استخدام تعابير break وcontinue وpass عند التعامل مع حلقات التكرار المقال الثامن والعشرين: كيفية تعريف الدوال المقال التاسع والعشرين: كيفية استخدام *args و**kwargs المقال الثلاثين: كيفية إنشاء الأصناف وتعريف الكائنات المقال الحادي والثلاثين: فهم متغيرات الأصناف والنسخ المقال الثاني والثلاثين: وراثة الأصناف المقال الثالث والثلاثين: كيفية تطبيق التعددية الشكلية (Polymorphism) على الأصناف المقال الرابع والثلاثين: كيف تستخدم منقح بايثون المقال الخامس والثلاثين: كيفية تنقيح شيفرات بايثون من سطر الأوامر التفاعلي المقال السادس والثلاثين: كيف تستخدم التسجيل Logging المقال السابع والثلاثين: كيفية ترحيل شيفرة بايثون 2 إلى بايثون 31 نقطة -
يتناول هذا المقال، الأول من سلسلة دروس عن لغة الاستعلام البنائية Structured Query language التي تعرف بالاختصار المشهور SQL، مفهوم قواعد البيانات، وماذا نقصد بأنظمة إدارة قواعد البيانات، وما هو الجدول، وما هي خصائص قواعد البيانات العلاقية. ما هي قاعدة البيانات؟ بطريقة بسيطة مجرّدة من مفاهيم التقنية، قاعدة البيانات هي مكان لحفظ بيانات معينة على نحو مستمر بهدف الرجوع إليها وقت الحاجة، فدفتر أرقام الهواتف الذي كنا نستعمله في الماضي يُعدّ قاعدة بيانات؛ والكم الهائل من الفواتير المحاسبية الورقية المحفوظة في خزانات الأقسام المالية في الشركات قديماً، أيضاً هو قاعدة بيانات. وقِس على ذلك العديد من الأمثلة الواقعية والملموسة. نستنبطُ من هذا التعريف البسيط وجود خاصية هامة لقاعدة البيانات، ألا وهي “الاستمرارية” أو “الدوام” في حفظ البيانات. في الجانب التقني والبرمجي، فإن قاعدة البيانات Database هي عبارة عن مستودع تُحفظ البيانات فيه داخل جهاز الحاسوب أو الخادوم، ويتمتع هذا المستودع بخاصية الاستمرارية في حفظ البيانات. ونعني بخاصية الاستمرارية هنا أنه في حال إطفاء جهاز الحاسوب أو إعادة تشغيله أو انقطاع التواصل معه، فإن قاعدة البيانات وما تحتويه من بيانات تبقى موجودة ومحفوظة دون أي خلل. أنظمة إدارة قواعد البيانات العلاقية تُسمى البرمجيات التي تنشئ وتدير قواعد البيانات بأنظمة إدارة قواعد البيانات (Databases Management Systems) وتكتب بالاختصار DBMS. ما هي أنواع أنظمة إدارة قواعد البيانات؟ تختلف وتتعدد تسميات أنواع أنظمة إدارة البيانات، وهذا الاختلاف نابع بالدرجة الأولى من تقدم الزمن وما صاحبه من تقدم في العلوم والتقنيات، ومن ثم بالدرجة الثانية، ينبع الاختلاف من التقنيات والخصائص المتعددة لهذه الأنظمة وما تقدمه من خدمات. تنقسم أنواع أنظمة قواعد البيانات إلى ثلاثة أنواع رئيسية. نظام قاعدة البيانات الملف والواحد Flat File Database: يعدّ هذا النوع من الأنظمة قديما ومن النادر أن تجد أحدا يعمل عليه إلى الآن، وهو ببساطة قاعدة بيانات من ملف واحد كبير يحتوي على كل البيانات، وهو يشبه جدول واحد به كل البيانات. نظام إدارة قاعدة البيانات غير العلاقية Non-Relational DBMS :ظهر هذا النوع من أنظمة قواعد البيانات في ظل عصر تضخم البيانات وزيادة حجمها، وخاصة مع انتشار ما يسمى بالمواقع الاجتماعية وتطبيقات الجوال وصفحات الوب الحديثة، فهذا النوع من الأنظمة يسمح بحفظ بيانات غير مرتبة وفق بنية معينة Unstructured Data، وليس من الشرط أن تترابط هذه البيانات Not relational، كما يطلق عليها No-SQL Databases. نظام إدارة قاعدة البيانات العلاقية Relational DBMS: وهو النوع الأشهر والأكثر استخداما منذ بداية ظهوره والذي سنعتمده في هذه السلسلة لشرح SQL، حيث تُجمَّع في هذا النوع من الأنظمة البيانات التي لها علاقة ببعضها البعض في مكان واحد يسمى الجدول، مع وجود الإمكانية لربط الجداول مع بعضها البعض بعلاقات ترابط. دورة علوم الحاسوب دورة تدريبية متكاملة تضعك على بوابة الاحتراف في تعلم أساسيات البرمجة وعلوم الحاسوب اشترك الآن ما هو الجدول؟ يُعدّ الجدول العنصر الأساسي في قواعد البيانات العلاقية، وعليه تعتمد أغلب مكونات قاعدة البيانات من مشاهد Views ودوال Functions وحِزم Packages وغيرها من العناصر الأخرى. يتكون الجدول من أعمدة Columns وصفوف Rows، حيث تمثل الأعمدة ما يسمى بالخصائص Features، والصفوف عبارة عن القيم التي تأخذها الأعمدة وتسمى بالسجلات Records. يوضح الشكل التالي مثالا لجدول يحتوي على بيانات تواريخ ميلاد وأسماء طلاب في مدرسة، وفي المثال نوضح مكونات الجدول في قاعدة البيانات. خصائص قواعد البيانات العلاقية ومميزاتها ظلت قواعد البيانات العلاقية مسيطرة منذ بدايات ظهور النموذج الأساسي لها عام 1970 على يد عالم الحاسوب Frank Codd أثناء عمله لصالح شركة IBM، ولم تكن هذه الأفضلية التي يتمتع بها نظام قواعد البيانات العلاقية تأتي من فراغ، بل من الخصائص التي تتمتع بها. البساطة تُرتَّب البيانات في أنظمة قواعد البيانات العلاقية وتُحفَظ بطريقة بعيدة عن التعقيد، حيث يعدّ الجدول الذي تُحفظ فيه البيانات مفهوما لأغلب المستخدمين وخاصة الذين مارسوا أعمالا في مجال البيانات المجدولة أو مراجعة السجلات. سهولة الاستعلام عن البيانات بعد عمليات الإضافة على قاعدة البيانات، وعند الحاجة للرجوع لها، فإن نظام قواعد البيانات العلائقية يوفر آلية سهلة للاستعلام عن هذه البيانات واستردادها، وذلك عن طريق لغة SQL، بالإضافة إلى وجود الإمكانية للمستخدم أن يستعلم عن البيانات من أكثر من جدول في نفس الوقت باستخدام جمل الربط Joins. كما أن خاصية ترشيح Filtering البيانات وتحديد شروط خاصة لظهور سجلات معينة هو أمر متاح بكل سهولة. سلامة البيانات تعدّ هذه الخاصية أساسية في أي نظام قواعد بيانات بغض النظر عن نوعه. ونعني بهذه الخاصية أن تتوفر جميع القدرات والإمكانات في نظام قواعد البيانات لضمان دقة وصحة المعلومات الموجودة فيه. ويندرج تحت هذه الخاصية ما يسمى بقيود التكامل Integrity constraints والتي هي عبارة عن مجموعة من القيود التي يجب الالتزام بها عند التعامل مع البيانات في الجدول، وسنتكلم عنها في مقال متقدم. المرونة تتمتع قواعد البيانات العلاقية بطبيعتها بالمرونة والقابلية للتطوير، مما يجعلها قابلة للتكيف مع طلبات التغيير والزيادة في كم البيانات. وهذا يعني مثلا أنك تستطيع التغيير على هيكلية جدول معين دون التأثير على البيانات الموجودة فيه أو على قاعدة البيانات ككل، كما أنك – مثلا - لن تحتاج إلى وقف قاعدة البيانات وإعادة تشغيلها مرة أخرى لتنفيذ بعض لتغييرات عليها. ما هي البرمجيات التي تقدم قواعد البيانات العلاقية؟ تَتَعدد الشركات والبرمجيات التي تُقدم أنظمة إدارة قواعد البيانات، وكل منها له سوقه ومجاله الذي يشتهر به. نُقدم لكم في الفقرات القادمة بعضًا من أشهر أنظمة إدارة قواعد البيانات العلاقية. قواعد بيانات MySQL أحد أشهر أنظمة قواعد البيانات العلاقية مفتوحة المصدر. تستطيع إنشاء العديد من قواعد البيانات بداخلها، وتستطيع الوصول لها عبر الوِب. تَعمل MySQL على هيئة خِدمة Service تُتيح لأكثر من مستخدم الوصول إلى أكثر من قاعدة بيانات، وتشتهر بين معشر مبرمجي تطبيقات الوِب لارتباطها الشائع مع لغة البرمجة PHP، ويمكن تنصيبها على أكثر من نظام تشغيل مثل وندوز أو لينكس أو ماك. تعدّ MySQL الخيار المفضل للشركات الناشئة أو المتوسطة وذلك لسهولة التعامل معها وانخفاض تكاليف تشغيلها مقارنة بخيارات أخرى. قواعد بيانات أوراكل Oracle تعدّ شركة أوراكل عملاق الشركات البرمجية التي تقدم أنظمة إدارة قواعد البيانات العلاقية، وتأتي قاعدة البيانات أوراكل بأكثر من إصدار (حسب البيئة والغرض) تبدأ من الإصدار الشخصي والخفيف، وتنتهي بالإصدار المتقدم Enterprise. تتميز قواعد بيانات أوراكل بكم كبير من الإمكانات التي تسهل عليك حل العديد من المشاكل والعقبات في التطبيقات التي تديرها وتنشئها، مع وجود دعم فني قوي عبر مجتمع أوراكل، لذلك فهي تعتبر الخيار الإستراتيجي (البعيد المدى) للعديد من الشركات الكبيرة والجامعات والحكومات. قواعد بيانات مايكروسوفت Microsoft SQL Server من قواعد البيانات الشهيرة، والذي تأتي أيضا بأكثر من إصدار، لتلبي احتياجات المستخدمين المختلفة وبيئات عملهم، ولكي تتعامل مع البيانات في هذا النوع تحتاج لاستخدام النسخة الخاصة من SQL والمسماة T-SQL اختصارا ل Transact SQL والتي هي عبارة عن نسخة SQL مضاف عليه ادوال خاصة وتعديلات على طريقة حذف وتعديل السجلات. قواعد بيانات PostgreSQL قواعد بينات PostgreSQL من قواعد البيانات العلاقية المفضلة لدى بعض مطوري تطبيقات الوِب وتطبيقات سطح المكتب، وهو نظام إدارة قواعد بيانات مفتوح المصدر. توجد الكثير من الشركات الكبيرة والعاملة في مجال نطاقات إنترنت تعتمد على هذا النوع من قواعد البيانات.1 نقطة
-
1 نقطة
-
https://youtu.be/ruFoV01AikI هذا الفيديو هو الذي يشفي مشكلتي وقمت بتطبيقة بالحرف الوحد لكن لم يعمل معي مثل ما يعمل مع ناشر الفيديو ما السبب1 نقطة
-
1 نقطة
-
اسف على ذلك, المشطلة تظهر عند التشغيل ورسالة الخطأ net.sqlcipher.database.SQLiteException: no such table: tab1: , while compiling: SELECT * FROM tab1 WHERE id LIKE 1 مع العلم بأني تأكد جيد من اسماء الحقول1 نقطة
-
1 نقطة
-
لماذا برمجة الفروند اند حكرا للغة الجافا سكربت، وان كانت هناك بعض اللغات بدات تدخل مجال برمجة الفرونت اند لكنها لي س بذاك المستوى، رغم قوة هذة اللغات في مجال الباك اند اومجالات اخرى؟1 نقطة
-
بس هذة اللغات لعمل تطبيقات الموبايل ، اقصد هل هناك لغة للتطوير الويب من جهة الفرون اند غير جافاسكربت، لاني سمعت ان السي شارب دخل هذا المجال ولكن ليس بتلك القوة، هل هناك لغات اخرى، لااعرف؟1 نقطة
-
ماهي اللغات الاخرى ، التي يمكن استخدامها في الفرونت اند؟1 نقطة
-
أظن أن هذا بسبب تخصصيتها في الويب بدرجة أولى وبسبب تاريخها بدرجة ثانية، فجافاسكربت صممت أول مرة كلغة برمجة نصية لمتصفحات Netscape وهي أولى متصفحات الويب، ومن ثم تم اعتمادها من قبل Microsoft وهيمنتها على المجال بدعم متصفحات Internet Explorer لها في بداية الألفية الجديدة. اطلاق النسخة السادسة من ECMAScript في 2015 أيضا زادها هيمنة.1 نقطة
-
إذا كنت تريدهم على نفس الخط وبنفس الوقت متجاوب responsive تحتاج أولاً إعطاء الحافظة feature عرض كامل الشاشة %100 وتحديد خاصية flex-wrap بالقيمة wrap أي أن العناصر ستقوم بالنزول الى سطر جديد عندما لا يتسع عرض الشاشة لها جميعاً. يمكنك أيضاً إستعمال media query للتحكم أكثر بتجاوبية التصميم من خلال تعديل خواص flex box. #feature { width: 100%; /* تعديل عرض الحافظة */ display: flex; align-items: center; justify-content: space-between; /*flex-start الأفضل يكون */ flex-wrap: wrap; /* تعديل طريقة ملء الحافظة بالعناصر */ gap: 20px; }1 نقطة
-
السلام عليكم لماذا الكود التالي لايعمل let form = document.querySelector('.form') form.addEventListener('submit', function(e){ e.preventDefault() // if(username.value === ""){ // showError(username, 'username is required') // } else { // showSuccess(username) // } console.log(form); }) شكرا1 نقطة
-
في الconsole form تظهر لي فارغة و أعتقد أن ذلك هو أصل الخطأ كود javascript let form = document.getElementByClassName(".form") console.log(form) // event listeners form.addEventListener('submit', function(e){ e.preventDefault() // if(username.value === ""){ // showError(username, 'username is required') // } else { // showSuccess(username) // } console.log(form); }) html code <!-- section-form --> <div class="logInForm"> <h2>Sign Up</h2> <form class="form"> <input type="text" placeholder="Username" class="register-username"> <input type="text" placeholder="Email" class="register-email"> <input type="text" placeholder="Password" class="register-password"> <input type="text" placeholder="Confirm Password" class="register-confirm-password"> <input type="button" value="Sign In" class="register-btn"> </form> </div> عفوا قمت بالإصلاح التالي let form = document.getElementByClassName("form") ولكن تظهر لي نفس رسالة الخطأ1 نقطة
-
شكرا. من المفارقات أني وضعت form.form (class="form") نفس الشيء لاتعمل كود الجافاسكريبت // register-username-register-email-register-password-register-btn let form = document.querySelector('form') let username = document.querySelector(".register-username"); let email = document.querySelector(".register-email"); let password = document.querySelector(".register-password"); let confirmPassword = document.querySelector('.register-confirm-password') let registerBtn = document.querySelector(".register-btn"); // registerBtn.addEventListener('click', register) // function register(e){ // e.preventDefault() // if(registerUsername.value !== "" || registerEmail.value !=="" || registerPassword.value !== ""){ // localStorage.setItem("register-username", username.value) // localStorage.setItem("register-email", email.value) // localStorage.setItem("register-password", password.value) // } // setTimeout(()=>{ // window.location = "./login.html" // }, 1000) // } // show input error message // function showError(input,message) { // // const formControl = input // console.log(input,message); // // console.log(formControl); // } // event listeners form.addEventListener('submit', function(e){ e.preventDefault() // if(username.value === ""){ // showError(username, 'username is required') // } else { // showSuccess(username) // } console.log(form); }) كود html <div class="logInForm"> <h2>Sign Up</h2> <form action=""> <input type="text" placeholder="Username" class="register-username"> <input type="text" placeholder="Email" class="register-email"> <input type="text" placeholder="Password" class="register-password"> <input type="text" placeholder="Confirm Password" class="register-confirm-password"> <input type="button" value="Sign In" class="register-btn"> </form> </div> <script src="../js/register.js"></script> </body> form تظه لي فارغة1 نقطة
-
لا يُفهم الكثير من وصفك للمشكلة سوى أن الأمر لم يعمل لديك. حتى نشخص المشكلة نحتاج معرفة ما هي رسالة (أو رسائل) الخطأ التي تظهر لديك, أيضاً هل المشكلة في فشل عمل build للمشروع أم فقط أثناء التشغيل؟ يمكن أن يكون ألأمر عدم توافق إصدارات مثلاً , خصوصاً أن الفديو مضى عليه سنتين. يرجى مشاركة الرسائل التي تظهر في android studio عند عمل build إذا يحصل لديك فشل في هذه المرحلة.1 نقطة
-
احب اتخصصص ف مجال web application و كيفيه تصميم برامج كامله ب front and back end و ما اعرف ما هي اللغه او الطريق اللي يجهزني للوصول ل هذي المرحله …. في الكثير من اللغات و الطرق و لكن ما هي اللغه المطلوبه الان و المستقبل و كيف اكون جاهز للمرحله القادمه من سوق التقنيه ؟ و شكرا1 نقطة
-
1 نقطة
-
لقد قمت بالاطلاع على الملفات الخاصة بك وهي تعمل بشكل جيد ولا يوجد اي مشكلة. وقمت بالتعديل على الملف ولم يظهر لي المشكلة التي تظهر في الصورة الخاصة بك. وربما تكون المشكلة التي تواجهها هي أنك استخدمت الأمر npm run build الخاص بال production بدلاً من الأمر npm run dev الخاص بالـ development.1 نقطة
-
1 نقطة
-
1 نقطة
-
1 نقطة
-
لقد قمت بتجربة المشروع و بالضبط خاصية رفع الصور لم تُقابلني أية مشاكل قمت بالتعديل على بائع و غيرت صورة المتجر و تم تغييرها بنجاح، أيضاً من الإعدادات غيرت شعار الموقع و تم تغييره بنجاح. رُبما الطرف الآخر لم يقم بإستيراد قاعدة البيانات، أو أنه لم يحقق شروط الإتصال بقاعدة البيانات تلك في ملف الإتصال. من الممكن أيضاً أنه يعمل على نظام مغاير حيث توجد صلاحيات على نظام الملفات و المجلدات أي لا يُمكن الكتابة بداخلها، و في هذه الحالة يجب عليه ضبط الصلاحيات على تلك المجلدات حتى يتسنى للسكربت نقل الصور لها. من الأفضل أيضاً إنشاء ملف يتضمن طريقة و خطوات تشغيل المشروع على سيرفر محلي، و إرسالها له.1 نقطة
-
المشكلة أنك لم تقم بربط ملف الـ JavaScript مع ملف الـ index.html. قم بإضافة السطر التالي لملف الـ index.html ويجب ان يكون آخر عنصر في الـ body: <script src="index.js"></script>1 نقطة
-
في الصورة التي ارفقتها لا يظهر console. قم بمشاركة ملف الـ index.html1 نقطة
-
من الصورة التي ارفقتها يبدو أنك لم تقم بحفظ الملف لاحظ الدائرة البيضاء بجانب اسم الملف : اضغط على ctrl + s لحفظ الملف ثم حدث الصفحة وانظر إن كان سيظهر أم لا. في حال قمت بالخطوة السابقة ولم يظهر أي شيء رغم ذلك فقد تكون المشكلة في عملية الاستدعاء في ملف الـ html. قم بمشاركة ملفات المشروع بالكامل في حال لم تكن المشكلة في اي مما سبق.1 نقطة
-
1 نقطة
-
رسالة الخطأ تخبرك بوجود الملف Swap file COMMIT_EDITMSG.swp already exists وهو ما يسبب الخطأ وغالباً ما يكون سبب وجود هذا الملف هو انك قمت بالتعديل على ملف الـ git من خلال برنامج اخر مختلف عن الـ git مثل الـ terminal أو الـ VS code ولم تنهي التعديل. ما انصحك به هو اكمال العملية من المكان الذي بدأتها منه وليس من git وفي حال اردت الاكمال من git رغم ذلك فقم باختيار الخيار D لحذف الملف COMMIT_EDITMSG.swp1 نقطة
-
رسالة الخطأ تخبرك بوجود الملف Swap file COMMIT_EDITMSG.swp already exists وهو ما يسبب الخطأ وغالباً ما يكون سبب وجود هذا الملف هو انك قمت بالتعديل على ملف الـ git من خلال برنامج اخر مختلف عن الـ git مثل الـ terminal أو الـ VS code ولم تنهي التعديل. ما انصحك به هو اكمال العملية من المكان الذي بدأتها منه وليس من git وفي حال اردت الاكمال من git رغم ذلك فقم باختيار الخيار D لحذف الملف أو اكتب الامر : ".git/.COMMIT_EDITMSG.swp" ثم جربك مرة ثانية.1 نقطة
-
قمت بإنشاء popup تحتوي على المنتوجات المفضلة. يتم جلب هذه المصفوفات من مصفوفة من localStorage. المشكلة التي واجهتي هو عند الضغط على dislike يقع حذف العنصر لكن عند الضغط ثانية لا يقع الحذف إلا بعد تحديث الصفحة. الكود: // show my favorite products const favorites = document.querySelector(".favorite-products") favorites.addEventListener('click', ()=>{ // get liked products from localStorage favoriteItems = JSON.parse(localStorage.getItem('favoriteProducts')) if(favoriteItems.length != 0){ // create a popup let overlay = document.createElement('div') overlay.setAttribute("class", "popup-overlay") document.body.appendChild(overlay) // styling and draw the popup-overlay let favoriteWrapper = document.createElement('div') favoriteWrapper.setAttribute("class", "favorite-wrapper") overlay.appendChild(favoriteWrapper) function drawFavoriteProducts(favorites) { let drawFavoriteProducts = favorites.map(favItem => { return ` <i class="fa-sharp fa-solid fa-circle-xmark closeBtn"></i> <div class="favorite-card"> <p class="dislike" data-click="${favItem.id}"> Dislike <i class="fa-regular fa-thumbs-down"></i> </p> <img src="${favItem.img_url}.png" alt="" srcset=""> <h2 class="favorite-title">${favItem.title}</h2> </div> ` }) favoriteWrapper.innerHTML = drawFavoriteProducts.join("") } drawFavoriteProducts(favoriteItems) // remove disliked products let disliked = document.querySelectorAll('.dislike') disliked.forEach(item =>{ let dislikeId = item.getAttribute("data-click") item.addEventListener('click', ()=>{ favoriteItems = favoriteItems.filter((data) => data.id != dislikeId) localStorage.setItem("favoriteProducts", JSON.stringify(favoriteItems)) drawFavoriteProducts(favoriteItems) }) }) } }) // close popup document.addEventListener('click', function(e){ // console.log(e.target.className == "fa-sharp fa-solid fa-circle-xmark closeBtn"); if (e.target.className == "fa-sharp fa-solid fa-circle-xmark closeBtn") { e.target.parentNode.remove() document.querySelector('.popup-overlay').remove() } }) يمكنكم الإطلاع على الكود كاملا من خلال الرابط التالي: https://github.com/Saadaoui-Forkan/Ecommerce-website شكرا على المساعدة.1 نقطة
-
المشكلة هي أنك في نهاية ال dislike الأول تقوم بإعادة إنشاء عناصر HTML من أجل favoriteItems عن طريق الوظيفة drawFavoriteProducts لكن لا تقوم بإعادة إسناد حدث الـ dislike لتلك العناصر، لحل المشكلة يمكنك التعديل على الكود بالشكل التالي: favorites.addEventListener('click', ()=>{ // get liked products from localStorage favoriteItems = JSON.parse(localStorage.getItem('favoriteProducts')) if(favoriteItems.length != 0){ // create a popup let overlay = document.createElement("div"); overlay.setAttribute("class", "popup-overlay"); document.body.appendChild(overlay); // styling and draw the popup-overlay let favoriteWrapper = document.createElement("div"); favoriteWrapper.setAttribute("class", "favorite-wrapper"); overlay.appendChild(favoriteWrapper); function drawFavoriteProducts(favorites) { let drawFavoriteProducts = favorites.map((favItem) => { return ` <i class="fa-sharp fa-solid fa-circle-xmark closeBtn"></i> <div class="favorite-card"> <p class="dislike" data-click="${favItem.id}"> Dislike <i class="fa-regular fa-thumbs-down"></i> </p> <img src="${favItem.img_url}.png" alt="" srcset=""> <h2 class="favorite-title">${favItem.title}</h2> </div> `; }); favoriteWrapper.innerHTML = drawFavoriteProducts.join(""); // أسندنا هنا قابلية إزالة الإعجاب إلى العناصر ablityToDislike(); } drawFavoriteProducts(favoriteItems); // remove disliked products //ablityToDislike وضعت هنا وظيفة إسناد الأحداث ضمن طريقة سميتها function ablityToDislike() { let disliked = document.querySelectorAll(".dislike"); disliked.forEach((item) => { let dislikeId = item.getAttribute("data-click"); item.addEventListener("click", () => { favoriteItems = favoriteItems.filter((data) => data.id != dislikeId); localStorage.setItem("favoriteProducts", JSON.stringify(favoriteItems)); drawFavoriteProducts(favoriteItems); }); }); } } }) هذا سيحل المشكلة، بالتوفيق إن شاء الله.1 نقطة
-
قد يكون صديقك لم ينشئ قاعدة البيانات بالشكل الصحيح أو انه استخدم اسم المستخدم وكلمة السر الخاصين بقاعدة البيانات الموجودة على جهازك والمرفقة مع المشروع بدلاً من اسم المستخدم وكلمة السر الخاصة بقاعدة البيانات الموجودة على جهازه، ولذلك تظهر له المشكلة عند حفظ الصورة. فحسب ما فهمته منك ان المشروع مكون من سيرفر و قاعدة بيانات فإما ان تكون المشكلة في اصدار الـ node او أياً كانت اللغة التي تبرمجين السيرفر بها أو ان المشكلة في قاعدة البيانات. اجعليه يتأكد من وجود قاعدة البيانات على جهازه وأنها تعمل ثم اجعليه يتأكد من كونها مربوطة بشكل صحيح مع السيرفر "كلمة السر واسم المستخدم مطابقين لقاعدة البيانات الخاصة به" ويتأكد كذلك من ان اصدار الـ node او أياً كانت اللغة التي تبرمجين السيرفر بها مطابق لما هو لديك. وفي حال لم تكن المشكلة في اي واحدة من الخطوات السابقة فاجعليه يقوم بطباعة الخطأ الذي يواجهه عن طريق استخدام try و catch في node أو ما يقابلها في اللغة التي تستخدمينها. try { // الكود الخاص بحفظ البيانات إلى قاعدة البيانات } catch(error) { console.log(error) // console سيطبع الخطأ في الـ }1 نقطة
-
ارفقت المشروع بالكامل المشكلة فقط عن اضافة الصور حساب المدير admin كلمة المرور admin1231 نقطة
-
يُرجى إرفاق صورة للمشكلة حتى يمكننا تشخيصها1 نقطة
-
نموذج TCP/IP TCP/IP هو مجموعة من قواعد الاتصال على الشبكة يهدف إلى توفير عنوان IP لكل جهاز على الشبكة من أجل التمكن من توجيه حزم البيانات IP packet . وقد ثم تصميم هذا البرتوكول لتلبية الحاجات التالية: تقسيم الرسالة إلى عدة حزم قبل إرسالها في الشبكة. استخدام عنوان IP خاص بكل جهاز داخل الشبكة. توجيه الحزم عبر الشبكة (routing). التحقق من أخطاء الإرسال. إسم النموذج جاء من البرتوكول TCP و البرتوكول IP لأنهما أول برتوكولات يضافان إلى حزمة برتوكولات TCP/IP. إن معرفة بروتوكولTCP / IP مهمة لمن يرغب في إحتراف إدارة الشبكة. من أجل تطبيق نموذج TCP / IP بشكل مستقل عن نظام التشغيل (windows, linux, mac os…. )، تم تقسيم بروتوكول TCP/IP إلى عدة وحدات بمهمات محددة و بترتيب معين، لنحصل في النهاية على نظام متعدد الطبقات. في هذا النموذج البيانات تمر إلى الشبكة عبر عدة مستويات أو طبقات . وبالتالي، تتم معالجة البيانات (حزم أو packet) المرسلة على الشبكة في كل طبقة على حدى.1 نقطة
-
محاولتك لتطوير معرض الأعمال الخص بك (Portfolio) فكرة جيدة لأنه يعتبر الواجهة التي سوف تعرف العميل عليك ، أيضاً بناء المواقع والممارسة المستمرة سوف يزيد لديك مهارات تصميم الواجهات . وسوف أقدم لك عدّة أفكار لتطوير مواقع :- موقع ويب أسئلة وأجوبة موقع ويب يمثل متجر إلكتروني (قم بتصميمه بطريقتك الخاصة بعيداً عن الذي تدرتبه خلال الدورة ) موقع ويب عن السياحة والسفر موقع ويب عن الرياضة موقع ويب عن الأخبار موقع ويب عن الموضة والأزياء موقع ويب مختص بالطب و الصحة موقع ويب متخصص في وساطة الخدمات مثل (خمسات ومستقل وغيرها) موقع ويب عن الكورسات ولرؤية المزيد حول الأفكار قم بزيارة المواقع المختصة في أعمال مثل بيهانس أو يمكنك تصفح معرض أعمال المستقلين ورؤية أعمالهم ، وأخذ أفكار لتطبيقها والتدريب عليها يمكنك الإطلاع على هذا المقال للإستفادة من أفكار تطوير المواقع ويوجد على مدونة خمسات مقال رائع في جلب أفكار لتطوير المواقع ، ويعرض 30 فكرة مميزة ، يمكنك الإطلاع عليه من هنا1 نقطة
-
يمكنك عمل مدونة إذاً. هذه اكثر الافكار الرائجة، ويمكنني ان اعطيك المزيد من الافكار ولكن ليس هنالك الكثير من الطلب عليها. والافضل الان ان تبحث عن شخص يحتاج لموقع الكتروني ولو قمت بتنفيذه بمقابل رمزي وهذا افضل من تصميم مواقع للـ portfolio.1 نقطة
-
ولكنهم كلهم مشروحين في الدوره ولقد فعلتهم عدا موقع أكاديمية أو مدرسة.1 نقطة
-
الموقع الذي شاركته جيد جداً و هذه بعض الافكار المهمة والبسيطة : صفحة هبوط لمنتج معين صفحة سيتم اطلاق الموقع بعد كذا ..إلخ. صفحة الصفحة غير موجودة 404. لوحة تحكم. موقع للتسوق (متجر) موقع أكاديمية أو مدرسة.1 نقطة
-
إن كنت تستعمل نظام MacOS فيمكنك تحميل وتثبيت git عبر Homebrew من خلال الأمر التالي: brew install git أو يمكنك تحميل ملف التثبيت بصيغة dmg من هنا، فقط أضغط على Download وإنتظر 5 ثواني.1 نقطة
-
1 نقطة
-
شهد مجال تطوير الوِب في السنوات الأخيرة تطورات هائلة على جميع المستويات والذي أدى إلى تحسنيات كبيرة على مستوى السرعة والجودة وقابلية الصيانة. ليس ذلك فحسب بل تعدى الأمر إلى إمكانية أتمتة بعض المهام الروتينية والتركيز على جودة العمل المُسلم ورفع الإنتاجية وذلك باستخدام أدوات مساعدة مثل Webpack، والتي تعدّ من أشهر الأدوات المساعدة بل إنها أصبحت عنصرًا أساسيًا في أي مشروع يُبنى بلغة جافاسكربت. حدثت بعض هذه التطورات على صعيد تطوير الواجهات الخلفية (Back-end) مثل ظهور Node.js والّتي تستخدم استخدامًا كبيرًا في هذه الأيام، وبعضها الآخر كان في تطوير الواجهات الأمامية (front-end)، ومن أبرز هذه التطورات هي النقلة النوعية للغة جافاسكربت ودعمها للعديد من المميزات والخصائص، والتي أدت في نهاية المطاف لجعلها واحدةً من أبرز الخيارات القوية في برمجة الوِب. بل وحتى إنها احتلت المرتبة الأولى في ترتيب أشهر لغات البرمجة شعبية لعام 2019 وذلك بحسب إحصائية موقع Stackoverflow. وكما أعلنت شركة Web3Techs أن لغة جافاسكربت مُستخدمة من قِبل حوالي 95% من مواقع العالم قاطبةً، وهي أيضًا على رأس قائمة "أكثر لغة مشهورة في برمجة الواجهات الأمامية" بحسب نفس الشركة. ولكن كيف جرت هذه التحولات السريعة والقوية بنفس الوقت لهذه اللغة؟ وما هي المراحل الأساسية الّتي مرت بها؟ وما هي الأدوات المختلفة الّتي ظهرت بالتزامن مع تطور هذه اللغة؟ وما هي أدوات البناء (مثل Gulp)؟ وما هو مجمع الوحدات (مثل Webpack)؟ ولماذا ظهر؟ وما هي مميزاته؟ سنحاول في هذا الدليل مرافقتكم في رحلة استكشافية للإجابة على هذه الأسئلة، ولنستعرض فيها أيضًا التاريخ العريق لهذه اللغة، ومعرفة الاسباب الرئيسية لتطورها وكيف احتلت بهذه السرعة المكانة العالية الّتي هي عليها اليوم. فهل أنت مستعد لمشاركتنا في هذه الرحلة؟ لمحة تاريخية بالعودة إلى زمن ما قبل الأدوات الحديثة كيف كان الحال آنذاك؟ في الحقيقة كانت الأمور بسيطة وسهلةً جدًا. سنبدأ رحلتنا بالتعرف على طرق استخدام لغة جافاسكربت في ملفات HTML. طرق استخدام لغة جافاسكربت يوجد طرقٌ عديدة يمكننا لتضمين الشيفرات البرمجية للغة جافاسكربت في ملفات HTML، يعتمد اختيارنا للطريقة بحسب حجم المشروع الّذي سنعمل عليه وسنذكر أهم الطرق المستخدمة من البداية وحتى يومنا الحالي. الشيفرة السطرية بدأت لغة جافاسكربت في كتابة شيفرتها البرمجية بهذه الطريقة البسيطة إذ أن الشيفرة البرمجية مكتوبة مباشرة في ملف HTML داخل وسم <script>. لا بدّ بأن معظمنا كتبَ شيفرة برمجية بهذه الطريقة في بداية تعلمه لهذه اللغة وذلك من كونها أسهل الطرق لتطبيق شيفرة برمجية معينة. في المثال التالي يحتوي الملف index.html على شيفرة جافاسكربت سطرية: <html> <head> <meta charset="UTF-8"> <title>Inline Example</title> </head> <body> <h1> The Answer is <span id="answer"></span> </h1> <script type="text/javascript"> function add(a, b) { return a + b; } function reduce(arr, iteratee) { var index = 0, length = arr.length, memo = arr[index]; for(index += 1; index < length; index += 1){ memo = iteratee(memo, arr[index]) } return memo; } function sum(arr){ return reduce(arr, add); } /* Main Function */ var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ]; var answer = sum(values) document.getElementById("answer").innerHTML = answer; </script> </body> </html> هذه الطريقة جيدة جدًا للبدء إذ لا يوجد أي ملفات خارجية أو تبعية ناشئة بين الملفات، ولكن هذه هي الطريقة المثالية لبناء شيفرة برمجية غير قابلة للصيانة وذلك بسبب المشاكل التالية: عدم إمكانية إعادة استخدام الشيفرة البرمجية: فإذا احتجنا إلى إضافة صفحة أخرى وهذه الصفحة تحتاج إلى بعض الوظائف المحققة في الشيفرة البرمجية الّتي كتبناها سابقًا في الملف السابق فيجب علينا حينها نسخ الشيفرة البرمجية المطلوبة ولصقها في الصفحة المراد تحقيق الوظائف فيها. عدم وجود ثبات في التبعية بين الشيفرة البرمجية: أنت مسؤول عن تنسيق التبعية بين الدوال المستخدمة على سبيل المثال إن كان هنالك دالة معينة تعتمد في وظيفتها على نتيجة دالة أخرى محققة قبلها فيجب علينا حينها الانتباه لهذا الأمر وعدم تغيير ترتيب تواجد الدوال في الشيفرة البرمجية. التضارب في المتغيرات العامة (Global Variables): جميع الدوال والمتغيرات ستكون موجودة على نطاق عام (Global Scope) وهذا بدوره سيؤدي إلى تضارب في المتحولات، ويحدث هذا عند ازدياد ضخامة المشروع وكتابة شيفرة برمجية كبيرة مما يجعل إمكانية إعادة استخدام نفس أسماء المتحولات أمر وارد الحدوث بقوة، وخصيصًا إن كان هنالك أكثر من مبرمج يعمل على نفس المشروع. استخدام الملفات الخارجية الطريقة الأخرى المستخدمة في كتابة الشيفرة البرمجية للغة جافاسكربت هي استخدام ملفات منفصلة إذ سنجزّء الشيفرة البرمجية إلى أجزاء صغيرة ونحملها تباعًا في ملف index.html. وبهذه الطريقة يمكننا استدعاء مكتبات منفصلة والّتي تقدم لنا وظائف إضافية مثل مكتبة moment (والتي تتيح لنا التلاعب بالتواريخ وإمكانية إجراء تحويلات معينة من نمط تاريخٍ معين إلى نمطٍ آخر). وبالإضافة إلى ذلك أدى استخدام الملفات المنفصلة في الشيفرة البرمجية إلى إتاحة الفرصة لنا بإعادة استخدام الشيفرة البرمجية أكثر من مرة ومن أي مكان نريد، وبذلك نكون تخلصنا من فكرة نسخ الشيفرة البرمجية من مكان ما ولصقها في مكان آخر متى ما أردنا استخدام هذه الشيفرة. الملف index.html هو الملف الّذي سيستدعي جميع التبعيات (ملفات جافاسكربت) الّتي سيحتاجها ويكون محتواه على الشكل التالي: <html> <head> <meta charset="UTF-8"> <title>External File Example</title> <!-- الملف الرئيسي الّذي سيستدعي ملفات جافاسكربت --> </head> <body> <h1> The Answer is <span id="answer"></span> </h1> <script type="text/javascript" src="./add.js"></script> <script type="text/javascript" src="./reduce.js"></script> <script type="text/javascript" src="./sum.js"></script> <script type="text/javascript" src="./main.js"></script> </body> </html> إن هذا المثال يجمع عناصر المصفوفة الموجودة في الملف main.js ويظهرها في صفحة index.html ويكون محتوى الملفات على الشكل التالي: الملف add.js: function add(a, b) { return a + b; } الملف reduce.js function reduce(arr, iteratee) { var index = 0, length = arr.length, memo = arr[index]; index += 1; for(; index < length; index += 1){ memo = iteratee(memo, arr[index]) } return memo; } الملف sum.js function sum(arr){ return reduce(arr, add); } الملف main.js var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ]; var answer = sum(values) document.getElementById("answer").innerHTML = answer; ولكن هذا الحل لم يكُ مثاليًا بل واجه المشاكل التالية: عدم وجود ثبات في التبعية بين الشيفرة البرمجية. التضارب في المتغيرات العامة مازالت موجودة. تحديث المكتبات أو الملفات المستدعاة سيكون يدويًا في حال ظهور نسخة جديدة للمكتبة (أو الملف). زيادة عدد طلبات HTTP بين المخدم والعميل: يؤدي زيادة الملفات المتضمنة في إلى زيادة الحمل على المخدم. فعلى سبيل المثال في الشيفرة السابقة سيحتاج كلّ ملف من الملفات الأربع طلب HTTP منفصل (أي أربع رحلات ذهاب وعودة من المخدم وإلى جهاز العميل) وبناءً عليه سيكون من الأفضل لو استخدمنا ملفًا واحدًا بدلًا من أربع ملفات منفصلة. كما في المثال التالي: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>External File Example</title> <!-- الملف الرئيسي الّذي سيستدعي ملفات جافاسكربت --> </head> <body> <h1> The Answer is <span id="answer"></span> </h1> <script src="/dist/bundle.js"></script> </body> </html> استخدام كائن الوحدات (Module Object) ونمط الوحدات (Module Pattern) باستخدام نمط الوحدات (والذي تحدثنا عنه في مقال مفصّل) يمكننا أن نقلل التضارب في المتغيّرات العامة. إذ إننا سنكشف عن كائن واحد للمجال العام (Global Scope) وهذا الكائن سيحتوي على كافة الدوال والقيم الّتي سنحتاج إليها في تطبيق الوِب خاصتنا. في المثال التالي سنكشف عن كائن واحد وهو myApp للمجال العام (Global Scope)، وكلّ الدوال ستكون متاحة عن طريق هذا الكائن. والمثال التالي سيُوضح الفكرة: الملف my-app.js: var myApp = {}; الملف add.js: (function(){ myApp.add = function(a, b) { return a + b; } })(); الملف reduce.js: (function () { myApp.reduce = function (arr, iteratee) { var index = 0, length = arr.length, memo = arr[index]; index += 1; for (; index < length; index += 1) { memo = iteratee(memo, arr[index]) } return memo; } })(); الملف sum.js: (function () { myApp.sum = function (arr) { return myApp.reduce(arr, myUtil.add); } })(); الملف main.js: (function (app) { var values = [1, 2, 4, 5, 6, 7, 8, 9]; var answer = app.sum(values) document.getElementById("answer").innerHTML = answer; })(myApp); الملف index.html: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>JS Modules</title> </head> <body> <h1> The Answer is <span id="answer"></span> </h1> <script type="text/javascript" src="./my-app.js"></script> <script type="text/javascript" src="./add.js"></script> <script type="text/javascript" src="./reduce.js"></script> <script type="text/javascript" src="./sum.js"></script> <script type="text/javascript" src="./main.js"></script> </body> </html> لاحظ أن كلّ ملف من الملفات غُلّف بطريقة نمط الوحدات ما عدا my-app.js إذ أن طريقة التغليف العامة المتبعة في نمط الوحدات هي على الشكل التالي: (function(){ /*... your code goes here ...*/ })(); بهذه الطريقة نجد أن كلّ المتحولات المحلية ستبقى ضمن مجال الدالة المستخدمة فيه. وبذلك لن يكون هنالك أي احتمالية لحدوث تضارب في المتغيرات العامة. وهكذا نكون حللنا مشكلة التضارب في المتغيرات العامة. سيكون الوصول إلى الدوال المعرفة في الملفات مثل (add و reduce ..إلخ) عن طريق ربط اسم الدالة مع الكائن myApp كما في المثال التالي: myApp.add(1,2); myApp.sum([1,2,3,4]); myApp.reduce(add, value); كما يمكننا أيضًا تمرير الكائن myApp كوسيط مثل ما فعلنا في ملف main.js وذلك من أجل تصغير أسم الكائن وجعل الشيفرة البرمجية أصغر حجمًا. تعدّ هذه الطريقة تحسنًا ممتازًا بالموازنة مع المثال السابق وفي الحقيقة إن أشهر مكتبات جافاسكربت مثل jQuery تستخدم هذه الطريقة؛ إذ أنها فقط تكشف عن متغير عام وهو $ وكل التوابع تُستدعى عن طريق هذا الكائن. في الحقيقة إننا إلى هذه اللحظة لم نصل إلى الحل النهائي إذ أن الحل السابق لا يزال يعاني من بعض المشاكل مثل: عدم وجود ثبات في التبعية بين الشيفرة البرمجية: نلاحظ في ملف index.html الملفات لا تزال بحاجة إلى وضعها وفق ترتيب معين يحافظ على التبعية بين الملفات إذ أن الملف myApp.js يجب أن يأتي قبل أي ملف وملف main.js يجب أن يأتي بعد كلّ الملفات. التضارب في المتغيّرات العامة: صحيح أننا قللنا عدد المتغيّرات العامة إلى الواحد ولكنها لا تزال موجودة. تحديث المكتبات: إن تحديث المكتبات (أو التبعيات أو أي ملف عمومًا) المستدعاة سيكون يدويًا في حال ظهور نسخة جديدة للمكتبة أو للتبعية. زيادة عدد طلبات HTTP بين الخادم والعميل: وذلك بعد زيادة عدد الملفات المستدعاة في ملف index.html. ظهور CommonJS جرت نقاشات عديدة في عام 2009 حول إمكانية جلب لغة جافاسكربت إلى الواجهات الخلفية (استخدامها من جانب الخادم) وفعلًا كان ذلك على يد مهندسٍ شاب يعمل لدى شركة موزيلا ويُدعى كيفن دانجور (Kevin Dangoor). قدمت CommonJS طريقة لتعريف الوحدات لحل مشكلة المجال في جافاسكربت من خلال التأكد من تنفيذ كلّ وحدة في فضاء الأسماء (namespace) الخاص بها وذلك بإجبار الوحدات على تصدير تلك المتغيّرات صراحةً (الذين نريد عرضهم واستخدامهم في المجال العام) وأيضًا تعريف تلك الوحدات المطلوبة للعمل بالشكل الصحيح مع بعضها بعضًا. بالإضافة إلى ذلك قدمت CommonJS واجهة برمجية مخصصة للجافاسكربت هذه المميزات فتحت أبصار العالم لإمكانيات هذه اللغة العظيمة، وجذبت اهتمام كبير من المطورين والذي انعكس في سرعة تطورها وتأقلمها مع جميع التغيّرات والمشاكل الّتي واجهتها. إن CommonJS ليست مكتبة جافاسكربت وإنما هي معايير تنظيمية لكتابة الشيفرة البرمجية (على سبيل المثال الطريقة الّتي خصصتها لاستيراد الوحدات استيرادً منظمًا) وهذه المعايير شبيه بالّتي تطرحها منظمة ECMA (European Computer Manufacturers Association). ولتحقيق التنظيم في طريقة استدعاء الوحدات تقدم لنا CommonJS الدوال والأغراض المساعدة لذلك وهي: دالة require(): والّتي تسمح لنا باستيراد وحدات معينة في المجال المحلي الّذي نكون فيه. كائن الوحدة module.exports: والذي يسمح لنا بتصدير دالة ما من المجال الحالي إلى الوحدة. ولنأخذ مثلًا بسيطًا عن كيفية استخدام CommonJS: الملف moduleA.js: module.exports = function( value ){ return value*2; } الملف moduleB.js: var multiplyBy2 = require('./moduleA'); var result = multiplyBy2( 4 ); لاحظ أننا لم نستخدم الاسم الكامل للملف moduleA.js وإنما حذفنا اللاحقة، كما أن /. تشير إلى أن الوحدة moduleA موجودة في نفس المجلد الموجود فيه moduleB. وبفضل مشروع CommonJS ظهر أكبر مشروع معتمدًا عليه وهو Node.js وهي بيئة تشغيل لغة جافاسكربت مفتوحة المصدر وتعمل على جميع أنظمة التشغيل، والّتي تستطيع تشغيل شيفرة جافاسكربت خارج المتصفحات. بالإضافة إلى ذلك تسمح Node.js للمطوري الواجهات الخلفية باستخدام جافاسكربت لكتابة برمجيات تعمل من جهة الخادم وذلك لتوليد صفحات الوِب توليدًا ديناميكيًا قبل إرسالها إلى المتصفح، وكما تستطيع أيضًا التعامل مع الملفات وقواعد البيانات، ومختلف أنظمة الشبكات وخدمات أنظمة التشغيل. الوحدة غير المتزامنة AMD علِمنا إن الهدف الرئيسي الّذي بنيت عليه CommonJS هو استخدامها في الواجهات الخلفية (من طرف المخدم) ولذلك مشكلة استدعاء الوحدات استدعاءً متزامنًا لم تكُ مشكلة في ذلك الحين، بل ولم تكُ لتظهر لولا إصرار مجتمع المطورين على جلب CommonJS إلى الواجهات الأمامية ولنُلخص مشكلة CommonJS هي عندما نستدعي الوحدة معينة في ملف ما ولتكن مثلًا add كما في السطر التالي: var add=require('add'); سيتوقف النظام حتى تصبح الوحدة add جاهزة؛ أي أن هذا السطر البرمجي سيجمد المتصفح أثناء تحميل هذا الملف. ماذا لو كان هنالك أكثر من وحدة مطلوبة كيف ستكون الأمور عندها؟ لذلك إنها ليست أفضل طريقة لتعريف الوحدات من جانب المتصفح. من أجل نقلِ صياغة تعريف وحدة ما من صياغة يفهمها الخادم إلى صياغة يفهمها المتصفح قدمت لنا CommonJS العديد من التنسيقات لتعريف الوحدات وكانت واحدة من أشهرها هي تعريف الوحدات بالطريقة غير المتزامنة (Asynchronous Module Definition) والّتي تُعرف إختصارًا AMD وتكون طريقة تعريفها كما في المثال التالي: define([‘add’, ‘reduce’], function(add, reduce){ return function(){...}; }); إن الدالة define تأخذ قائمة بالتبعيّات الموجودة والتي مرّرناها لها كقائمة وستُعيد النتيجة إلى الدالة المجهولة (Anonymous function) كوسطاء (الدالة المجهولة هي الدالة الّتي ليس لديها اسم ولقد فصلنا في مقالٍ سابق كيفية استخدمها وذلك في سلسلة تعلم جافاسكربت). وسيكون ترتيب التبعيات بنفس الترتيب الموجود في القائمة ومن ثم ستُعيد الدالة المجهولة النتيجة والتي سنُصدرها ونستخدمها في نهاية المطاف. هذه الطريقة تُكافئ الطريقة السابقة لاستدعاء الوحدات (التبعيات) لأنها تعطي نفس النتائج الوظيفية، إلا أن هذه الطريقة تستخدم التحميل غير المتزامن للوحدات. إن CommonJS و AMD حلّت آخر مشكلتين متبقيتين مع نمط الوحدات وهما على الشكل التالي: عدم وجود ثبات في التبعية بين الشيفرة البرمجية. التضارب في المتغيرات العامة. نحن فقط سنهتم بالتبعيات الموجودة من أجل كلّ وحدة (أو ملف) وبالتأكيد في هذه الحالة لا يوجد هناك أي تضارب في المتغيرات العامة. مُحمل الوحدات RequireJS في الحقيقة إن طريقة استخدام AMD مع CommonJS كانت مفيدة جدًا، ولكن كيف يمكننا استخدامها في المتصفح وهنا جاء محمل الوحدات RequireJS إلى الساحة والّذي سيساعدنا لتحميل الوحدات تحميلًا غير متزامن AMD. والذي يعتبر من أوائل التطبيقات الفعلية لمكتبة CommonJS مع AMD. على الرغم من إسمها، إلا أن هدفها ليس دعم بناء جملة 'require' في CommonJS. إن RequireJS تُتيح لنا كتابة وحدات من نمط AMD. ملاحظة: قبل أن تطبق المثال التالي، يجب عليك تنزيل ملف require.js من موقعه الرسمي. لاحظ أن الاستدعاء الوحيد الموجود في ملف index.html هو: <script data-main=”main” src=”require.js”> </script> هذا الوسم سيُحمِّلُ محليًا مكتبة require.js إلى الصفحة، ومن بعدها السمة data-main في الوسم <script> ستخبر المكتبة من أي ملف ستبدأ. بعد أن تستدعي require.js ملف main.js سيقودنا هذا الملف إلى التبعيات المتعلقة به، وهكذا إلى أن نحمل جميع التبعيات (الملفات) المطلوبة. ملاحظة: استخدمنا main فقط بدون الإشارة إلى اللاحقة والتي هي js وذلك لأن هذه المكتبة تفترض بأن كلّ القيم الّتي سنُسندها لهذه الخاصية ستكون ملفات جافاسكربت. وسيكون تحميل التبعيات للمثال السابق كما في الصورة التالية: نلاحظ أن المتصفح يحمّل الملف index.html والملف بدوره يحمّل المكتبة require.js والّذي سيتكفل ببقية الملفات الأخرى. وبهذا نكون حللنا جميع المشاكل الّتي واجهتنا حتى هذه اللحظة، ولكن وكما هي العادة في عالم البرمجة في كلّ محاولة لإصلاح مشكلةٍ ما تظهر مشكلةٌ أخرى، ولكن عادة ما تكون أخف من سابقتها في التأثير. ومن المشاكل الّتي ظهرت مع هذا الحل هي: صياغة AMD طويلة ومضجرة: إذ أن كلّ شيء يجب تغليفة بالدالة define ولذا يوجد مسافة بادئة (يُقصد تعريف الدالة define) للشيفرة البرمجية وهذه المشكلة يمكن ألا تظهر مع الملفات الصغيرة ولكن ما إن يكبر حجم الملف وتزداد تفاصيله ستصبح مشكلة كبيرة. قوائم الاعتماديات الموجودة في المصفوفة: إن قائمة الاعتماديات الموجودة في المصفوفة يجب أن تتشابه تمامًا مع القائمة المررة كوسيط للدالة وإذا كان لدينا العديد من التبعيات ستزداد الأمور تعقيدًا. في النسخة الحالية من HTTP للمتصفحات تحميل العديد من الملفات الصغيرة سيخفف من الأداء (وخصيصًا بدون استخدام أي تقنيات مساعدة مثل: ذاكرة التخزين المؤقت Cache). مجمع الوحدات أو مجمع الحزم (Module Bundler) انطلاقًا من الأسباب السابقة أراد بعض الأشخاص بناء التعليمات من خلال CommonJS لكن هذه الأخيرة مخصصة للعمل على الخوادم وليس المتصفح وهي تدعم التحميل المتزامن أيضًا. بقيت هذه الإشكالية عصية على الحل حتى جاء مجمع الوحدات Browserify لينقذ الموقف ويقدم لنا حلولً مناسبة. وهو يعدّ أول مجمع وحدات وجد آنذاك، وكان باستطاعته تحويل الوحدات المكتوبة بصياغة CommonJS والتي لا يستطيع المتصفح أن يفهمها بطبيعة الحال (وذلك لأنها بُنيت بالأساس للتعامل مع الخادم) إلى صياغة يفهمها المتصفح. يمكننا التعامل مع مجمع الحزم Browserify من خلال سطر الأوامر إذ يمكنك إعطاؤه أمرًا ما وتمرير بعض الخيارات الإضافية المتاحة لكل أمر من الأوامر الموجودة. وهو يعتمد في بنيته على مدير الحزم npm وبيئة Node.js. يمكن لمُجمع الوحدات Browserify تجميع شجرة التبعيات للشيفرة البرمجية الخاصة بك وتحزيمها في ملف واحد. وأيضًا يحل مشكلة عدم إمكانية الوصول إلى الملفات من قبل المتصفح إذ إنه يبحث عن جميع تعليمات require (والتي لا يمكن للمتصفح أن يستدعي الملف المطلوب لأنه لا يملك الصلاحيات المناسبة لذلك) فيستبدلها بمحتوى الملف المراد تضمينه (وذلك انطلاقًا من اعتماده على Node.js والتي تستطيع الوصول إلى الملفات) وهكذا يصبح الملف جاهزًا للتنفيذ في المتصفح ولا يواجه أي مشكلة. حلول كثيرة فأي منها سنستخدم؟ إذا لدينا الكثير من الخيارات المتاحة فأي واحدة منهم سنستخدم في مشروعنا القادم؟ إذ إننا حتى هذه اللحظة ناقشنا العديد من الخيارات المتاحة لكتابة الوحدات مثل: نمط الوحدات (Module Pattern) وطريقة CommonJS مع AMD وطريقة RequireJS فأي منهم سنعتمده لمشروعنا القادم؟ الجواب: ولا واحدة منها. كما نعلم بأن لغة جافاسكربت لم تكُ تدعم نظام الوحدات في أصل اللغة، ولهذا نلاحظ وجود العديد من الخيارات المتاحة لتعويض هذا النقص. ولكن هذا الأمر لم يستمر طويلًا إذ بعد تحديث المواصفات القياسية للغة جافاسكربت ES6 أصبح وأخيرًا نظام الوحدات جزءًا أساسيًا منها وهذا يكون جواب سؤالنا الرئيسي أي الخيارات سنستخدم؟ إنها طريقة ES6 (تحدثنا في سلسلة مقالاتٍ سابقة عن المميزات الجديدة لهذا الإصدار من لغة جافاسكربت ES6). تُستخدم التعليمة import و export لاستيراد وتصدير الوحدات في التحديث الجديد ES6. وإليك مثالًا عمليًا يوضح لك كيفية استخدامها: الملف main.js: import sum from "./sum"; var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ]; var answer = sum(values); document.getElementById("answer").innerHTML = answer; الملف sum.js: import add from './add'; import reduce from './reduce'; export default function sum(arr){ return reduce(arr, add); } الملف add.js: export default function add(a,b){ return a + b; } الملف reduce.js: export default function reduce(arr, iteratee) { let index = 0, length = arr.length, memo = arr[index]; index += 1; for(; index < length; index += 1){ memo = iteratee(memo, arr[index]); } return memo; } في الحقيقة دعم جافاسكربت لنظام الوحدات بهذا الشكل المختصر قلبَ مجريات الُلعبة، بل إن نظام الوحدات هذا سيحكم عالم جافاسكربت في نهاية المطاف. إنها باختصار مستقبل لغة جافاسكربت. ولكن! (أعلم بأنك سئمت من هذه الكلمة ولكن يجب علينا إكمال بقية جوانب هذه الطريقة لتوضيح جميع تفاصيل المشهد لديك) هذا التحديث من اللغة لم تدعمه جميع المتصفحات المستخدمة في هذا اليوم، أو لنكن أكثر دقة ما تزال بعض المتصفحات لا تدعم هذه التحديث وخصوصًا المتصفحات القديمة وفي الحقيقة نسبة المستخدمين لهذه المتصفحات كبيرة وتؤخذ بعين الإعتبار. إذا ما الّذي يجب علينا فعله الآن؟ وكيف سنحوّل الشيفرة البرمجية من الإصدار ES6 إلى الإصدار ES5؟ هنا يأتي دور أهم أداة سنتعرف عليها اليوم ألا وهي مجمع الوحدات Webpack. ما هي Webpack؟ هي عبارة عن مجمع واحدات (Module Bundler) أو أداة بناء (Build Tool) مثل أداة Browserify وهي تحول شجرة تبعية الملفات وتُجمعها في ملف واحد. ليس ذلك فحسب وإنما تحوي على الكثير من المميزات الأخرى نذكر منها: تقسيم الشيفرة البرمجية: عندما يكون لدينا تطبيقات متعددة تتشارك نفس الوحدات يمكننا من خلال هذه الأداة تجميع الشيفرة البرمجية في ملفين أو أكثر. فمثلًا إذا كان لدينا التطبيقين التاليين app1 و app2 ويشترك كلاهما في العديد من الوحدات إذا استخدمنا Browserify سيكون لدينا app1.js و app2.js وكل ملف منهم مجمع فيه الشيفرات البرمجية الخاصة بكل تطبيق، بينما مع أداة Webpack يمكننا إنشاء ملف app1.js وملف app2.js وبالإضافة إلى الملف المشترك shared-lib.js. نعم ستضطر إلى تحميل ملفين في صفحة Html ولكن مع إمكانية استخدام تقنيات مساعدة مثل: التخزين في الذاكرة المؤقتة للمتصفح (Cache) واستخدام شبكة توصيل المحتوى الموزعة CDN (Content Delivery Network) كلّ هذه التقنيات ستقلل من وقت التحميل الإبتدائي للصفحات. المُحملات (Loaders): مع استخدام المُحمل المخصص يمكنك تحميل أي ملف إلى التطبيق إذ يمكنك استخدام الدالة reuiqre ليس فقط لتحميل ملفات جافاسكربت فقط (مثلما كانت أداة Browserify) وإنما ملفات التنسيقات وملفات SaSS وملفات Less بالإضافة إلى إمكانية تحميل الصور والخطوط والملفات والكثير غيرها. الملحقات (Plugins): تقدم Webpack ميزة الملحقات مجموعة واسعة من المهام مثل تحسين الحزمة وإدارة الملحقات (مثل الصور) بالإضافة إلى ذلك يمكننا حتى التلاعب بالملفات قبل تحميلها وتحزيمها في الملف الهدف (على سبيل المثال إمكانية ضغط الصور قبل رفعها على المخدم سنتطرق لهذه الميزّة لاحقًا في هذا الدليل). نظام الأوضاع (Mode): توفر لك هذه الأداة ثلاث أنواع من الأوضاع وضع التطوير ووضع الإنتاج أو بدون وضع. باختصار يستخدم كلّ وضع من الأوضاع من أجل سيناريو معين: وضع التطوير: يستخدم عند العمل في مرحلة التطوير لمشروع الوِب وتكون الشيفرة البرمجية مقروءة ومفهومة. وضع الإنتاج: يستخدم عند العمل في مرحلة عرض مشروع الوِب على الإنترنت وتكون الشيفرة البرمجية مختصرة وغير مقروءة. بدون وضع: يكون الشيفرة الّتي استخدمت في مرحلة التطوير هي ذاتها الّتي ستعرض على الإنترنت بدون أي تعديل. تسهل عملية تحويل الشيفرات البرمجية إلى إصدار أقدم (Transpiling): إمكانية تحويل شيفرات جافاسكربت البرمجية الحديثة مثل ES6 والّتي لا تدعمها بعض المتصفحات إلى شيفرات جافاسكربت البرمجية القديمة مثل ES5 من خلال Babel على سبيل المثال، وبذلك نكون ضربنا عصفورين بحجر واحد أولهما أننا كتبنا التطبيق بالإصدار الحديث من اللغة، وثانيهما أننا استطعنا تشغيل التطبيق على كافة المتصفحات. أليس هذا رائعًا؟ لابد بأنك متحمسٌ جدًا للتعرف على هذه الأداة والبدء في استخدامها على الفور في مشاريعك القادمة إذًا هيا بنا نبدأ لنتعرف عليها أكثر ونتعلم كيفية استخدامها. مفاهيم أساسية قبل الشروع في العمل مع هذه الأداة لا بدّ لنا من التعرف على بعض المفاهيم الرئيسية والتي سنستخدمها بكثرة في الأمثلة ملف الإعداد webpack.config.js :هو عبارة عن ملف يحدد كيفية تعامل الأداة Webpack مع بعض الخصائص للمحملات أو الملحقات وبعض خصائص الأداة نفسها. وعند إنشائنا لمشروع جديد لن نجده في مجلد المشروع وذلك بسبب ميزة التعديلات الصفرية (zero configuration) الجديدة الّتي جاءت مع الإصدار الرابع أو الأحدث من هذه الأداة والتي تمكننا من استخدام الأداة بدون الحاجة لأي تعديل في ملف الإعداد. بل يمكننا عدم إنشاؤه من الأساس. يمكن تخصيص هذا الملف تخصيصًا بسيطًا أو متقدمًا وذلك بحسب المشروع الّذي تعكفُ على تطويره، وهنالك بعض الخصائص الأساسية المشتركة في كلّ المشاريع، والّتي يجب التنويه إليها: الخاصية Entry: تحدد هذه الخاصية ملف الأولي من الشروع الّذي سنبنيه (نجمعه) في أول التنفيذ. الخاصية Output: تحدد مجلد المشروع الّذي سنستخدمه في مرحلة الإنتاج (أو النشر) يكون فيه الشيفرة البرمجية الّتي نريد استخدامها لنشر المشروع على الإنترنت. تحتوي هذه الخاصية على بعض الخيارات نذكر منها: filename: نحدد فيها أسم الملف الّذي نريد تجميع الحزم فيه. path: نحدد فيها مسار الملف الّذي نريد تجميع الحزم فيه. الخاصية Loaders: تحدد المحملات الّتي نريد أن نستخدمها مع Webpack. كلّ مُحمل يحتوي على بعض الخيارات نذكر منها: test: تحدد نوع الملفات الّتي سيُحملها هذا المُحمل (يمكننا استخدام التعابير النمطية في تحديد الملفات). exclude: الملفات الّتي نريد استبعادها (والتي من الممكن أن تحقق الخاصية الأولى). use: ما هو المُحمل الّذي سنستخدمه لإجراء التغييرات على الملفات المقبولة في test ولم تُستبعد في الخاصية exclude. الخاصية Plugins: تحدد هذه الخاصية الملحقات الّتي نريد استخدامها وكما تحدد بعض الخيارات لهذه الملحقات. الخاصية Mode: تحدد هذه الخاصية الوضع الّذي نريد العمل عليه أي طريقة نشر المشروع إلى مجلد الخرج (output). كانت هذه أبرز المفاهيم الأساسية الّتي سنتعامل معها في الأمثلة القادمة. إعداد بيئة التطوير بما أن مجمعات الحزم (الوحدات) تعتمد على Node.js اعتمادً أساسيًا للوصول إلى نظام الملفات من أجل تجميع الاعتماديات بشكلها النهائي لذا لابد لنا من تثبيت Node.js في البداية وإليك الخطوات الكاملة لتثبيته على نظام التشغيل ويندوز (يمكنك الاطلاع على مقال سابق تحدثنا فيه عن كيفية تثبيت Node.js على نظام لينكس): ندخل إلى الموقع الرسمي الخاص ببيئة Node.js ونحمل النسخة الموافقة لنظام التشغيل الخاص بك (يفضل تحميل الإصدارات ذات الدعم الطويل والتي تُدعى LTS). بمجرد إنتهاء التنزيل نفتح الملف المُنزّل. سيسألك معالج التثبيت عن موقع التثبيت والمكونات الّتي تريد تضمينها في عملية التثبيت يمكنك ترك تلك الخيارات بوضعها الافتراضي. انقر فوق تثبيت وانتظر حتى ينتهي معالج التثبيت. تهانينا أصبحت بيئة Node.js جاهزة للعمل. ملاحظة: يمكنك التأكد من تثبيت البيئة تثبيتًا صحيحًا من خلال تنفيذ الأمر التالي على سطر الأوامر (Command line) node –v يجب أن يظهر لك نسخة Node.js المثبتة وفي حالتنا ستظهر 12.16.1. كما يمكنك التأكد من تثبيت مدير الحزم Node Package Manager والّذي يُعرف اختصارًا npm من خلال الأمر npm –v يجب أن تظهر لك نسخة npm المثبتة وفي حالتنا ستظهر 6.13.4. نحن جاهزون الآن لإنشاء مشروع جديد وتثبيت Webpack! إنشاء مشروع جديد وتثبيت Webpack في البداية ننشئ مجلدًا جديدًا للمشروع وندخل إليه عبر التعليمات التالية: $ mkdir project; cd project ثم ننشئ مشروعًا جديدًا عبر التعليمة التالية: $ npm init -y ثم نثبت أداة Webpack عبر التعليمة التالية: $ npm install -D webpack webpack-dev-server يجب أن يظهر لك نتيجة مماثلة للصورة التالية: الآن لننشئ بنية الملفات الهرمية التالية: webpack-demo |- package.json + |- index.html + |- /src + |- index.js ملاحظة: جرى عرض الملفات في هذه المقالة بأسلوب عرض التغييرات في git فالأسطر الّتي بجانبها إشارة موجبة + يجب عليك إضافتها والّتي بجانبها إشارة سالبة - يجب عليك حذفها. الآن نفتح ملف index.js الموجود في مجلد src ونجري عليه التعديلات التالية: unction component() { const element = document.createElement('div'); // Lodash, currently included via a script, is required for this line to work element.innerHTML = _.join(['Hello', 'webpack'], ' '); return element; } document.body.appendChild(component()); ونعدل الملف index.html الموجود في مجلد webpack-demo ونجري عليه التعديلات التالية: <!doctype html> <html> <head> <title>Getting Started</title> <script src="https://unpkg.com/lodash@4.16.6"></script> </head> <body> <script src="./src/index.js"></script> </body> </html> كما يجب علينا أن نعدل ملف package.json لتفعيل الوضع الخاص كي نمنع النشر العرضي للشيفرة البرمجية الخاصة بنا ليصبح على الشكل التالي: { "name": "webpack-demo", "version": "1.0.0", "description": "", + "private": true, - "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^4.20.2", "webpack-cli": "^3.1.2" }, "dependencies": {} } لمزيد من المعلومات حول التفاصيل الداخلية لملف package.json ننصحك بأخذ جولة في التوثيق الرسمي الخاص به. في المثال السابق هنالك تبعيات ضمنية إذ أن الملف index.js يعتمد على loadash (وهي مكتبة تساعد المبرمجين على كتابة شيفرة برمجية أكثر إيجازًا وتزيد من قابلية الصيانة) في عمله وبناءً عليه يجب أن يكون loadash مُضمن قبل أن تعمل الشيفرة البرمجية للملف index.js. ولكن إن الملف index.js لم يُصرح بوضوح عن حاجته لهذه المكتبة وإنما افترض أنها موجودة ضمن المتحولات العامة. في الحقيقة إن إدارة المشروع بهذه الطريقة تعدّ مشكلة وذلك للأسباب التالية: ليس من الواضح أن الشيفرة البرمجية للملف index.js تعتمد على مكتبة خارجية. إذا كانت التبعية مفقودة، أو ضُمنت في الترتيب الخطأ، فلن يعمل التطبيق بالطريقة الصحيحة. إذا ضُمنت تبعيةً ما ولكن لم تُستخدم، سيضطر المتصفح إلى تنزيلها مع أنها غير ضرورية. لنستخدم Webpack لإدارة هذه المشروع بدلًا من الطريقة الحالية. إنشاء حزمة باستخدام Webpack سنعدل بنية المشروع قليلًا لفصل الشيفرة البرمجية الّتي سنعمل عليها عن الشيفرة البرمجية الّتي سننشرها. وتجدر الإشارة إلى أن الشيفرة البرمجية الّتي سننشرها هي الناتج المصغّر والمحسّن لعملية البناء الناتجة عن الشيفرة الّتي كتبناها والتي ستُحمّل في نهاية المطاف في المتصفح. إذًا سنضع الشيفرة البرمجية الّتي سنعمل عليها في المجلد src أما الّتي سننشرها ستكون في مجلد dist أي ستكون بهذا الشكل: webpack-demo |- package.json + |- /dist + |- index.html - |- index.html |- /src |- index.js لتحزيم مكتبة loadash (والّتي هي تبعية في ملف index.html) باستخدام الملف index.js سنحتاج أولًا إلى تثبيت هذه المكتبة محليًا باستخدام الأمر التالي: npm install --save lodash عند تثبيت حزمة (مكتبة) معينة والّتي سنحتاجها في عملية التحزيم النهائية قبل النشر النهائي يجب علينا إضافة install --save أما إذا أردت تثبيت حزمة (مكتبة) معينة لمرحلة التطوير فقط يجب عليك إضافة install --save-dev. لمزيد من المعلومات ننصحك بالإطلاع على التوثيق الرسمي لمدير الحزم NPM. الآن لنضيف تعليمة استيراد الحزمة في الشيفرة البرمجية للملف index.js الموجود في المجلد src كما في الشكل التالي: + import _ from 'lodash'; + function component() { const element = document.createElement('div'); - // Lodash, currently included via a script, is required for this line to work element.innerHTML = _.join(['Hello', 'webpack'], ' '); return element; } document.body.appendChild(component()); بما أننا قمنا باستيراد مكتبة loadash في الملف السابق لنعدل الملف index.html بما يتوافق مع ذلك ليكون كما في الشكل التالي: <!doctype html> <html> <head> <title>Getting Started</title> - <script src="https://unpkg.com/lodash@4.16.6"></script> </head> <body> - <script src="./src/index.js"></script> + <script src="main.js"></script> </body> </html> ملاحظة: يتطلب ملف index.js صراحة وجود المكتبة (أو أي تبعية إذا أردنا تعميم الفكرة) loadash ويربطه كمتحول _ (أي لا يوجد تضارب في المجال). من خلال تحديد التبعيات الّتي تحتاجها الوحدة، يمكن لمُجمع الوحدات (المقصود Webpack) استخدام هذه المعلومات لإنشاء مخطط بياني للاعتماديات (dependency graph). ثم يستخدم الرسم البياني لإنشاء حزمة محسنة والّتي ستُنفذ الاستدعاءات بالترتيب الصحيح. لننفذ التعليمة التالية npx webpack والتي ستأخذ الشيفرة البرمجية في الملف index.js وهو القيمة المسندة للخاصية entry وتنشر الخرج في ملف main.js والذي سيكون القيمة في المسندة للخاصية output. إن تعليمة npx والتي تأتي مع نسخة 8.2 من Node.js أو أحدث، و 5.2.0 من مدير الحزم NPM أو أحدث. إذ تعمل على أداة Webpack الثنائية الموجودة في (./node_modules/.bin/webpack) الّتي ثبتناها في البداية. ويكون ناتج التنفيذ كما يلي: npx webpack ... Built at: 13/06/2018 11:52:07 Asset Size Chunks Chunk Names main.js 70.4 KiB 0 [emitted] main ... WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/ ملاحظة: من الممكن أن يختلف الناتج الّذي سيظهر لك بعض الشيء ولكن إذا نجحت عملية البناء فلا تقلق من رسالة التحذير فإن الأمور على ما يرام. افتح الملف index.html يجب أن يظهر لك Hello webpack فإذا ظهرت لديك تهانينا هذا يدلّ على أنك أنجزت جميع الخطوات بنجاح. إذا ظهرت لديك رسالة خطأ في صياغة ملف جافاسكربت المصغّرة (minified) وذلك عند فتحك لملف index.html فعيّن وضع التطوير ومن ثم شغل الأمر npx webpack من جديد. هذا الخطأ يتعلق بتشغيل حزمة الوٍب npx على أحدث نسخة من Node.js (الإصدار 12.5 أو الأحدث) بدلًا من الإصدار ذو الدعم الطويل LTS. الوحدات في المواصفات القياسية لنسخة جافاسكربت ES6 دُعمت تعليمتي import وexport إذ أن معظم المتصفحات تدعمها في الوقت الحالي (ولكن يوجد بعض المتصفحات لا تدعمها) وWebpack ليست استثناءً بل إنها تدعمها دعمًا متميزًا. تدعم Webpack هذه الخاصية من خلال تحويل (transpiles) الشيفرة البرمجية المقابلة للتعليمتين import وexport إلى نسخة أقدم من ES6 مثل ES5 وبذلك تؤمن فهم المتصفح ما تعنيه هذه التعليمتين باللغة الّتي يفهمها. إضافةً إلى ذلك يدعم Webpack العديد من صيغ الوحدات الأخرى لمزيد من المعلومات يمكنك زيارة التوثيق الرسمي. ملاحظة: إن Webpack لن يحوّل أي تعليمة برمجية عدا تعليمتي import وexport ولذلك في حال كنت تستخدم مميزات أخرى من المواصفات القياسية ES6 فعندها يجب عليك استخدام محولات المخصصة لذلك مثل Babel أو Bublé. استخدام ملف الإعداد إن جميع الإصدارات الّتي جاءت بعد الإصدار الرابع من Webpack تدعم ميزة التعديلات الصفرية الّتي تحدثنا عنها سابقًا في هذا الدليل، ولذا إذا أردنا أن نخصص بعض الإعدادات في المشروع لا بد لنا من إنشاء ملف الإعداد إنشاءً يدويًا. إذًا سنضيف ملف الإعداد على بنية الملفات وستصبح البنية الهرمية للمشروع على الشكل التالي: webpack-demo |- package.json + |- webpack.config.js |- /dist |- index.html |- /src |- index.js وسنُضيف إليه بعض التعليمات المهمة والتي سنستخدمها كثير في المشروع وبذلك ستتحسن إنتاجيتنا أكثر من ذي قبل. وتكون الإضافة كما يلي: const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist'), }, }; أما الآن لنجرب تنفيذ التعليمة التالية: npx webpack --config webpack.config.js ... Asset Size Chunks Chunk Names main.js 70.4 KiB 0 [emitted] main ... WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/ ملاحظة: في حالة وجود أكثر من ملف للإعداد تلتقط التعليمة Webpack ذلك إلتقاطًا إفتراضيًا. ونستخدم خيار --config وبعده اسم الملف لتوضيح فكرة أنه بإمكانك تمرير أسم أي ملف تريده تمريرًا يدويًا، وهذا سيكون مفيدًا جدًا لملفات الإعداد الأكثر تعقيدًا والتي سنحتاج لتقسيمها إلى ملفات متعددة. يتيح ملف التكوين مرونة أكبر بكثير من استخدام سطر الأوامر CLI (Command Line Interface) البسيط. إذ يمكننا تحديد وتثبيت قواعد معينة للمُحمل (Loader) والملحقات (Plugin) وتخصيص الخيارات بالإضافة للعديد من التحسينات الأخرى. لمزيد من المعلومات يمكنك الإطلاع على التوثيق الرسمي لملف الإعداد. إنشاء إختصار لتعليمة البناء يمكننا استخدام مميزات Webpack لجعل الأمور أسهل من خلال وضع بعض اللمسات الجمالية مثل اختصار بعض الخطوات، وذلك من خلال كتابة الأوامر في ملف package.json وتحديدًا في scripts. وبذلك نختصر على أنفسنا عناء كتابة بعض التعليمات الطويلة والمُملة. إذًا لنعدل ملف package.json ليصبح على الشكل التالي: { "name": "webpack-demo", "version": "1.0.0", "description": "", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "build": "webpack" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^4.20.2", "webpack-cli": "^3.1.2" }, "dependencies": { "lodash": "^4.17.5" } } يمكننا الآن استخدام الأمر npm run build بدلًا من الأمر npx الّذي استخدمنا سابقًا. لاحظ بأنه يمكننا دائمًا الإشارة إلى الحزم المثبتة محليًا في مدير الحزم npm في السمة scripts في الملف package.json بنفس الطريقة الّتي استخدمناها مع التعليمة npx. هذا الطريقة هي متعارف عليها في معظم المشاريع القائمة على مدير الحزم npm لأنه يسمح لجميع المساهمين لاستخدام نفس التعليمات المشتركة (وحتى مع بعض الخيارات مثل --config). ملاحظة: يمكنك تمرير الخيارات الخاصة لتعليمة ما من خلال إضافة شرطتين -- بين الأمر npm run build والخيارات الّتي نريد تخصيصها مثل npm run build -- --colors. npm run build ... Asset Size Chunks Chunk Names main.js 70.4 KiB 0 [emitted] main ... WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/. أما الآن وبعد أن تعلمنا الأساسيات عن كيفية استخدام Webpack لننتقل إلى الاستخدامات الأهم لهذه الأداة مثل إدارة الصور والخطوط ..إلخ. إذا نفذت جميع التعليمات السابقة تنفيذًا صحيحًا يجب أن يكون البنية الهرمية للمشروع على الشكل التالي: webpack-demo |- package.json |- webpack.config.js |- /dist |- main.js |- index.html |- /src |- index.js |- /node_modules ملاحظة: إذا كنت تستخدم الإصدار الخامس من مدير الحزم npm من الممكن أن ترى ملفًا إضافيًا اسمه package-lock.json. استخدام Webpack لإدارة الملحقات بعد أن تعلمنا أساسيات Webpack وإنشأنا مشروعًا صغيرًا يعرض "Hello Webpack"، سنحاول الآن أن نركز أكثر على الأمور المهمة والتي تخدمنا بها هذه الأداة مثل إدارة ملفات التنسيق والصور وسنرى بالضبط كيف تتعامل هذه الأداة مع كلٍّ منهم. يستخدم مطورو الواجهات الأمامية أدوات بناء مثل: Grunt و Glup لمعالجة هذه الملحقات ونقلها من مجلد src إلى مجلد dist أو حتى لإنشاء مجلد جديد لهذه الملحقات، تستخدم Webpack نفس الفكرة للتعامل مع الوحدات، بالإضافة إلى أن Webpack تجمع كلّ التبعيات ديناميكيًا وتنشئ ما يعرف بمخطط التبعيات وهذا أمر رائع لأن كلّ وحدة (Module) توضح صراحة ما هي تبعياتها وبذلك نتجنب تجميع الوحدات غير المستخدمة في المشروع. واحدة من أروع مميزات Webpack هي إعطاؤك إمكانية تضمين أي نوع آخر من الملفات، إلى جانب جافاسكربت، ولهذه الفكرة تحديدًا أنشئ المحمل (Loader). أي يمكن تطبيق نفس المزايا المذكورة أعلاه (التبعيات المستخدمة من قبل الوحدات) على كلّ شيء مستخدم في إنشاء المواقع أو تطبيقات الوِب أيضًا. إدارة ملفات التنسيق CSS في البداية سنعدل قليلًا في بنية الملفات وبعض الملفات قبل أن ندير ملفات التنسيق. سنعدل الملف dist/index.html ليصبح كما يلي: <!doctype html> <html> <head> - <title>Getting Started</title> + <title>Asset Management</title> </head> <body> - <script src="main.js"></script> + <script src="bundle.js"></script> </body> </html> وكما سنعدل أيضًا ملف الإعداد webpack.config.js ليصبح: const path = require('path'); module.exports = { entry: './src/index.js', output: { - filename: 'main.js', + filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), }, }; وبذلك يُصبح كلّ شيء جاهز لنبدأ. أولًا لنثبت محمل ملفات التنسيق css-loader الّذي سيساعدنا في معالجة تعليمة الاستيراد import الّتي سنستخدمها أيضًا من أجل استيراد ملفات التنسيق ومحمل التنسيق style-loader الّذي سيساعدنا في أخذ ملف التنسيق ووضعه في ملف (أو ملفات) تنسيق منفصلة. إذًا لنُنفذ الأمر التالي: npm install --save-dev style-loader css-loader ولنُعدل ملف الإعداد webpack.config.js ليحوي المعلومات اللازمة لحزم المُحمل (Loader) الّتي ثبتناها للتو وسيصبح الملف على الشكل التالي: const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), }, + module: { + rules: [ + { + test: /\.css$/, + use: [ + 'style-loader', + 'css-loader', + ], + }, + ], + }, }; ملاحظة: تستخدم Webpack التعابير النمطية (Regular Expression) لتحديد الملفات الّتي يجب البحث عنها وتقديمها إلى مُحمل (Loader) محدد. في الحالة السابقة نلاحظ أنه ستُقدم جميع ملفات التنسيق (ذات اللاحقة css) إلى المُحمل style-loader والمُحمل css-loader. وهذا بدوره سيمكنك من استيراد ملف التنسيق الّذي سيكون مثل هذا import './style.css' إلى الملف الّذي يعتمد على ملف التنسيق هذا. عند تشغيل مجمع الحزم Webpack سيُضاف ملف تنسيق مخصص في وسم <style> وذلك في داخل الوسم <head>. لنجرب ذلك من خلال إضافة ملف تنسيق جديد style.css إلى مشروعنا واستيراده في الملف index.js: لننشئ أولًا ملف التنسيق style.css في مشروعنا لتصبح البنية الهرمية للمشروع على الشكل التالي: webpack-demo |- package.json |- webpack.config.js |- /dist |- bundle.js |- index.html |- /src + |- style.css |- index.js |- /node_modules ومن ثم لنُضف بعض الخصائص إلى ملف style.css ليصبح على الشكل التالي: .hello { color: red; } ولنستدعي ملف التنسيق style.css في ملف index.js ليصبح على الشكل التالي: import _ from 'lodash'; + import './style.css'; function component() { const element = document.createElement('div'); // Lodash, now imported by this script element.innerHTML = _.join(['Hello', 'webpack'], ' '); + element.classList.add('hello'); return element; } document.body.appendChild(component()); ومن ثم نُنفذ أمر البناء التالي: npm run build ... Asset Size Chunks Chunk Names bundle.js 76.4 KiB 0 [emitted] main Entrypoint main = bundle.js لنفتح الآن ملف index.html ولنرى ما هي التغييرات الّتي جرت عليه. لاحظ أن كلمة "Hello Webpack" أصبحت الآن باللون الأحمر. لمعرفة ما الّذي فعلته Webpack افحص الصفحة من خلال أدوات المطوّر (المُقدمة من متصفح جوجل كروم على سبيل المثال)، ولكن لا تعرض مصدر الصفحة لأنها لن تقدم لك النتيجة الّتي نود الإشارة إليها. إذ أن الوسم <style> سيُنشئ ديناميكيًا من قِبل جافاسكربت. لذلك افحصها حصرًا من خلال أدوات المطوّر وافحص تحديدًا الوسم <head> يجب أن يحتوي على الوسم <style> والذي استوردناه في ملف index.js. ملاحظة: سابقًا كان علينا تصغير (Minimize) ملفات التنسيق تصغيرًا يدويًا قدر الإمكان وذلك بحذف المساحات الفارغة بين الخصائص الموجودة في الملف من أجل زيادة سرعة تحميل الصفحة، ولكن مع الإصدار الرابع من مجمع الحزم 4 Webpack أو الأحدث منه أصبح تصغير ملفات التنسيق خطوة افتراضية في الأداة، وبذلك أضافت لنا هذه الأداة بُعدًا آخر لتحسين الشيفرة البرمجية وسبب وجيّه لزيادة محبتنا لها. إدارة ملفات SASS تتيح لنا Webpack إمكانية التعامل مع ملفات SASS وتحويلها إلى ملفات تنسيق عادية. ولنأخذ مثالًا عمليًا نطبق فيه كيفية تحويل ملفات SASS إلى ملفات تنسيق عادية. سنركز في هذا المثال على دعم الملفات SCSS مثل: (your-styles.scss) و الملفات SCSS modules مثل: (your-component.module.scss). تجنبًا لتعقيد الأمور أكثر من اللازم سنبدأ بمشروع جديد ننفذ فيه هذه الفكرة. سنفترض أنك أنشأت مشروعًا جديدًا وثبتّ أداة Webpack، لننتقل الآن لتثبيت الإضافات والمُحملات اللازمة لهذا المشروع. سنبدأ أولًا بتثبيت الحزم التالية: npm install --save-dev node-sass sass-loader style-loader css-loader mini-css-extract-plugin ستكون مهمة كلّ حزمة من الحزم على الشكل التالي: node-sass: ستوفر هذه الحزمة ربط Node.js مع LibSass وهذا الأخير هو مترجم SASS. sass-loader: هو مُحمّل ملفات SASS إلى مشروعنا. css-loader: تستخدم هذه الحزمة لتفسير التعليمة @import و @url() بما تشير إليه في ملفات التنسيق. style-loader: تستخدم هذه الحزمة لإسناد الخصائص الموجودة في ملفات التنسيق إلى الوسوم الفعلية في شجرة DOM الافتراضية. mini-css-extract-plugin: هذه الحزمة تستخرج الخصائص الموجودة في ملفات التنسيق إلى ملفات منفصلة. إذ أنها تنشئ لكل ملف جافاسكربت ملف تنسيق الخاص به وهو لدعم ميّزة التحميل عند الطلب (On-Demand-Loading) لملفات التنسيق و خرائط الشيفرة البرمجية المحوّلة Source Maps (لمزيد من المعلومات عنها يمكنك الإطلاع على المقال التالي). سنحتاج إلى إضافة مُحملين، أحدهما للتنسيقات العامة والأخرى للتنسيقات المركبة. واللّذان يشار إليهما بملفات (SCSS modules). إذ أن هذه الأخيرة تعمل جيدًا مع المكتبات أو أطر العمل المُعتمدة على المكونات (component-based) مثل: React. سنضيف هذه الحزمة الملحقة إلى ملف الإعداد webpack.config.js على الشكل التالي: + const MiniCssExtractPlugin = require('mini-css-extract-plugin') module.exports = { plugins: [ + new MiniCssExtractPlugin({ + filename: isDevelopment ? '[name].css' : '[name].[hash].css', + chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css' + }) ] } نلاحظ أننا أضفنا خاصية أسماء الملفات المجزأة من أجل زيادة فعالية وسهولة خرق ذاكرة التخزين المؤقّت (Cache Bustring وهي تحِلُّ مشكلة التخزين المؤقت في المتصفح باستخدام إصدار معرف فريد للملف يميزه وذلك من أجل أخباره في حال وجود نسخة جديدة من الملف الذي حفظه فعلًا في هذه الذاكرة، وذلك من أجل أن يحملها تلقائيًا إلى ذاكرة التخزين المؤقت بدلًا من نسخة الملف القديمة الموجودة فيه)، والآن سنضيف الخواص المناسبة لملف الإعداد webpack.config.js من أجل عملية التحويل المناسبة للملفات: module.exports = { module: { rules: [ + { + test: /\.module\.s(a|c)ss$/, + loader: [ + isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { + modules: true, + sourceMap: isDevelopment + } + }, + { + loader: 'sass-loader', + options: { + sourceMap: isDevelopment + } + } + ] + }, + { + test: /\.s(a|c)ss$/, + exclude: /\.module.(s(a|c)ss)$/, + loader: [ + isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, + 'css-loader', + { + loader: 'sass-loader', + options: { + sourceMap: isDevelopment + } + } + ] + } ] }, resolve: { - extensions: ['.js', '.jsx'] + extensions: ['.js', '.jsx', '.scss'] } } نلاحظ أن القاعدة الأولى المطبقة في الشيفرة السابقة ستُطبق على الملفات ذات الامتدادات .module.scss أو .module.sass إذ في البداية ستُحوّلُ ملفات SASS إلى CSS من خلال sass-loader ومن ثم ستُمرّر إلى الحزمة css-loader لمعالجة التعليمات @import() و url() ..إلخ. ومن ثمّ ستُسندُ الحزمة style-loader الخصائص المناسبة في DOM أو ستسند من خلال الحزمة Mini CSS Extract Plugin وذلك لإخراج ملفات التنسيق أثناء وضع النشر. القاعدة الثانية مشابهة جدًا للقاعدة الأولى باستثناء أننا لا نحوّل أسماء الأصناف. الآن أصبح ملف الإعداد جاهز نحتاج الآن لإنشاء ملف تنسيقات SASS للتأكد من أن كل شيء يعمل مثلما خطط له. أنشئ مجلد src ثم أنشئ بداخله ملفًا جديدًا باسم app.module.scss وأضف الشيفرة البرمجية التالية: .red { color: red; } أي عنصر سيأخذ الصنف red سيكون لونه أحمر. افتح الملف app.js واستدعي الملف السابق على الشكل التالي: import styles from './app.module' نلاحظ أن اسم الملف لا يحتوي على اللاحقة .scss وذلك لأننا سبق وأخبرنا Webpack بأن يأخذ بعين الاعتبار هذه اللاحقة وذلك في ملف الإعداد. ثم لنُضيف الآن الدالة التالية في نفس الملف app.js ليصبح على الشكل التالي: function App() { return <h2 className={styles.red}>This is our React application!</h2> } أليست سهلة جدًا؟ بغض النظر عن كيفية تحويل الحزمة css-loader اسم الصنف الّذي بنيناه (red) والّذي سيصبح مثل: _1S0lDPmyPNEJpMz0dtrm3F أو شيء من هذا القبيل، ولكنها ساعدتنا في مهمتنا مساعدةً كبيرة. لنُضيف الآن بعض التنسيقات العمومية. لننشئ ملف جديد وليكن global.scss في المجلد src ولِنفتحه ونُضيف بداخله الشيفرة البرمجية التالية: body { background-color: yellow; } هذا الملف سيجعل لون خلفية الصفحة الرئيسية لتطبيق الوِب خاصتنا أصفر. ولكن يجب أن نفتح الملف index.js ونستدعي ملف التنسيق السابق في بداية الملف. مثلما هو موضح في الشيفرة التالية: import './global' واخيرًا سنحصل على الخرج التالي: نلاحظ أن استخدام وحدات SCSS يتطلب منا جهدًا لا بأس به، ولكن بالموازنة مع كمية الفائدة الّتي تقدمها وحدات SCSS من سهولة في الصيانة لتطبيق الوِب في المستقبل فأعتقد أن الأمر يستحق هذا الجهد. بالإضافة إلى ذلك توفر أداة Webpack حزم أُخرى مخصصة لأي نكهة من نكهات ملفات التنسيق مثل الحزمة: postcss لتوفير دعم Postcss أو الحزمة Less لدعم ملفات التنسيق من نوع Less أيضًا. إدارة الصور تتيح Webpack لنا إمكانية إدارة الصور مثل: الخلفيات والأيقونات وذلك من خلال مُحمل الملفات. في البداية لنثبت أولًا مُحمل الملفات من خلال الأمر التالي: npm install --save-dev file-loader ومن ثم سنضيف إلى ملف الإعداد webpack.config.js المعلومات اللازمة لعمل هذا المُحمل كما في الشكل التالي: const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ], }, + { + test: /\.(png|svg|jpg|gif)$/, + use: [ + 'file-loader', + ], + }, ], }, }; عند استيراد لصورة معينة ولتكن import MyImage from './my-image.png' ستضاف هذه الصورة إلى مجلد الخرج (output) الّذي حددناه في ملف الإعداد وسيحمل المتغير MyImage عنوان الرابط التشعبي الخاص بالصورة (URL) بعد معالجتها. عندما استخدمنا محمل ملفات التنسيق css-loader جرى نفس السيناريو السابق. أي أنه سيكون الخاصية التالية في ملفات التنسيق url('./my-image.png') والمُحمل سيلاحظ أن الملف هذا موجود محليًا وبذلك سيُحول './my-image.png' إلى المسار النهائي المُسند للخاصية (output) في ملف الإعداد webpack.config.js ومُحمل ملفات html (html-loader) سيتعامل مع <img src="./my-image.png" /> بنفس الطريقة. لنُضف الآن الصور اللازمة في مشروعنا. وهنا يمكنك استخدام أي صورة تريدها. لننسخ الصورة الّتي سنعمل عليها ولتكن icon.png إلى مشروعنا. لتصبح البنية الهرمية للمشروع على الشكل التالي: webpack-demo |- package.json |- webpack.config.js |- /dist |- bundle.js |- index.html |- /src + |- icon.png |- style.css |- index.js |- /node_modules ومن ثم لنستورد هذه الصورة في ملف src/index.js، ولنُضيفها إلى وسم <div> المتواجدة فيه، ولتصبح الشيفرة البرمجية للملف src/index.js على الشكل التالي: import _ from 'lodash'; import './style.css'; + import Icon from './icon.png'; function component() { const element = document.createElement('div'); // Lodash, now imported by this script element.innerHTML = _.join(['Hello', 'webpack'], ' '); element.classList.add('hello'); + // Add the image to our existing div. + const myIcon = new Image(); + myIcon.src = Icon; + + element.appendChild(myIcon); return element; } document.body.appendChild(component()); ومن ثم سنعدل ملف التنسيق من أجل تضمين الصور الّتي نعمل عليها ليصبح بذلك ملف التنسيق src/style.css على الشكل التالي: .hello { color: red; + background: url('./icon.png'); } ولننفذ الآن أمر البناء التالي: npm run build ... Asset Size Chunks Chunk Names da4574bb234ddc4bb47cbe1ca4b20303.png 3.01 MiB [emitted] [big] bundle.js 76.7 KiB 0 [emitted] main Entrypoint main = bundle.js ... إذا نفذت جميع الخطوات السابقة تنفيذًا صحيحًا يجب أن ترى الأيقونة (الصورة) مكررة في الخلفية، بالإضافة إلى وسم <img> بجوار النص "Hello webpack". وإذا فحصت الصورة ستجد أن اسمها الفعلي تغيّر إلى شيء شبيه بهذا الاسم 5c999da72346a995e7e2718865d019c8.png هذا يعني أن Webpack عثرت على الملف في مجلد src وعالجته. الخطوة المنطقية التالية الّتي سننفذها هي تصغير حجم الصور الّتي سنستخدمها في المشروع وتحسينها وذلك من خلال الحزم المساعدة المتوفرة مع الأداة Webpack مثل الحزمة Imagemin. ضغط الصور باستخدام Imagemin إن طريقة ضغط الصور الّتي سنستخدمها في المشروع هي باستخدام حزمة Imagemin إذ تعدّ هذه الحزمة خيارًا ممتازًا وذلك لأنها تدعم مجموعة متنوعة من أنواع الصور وسهلة التكامل مع الشيفرة البرمجية لأدوات البناء أو مجمع الحزم (الوحدات). سنعمل على مشروع جديد تجنبًا للمشاكل التي من الممكن أن تظهر لنا في حال أكملنا العمل على المشروع السابق. سنفرض أنك أنشأت مشروعًا جديدًا وثبّت الأداة Webpack تثبيتًا صحيحًا. في البداية لنثبت هذه الحزم عبر التعليمة التالية: npm install imagemin-webpack-plugin copy-webpack-plugin --save-dev ملاحظة: أن الحزمة copy-webpack-plugin ستساعدنا على نسخ الصور من مجلد images/ إلى مجلد dist/ وهو مجلد النشر وهو اختصار لكلمة distribution. لننشئ ملف الإعداد webpack.config.js ولنعدله ليتناسب مع الحزم الجديدة ليصبح على الشكل التالي: const ImageminPlugin = require('imagemin-webpack-plugin').default; const CopyWebpackPlugin = require('copy-webpack-plugin'); const path = require('path'); module.exports = { entry: './index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, plugins: [ new CopyWebpackPlugin([{ from: 'img/**/**', to: path.resolve(__dirname, 'dist') }]), new ImageminPlugin() ] } تعدّ هذه الطريقة من أسهل الطرق لإعداد لهذه الحزمة. ولنضغط الصور الّتي في المجلد images/ وننسخها إلى المجلد dist/ من خلال تنفيذ الأمر التالي: webpack --config webpack.config.js --mode development نلاحظ من التعليمة السابقة أن وضع التنفيذ الحالي هو وضع التطوير، ولكن ما الّذي سيحدث إذا نفذنا التعليمة في وضع النشر؟ لنرى ما الّذي سيحدث. webpack --config webpack.config.js --mode production ستُظهر لنا في هذه المرة أداة Webpack تحذيرًا يخبرك بأن الصور ذات النوعية PNG الخاصة بك لا تزال تتجاوز الحد الأقصى للحجم الموصى به، على الرغم من بعض الضغط. ولذلك سنحتاج إلى ضبط اليدوي لعملية الضغط لهذا النوع من الصور لتصبح أفضل مما قبل. لنعدل قيمة الضغط للصور ذات النوعية PNG لتصبح 50% وسيكون ملف الإعداد على الشكل التالي: const ImageminPlugin = require('imagemin-webpack-plugin').default; const CopyWebpackPlugin = require('copy-webpack-plugin'); const path = require('path'); module.exports = { entry: './index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, plugins: [ new CopyWebpackPlugin([{ from: 'img/**/**', to: path.resolve(__dirname, 'dist') }]), new ImageminPlugin({ pngquant: ({quality: [0.5, 0.5]}), }) ] } نلاحظ أننا مررنا للغرض Pngquant مجالًا لقيمة جودة الصور من 0.5 إلى 0.5 أي من الحدّ الأدنى إلى الحدّ الأعلى لجودة الصورة. إذ أن الحدّ الأعلى الأفتراضي 1 والحدّ الأدنى الافتراضي 0. ولكن ماذا لو أردنا أن نضبط الجودة (نسبة الضغط) المستخدمة لبقية الأنواع من الصور بنفس الطريقة السابقة؟ في الحقيقة يوجد بعض الملحقات الإضافية المساعدة لهذا الغرض تحديدًا والّتي سنستعرضها في هذا الجدول: نوع الصور ضغط مع خسارة بعض معلومات الصورة ضغط بدون خسارة بعض معلومات الصورة JPEG imagemin-mozjpeg imagemin-jpegtran PNG imagemin-pngquant imagemin-optipng GIF imagemin-giflossy imagemin-gifsicle SVG Imagemin-svgo WebP imagemin-webp 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; } نلاحظ أن لدينا نوعين من طرق ضغط الصور وهما كالتالي: ضغط مع خسارة بعض معلومات الصورة (Lossy): يؤدي استخدام هذا النوع إلى تصغير حجم الصورة تصغيرًا ملحوظًا، ولكن مع فقدان بعض معلومات الصورة. ضغط بدون خسارة بعض معلومات الصورة (Lossless): يؤدي استخدام هذا النوع إلى تصغير حجم الصورة تصغيرًا أقل من الطريقة السابقة، ولكن بدون فقدان بعض معلومات الصورة. إن طريقة الضغط الإفتراضية للحزمة imagemin للصور ذات النوعية JPEG هي الطريقة imagemin-jpegtran، سنستعرض كيفية ضغط الصور باستخدام الحزمة الأخرى وهي imagemin-mozjpeg. في البداية يجب علينا تثبيت هذه الحزمة من خلال الأمر التالي: npm install imagemin-mozjpeg ولنُعدل ملف الإعداد webpack.config.js لضبط طريقة ضغط الصور وفق ما نريده. ليصبح الملف على الشكل التالي: const imageminMozjpeg = require('imagemin-mozjpeg'); const ImageminPlugin = require('imagemin-webpack-plugin').default; const CopyWebpackPlugin = require('copy-webpack-plugin'); const path = require('path'); module.exports = { entry: './index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, plugins: [ new CopyWebpackPlugin([{ from: 'img/**/**', to: path.resolve(__dirname, 'dist') }]), new ImageminPlugin({ pngquant: ({quality: [0.5, 0.5]}), plugins: [imageminMozjpeg({quality: 50})] }) ] } نلاحظ أن نسبة الضغط في هذه الحزمة من 100 إذ مررنا القيمة 50 وبذلك نخبره بأننا نريد ضغط الصور من النوع JPEG بنسبة 50%. ولنُنفذ الآن التعليمة التالية لنرى النتيجة: webpack --config webpack.config.js --mode production تهانينا في حال نفذّت جميع الخطوات تنفيذًا صحيحًا يجب أن تكون الصور مضغوطة وفقَ المطلوب. إن الأداة Webpack تحذرنا من الصور ذات الحجم الكبير، ولكنها لا تستطيع إخبارنا إن كانت الصور مضغوطة أم لا، ولهذا السبب سنستخدم الأداة Lighthouse للتحقق من التغييرات المُنفّذة. تسمح لنا الأداة Lighthouse من التحقق من ترميز الصور بكفاءة وإن كانت الصور الموجودة في صفحتك مضغوطة على نحوٍ أمثلي أم لا (لمزيد من المعلومات حول هذه الأدة ننصحك بالاطلاع على هذا المقال المفصّل). وستكون النتيجة مشابهة للصورة التالية: بنفس الطريقة يمكنك استخدام بقية الحزم لتخصيص قيمة الضغط المطلوب للأنواع الأخرى من الصور المستخدمة في مشروعك. إدارة الخطوط يمكننا إدارة الخطوط أيضًا مع Webpack من خلال مُحمل الملفات الّذي ثبتناه في إدارة الصور (سنعمل على نفس المشروع الّذي بنيناه في فقرة إدارة الصور)، والذي سيأخذ أي ملف تضعه له في ملف الإعداد ويضعه في المسار النهائي المُسند للخاصية (output) في ملف الإعداد webpack.config.js. أي أن مُحمل الملفات قادر على التعامل الخطوط أيضًا. لنعدل الآن ملف الإعداد webpack.config.js ليصبح على الشكل التالي: const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ], }, { test: /\.(png|svg|jpg|gif)$/, use: [ 'file-loader', ], }, + { + test: /\.(woff|woff2|eot|ttf|otf)$/, + use: [ + 'file-loader', + ], + }, ], }, }; أضف خطًا معينًا إلى مجلد المشروع لتصبح البنية الهرمية للمشروع على الشكل التالي: webpack-demo |- package.json |- webpack.config.js |- /dist |- bundle.js |- index.html |- /src + |- my-font.woff + |- my-font.woff2 |- icon.png |- style.css |- index.js |- /node_modules من خلال التعديلات الّتي أجريناها على ملف الإعداد لمُحمل الملفات يمكننا الآن جعل التعليمة الّتي تصرح بها عن المسارات للخطوط @font-face إلى الشكل التالي url(...). سيعاد توجيه هذه المسارات إلى المسار النهائي المُسند للخاصية (output) في ملف الإعداد webpack.config.js تمامًا كما تعامل مع الصور. إذا سيصبح ملف التنسيق src/style.css على الشكل التالي: + @font-face { + font-family: 'MyFont'; + src: url('./my-font.woff2') format('woff2'), + url('./my-font.woff') format('woff'); + font-weight: 600; + font-style: normal; + } .hello { color: red; + font-family: 'MyFont'; background: url('./icon.png'); } لنرى كيف سيتعامل Webpack مع الخطوط ولننفذ أمر البناء على الشكل التالي: npm run build ... Asset Size Chunks Chunk Names 5439466351d432b73fdb518c6ae9654a.woff2 19.5 KiB [emitted] 387c65cc923ad19790469cfb5b7cb583.woff 23.4 KiB [emitted] da4574bb234ddc4bb47cbe1ca4b20303.png 3.01 MiB [emitted] [big] bundle.js 77 KiB 0 [emitted] main Entrypoint main = bundle.js ... افتح ملف index.html وانظر إلى النص "Hello webpack" كيف تغير شكل الخط ليصبح مطابق لشكل الخط الجديد. أما الآن لننتقل إلى واحدٍ من أبرز استخدامات مجمع الحزم Webpack وهو تحويل الشيفرات البرمجية الخاصة بالإصدارات الحديثة من المواصفات القياسية للغة جافاسكربت مثل ES6 إلى ES5. تحويل الشيفرة البرمجية باستخدام Babel بعد أن تعرفنا كيف ندير جميع التبعيات الموجودة في شيفرات جافاسكربت البرمجية من خلال Webpack. لا بُدّ لنا من جعل هذه الشيفرة البرمجية تعمل على جميع المتصفحات وذلك لكي نضمن أنه مهما يكن المتصفح الّذي سيستخدمه العميل (أو المستخدم) ستظهر النتيجة المطلوبة تمامًا مثل ما نريده. عملية التحويل (Transpiling): هي عملية تغيير الشيفرة البرمجية من إصدار معين إلى إصدار أقدم وذلك لضمان عمل هذه الشيفرة البرمجية على المتصفحات كلها. مقتطف من المخطط التفصيلي لدعم المتصفحات للمميزات الإصدار ES5 والإصدار ES6 من لغة جافاسكربت. سننشئ مشروعًا بسيطًا لشرح طريقة استخدام هذا المُحول: mkdir webpack-demo2 cd webpack-demo2 npm init -y npm install webpack webpack-cli --save-dev ملاحظة: لن نتطرق للتفاصيل نظرًا لأنها شُرحت في بداية هذا الدليل. ستكون البنية الهرمية للمشروع على الشكل التالي: webpack-demo2 |- package.json + |- index.html + |- /src + |- index.js في البداية سنحتاج لتثبيت ثلاث حزم للتعامل مع المحول Babel وستكون على الشكل التالي: npm install babel-core babel-loader babel-preset-env --save-dev الحزمة babel-core: تحتوي على الشيفرة البرمجية للنواة الأساسية للمُحول Babel. الحزمة babel-preset-env: تحتوي على الشيفرة البرمجية الّتي ستمكن النواة الأساسية للمحول Babel من تحويل الشيفرة البرمجية للجافاسكربت ذات الإصدار 6ES إلى الإصدار ES5. الحزمة babel-loader: أو (مُحمل المحول) وهي الّتي ستستخدمها Webpack في تحميل المحول والتعامل معه في عملية التحزيم (Bundling). أما الآن سننشئ ملف babelrc. والذي سيطلب من المحول Babel أن يستخدم الحزمة babel-preset-env أثناء عملية التحويل وسيكون الملف babelrc. على الشكل التالي: +{ "presets": ["env"] } ومن ثم سننشئ ملف الإعداد webpack.config.js ونطلب من Webpack الإستعانة بالمحمل Babel أثناء تحزيمه. وسيكون ملف الإعداد webpack.config.js على الشكل التالي: + const path = require('path'); + module.exports = { + entry: { main: './src/index.js' }, + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'main.js' + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader' + } + } + ] + } + }; سنضع الآن في ملف src/index.js شيفرة برمجية من الإصدار ES6، وليصبح على الشكل التالي: + const fn = () => "Arrow functions're Working!"; + alert(fn()); ومن ثم ننفذ الأمر التالي: npm run dev ومن ثم لنفتح ملف dist/main.js نلاحظ أن الدالة السهمية (Arrow function) تُحوّلُ إلى دالة عادية وذلك وفق الطريقة المتبعة في الإصدار ES5 من لغة جافاسكربت. وسيكون محتوى الملف dist/main.js على الشكل التالي: 'use strict'; var fn = function fn() { return "Arrow functions're Working!"; }; alert(fn()); وبذلك استطعنا استخدام مميزات الإصدار الحديث من لغة جافاسكربت ES6 وتحويله إلى إصدار أقدم ES5 لتفهمه المتصفحات القديمة. هل من المخاطرة تعلم Webpack؟ بعد أن تعرفنا سريعًا على Webpack يمكن لسائل أن يسأل ماذا لو أنني تعلمت العمل على Webpack وبعدها ظهرت أداة جديدة أفضل منها أو تغطي مجال أوسع في العمل؟ أليس ذلك هدرًا لوقتي ولجهدي؟ صراحة، الشيء الوحيد الّذي استطيع تأكيده لك هو أن عجلة التحديث في مجال الواجهات الأمامية سريع الدوران، وما إن تظهر مشكلة ما حتى يتسارع المطورون لحلها فمن الممكن أن تظهر أداة أقوى منها (وهذا لا اتوقعه أن يحدث قريبًا وخصيصًا مع الدعم الكبير الّتي تشهده هذه الأداة من الشركات العملاقة مثل فيسبوك) هذا وارد الحدوث، ولكن هذا الأمر لا يُلغي فكرة أهمية تعلمك العمل مع هذه الأداة إذ أن معظم الأدوات الّتي تظهر لحل مشكلة معينة تكون متشابهة في البنية وطريقة العمل مع بعض الإختلافات. أي إن تعلمك العمل مع هذه الأداة سيجهزك للعمل مع الأدوات الأخرى المستقبلية. ومن يعلم ربما تكون أنت صاحب فكرة الأداة الجديدة الّتي ستتفوق على Webpack! والسؤال الأخر الّذي من الممكن يخطر في أذهاننا أيضًا هل تستحق هذه الأداة الوقت المبذول لتعلمها؟ أو بتعبير أخر هل النتائج الّتي سأجنيها من استخدام هذه الأداة ستغطي على الوقت المبذول لتعلمها؟ في الحقيقة لا أبالغ أن قُلت أنه من الواجب عليك تعلمها فورًا وبدون تردد وخصيصًا إن كُنت تتخذُ من مهنة تطوير الواجهات الأمامية (أو مطور برمجيات عمومًا) مصدرًا أساسيًا للدخل في حياتك! لأنها ستعزز كثيرًا من إنتاجيتك وسرعتك وجودة العمل المُسلّم وهذا ما ستشعر به من أول شهر من استخدامك لها. في الحقيقة إن مجيئ Webpack بكل هذه المميزات وقابلية التخصيص والإمكانيات أطاح بجميع المنافسين لديها مثل Browserify، ليس ذلك فحسب بل تخطّت إمكانياتها حدود الفكرة التي أُنشأت من أجلها لتهدد أدواتٍ مساعدةٍ أخرى كأدوات البناء مثل Gulp (والتي تحدثنا عنها في مقالٍ سابق) إذ أنه يمكننا من خلال NPM Script أن نعوّض عمل Gulp ولكن هذا سيكون على حساب الصعوبة إذ أن العمل مع Webpack ليس بسهولة العمل مع Gulp. ننصحك بالانتقال بعد الانتهاء من هذا المقال إلى مقال، أيهما أفضل كأداة مساعدة Webpack أم Browserify مع Gulp؟ الذي يشرح الفروقات بين كل هذه الأدوات وحالات استخدامها وغيرها من التفاصيل المفيدة. بالإضافة إلى ذلك وفرت لنا Webpack إمكانية التكامل مع أدوات مساعدة أُخرى مثل أداة السقالة Yeoman (والتي تحدثنا عنها في مقالٍ سابق) وبذلك نحصل على فوائد كِلتا الأداتين ونختصر وقتنا اختصارًا ملحوظًا. وبذلك يمكننا القول بأن المرونة الموجودة لدى Webpack وملحقاتها الرائعة والدعم المتميز من مجتمعها ومستخدميها جعلها من أقوى الخيارات الموجودة في الساحة حاليًا. الخاتمة بدأنا هذا الدليل ونحن لا نعرف شيئًا عن Webpack، ثم حفرنا عميقًا في تاريخها لنفهم تمامًا ما هي الأسباب الّتي أدت لظهور أداةٍ قوية كهذه الأداة، ثم بدأنا بتجهز المتطلبات اللازمة لتشغيلها، وفصلّنا في جميع المفاهيم الأساسية لها، وتعلمنا كيفية استخدامها في إدارة ملفات التنسيق والصور والخطوط ، وكيفية استخدامها لضغط الصور قبل رفعها وتعلمنا طريقة استخدامها لتحويل الشيفرة البرمجية من إصدار حديث إلى إصدار أقدم منه. وأخيرًا ناقشنا أهمية تعلمها وفوائده. وختامًا إن هذا الدليل لم يغطي كافة المميزات والجوانب الموجودة في أداة Webpack وإنما فقط أردنا من هذا الدليل أن يلفت نظرك لأهمية هذه الأداة وقدراتها المتميزة في مساعدتك في العمل على المشاريع وخصوصًا الكبيرة منها. بل وجعلها يدك اليمنى في أي مشروع جديد وبالتأكيد إن كلّ هذه المعلومات لن تُشبع فضول المطوّر المحترف ولذلك يمكنك دومًا الاطلاع على التوثيق الرسمي للأداة لمزيد من المعلومات. المصادر التوثيق الرسمي لأداة Webpack. التوثيق الرسمي لمدير الحزم npm. المقال Using Imagemin with webpack لصاحبته Katie Hempenius. المقال How to configure SCSS modules for Webpack لصاحبه Jon Preece. المقال Brief history of JavaScript Modules لصاحبه SungTheCoder.1 نقطة
-
هذا المقال تمت كتابته بتعاون بين Joe Richardson و Robin Rendle ومجموعة من فريق موقع CSS-Tricks. أراد Joe إضافة مقال بخصوص BEM التي أحببناها وكان لدى كل منا أفكارًا وآراءً حولها، لذلك قررنا أن نتعاون سويًا لكتابة هذا المقال. تُعدُّ منهجية BEM (وهي اختصار لهذه الكلمات معًا Block و Element و Modifier) مصطلح تسمية شائع يستخدم من أجل الأصناف في HTML و CSS تم تطويره بواسطة فريق في Yandex؛ هدفها هو مساعدة المطورين في فهم العلاقة بين HTML و CSS في مشروع معين بشكل أفضل. فيما يلي مثال حول ما يكتبه مطور CSS في نمط BEM: /* مكون كتلة */ .btn {} /* عنصر يعتمد على كتلة */ .btn__price {} /* مُعدِّل يعدِّل تنسيق الكتلة */ .btn--orange {} .btn--big {} في شيفرة CSS السابقة، الكتلة (Block) عبارة عن عنصر جديد في أعلى مستوى من التجريد (top-level abstraction) على سبيل المثال زر: .btn{}. يجب اعتبار الجملة البرمجية هذه بمثابة أحد الأبوين، إذ يمكن وضع العناصر الأبناء (elements) في الداخل ويتم الإشارة إليها بواسطة شرطتين سفليتين يتبع اسم الكتلة أو الحاوية البرمجية تلك مثل .btn__price{}. أخيرًا، يمكن للمُعدلات (modifiers) معالجة الحاوية البرمجية (Block) بحيث يمكننا تمييز أو تصميم ذلك المكون المحدد دون إحداث تغييرات على بقية الحاويات البرمجية الأخرى يتم ذلك عن طريق إلحاق شرطتين باسم الحاوية البرمجية Block تمامًا مثل btn--orange. تبدو الشيفرة كما في المثال التالي: <a class="btn btn--big btn--orange" href="http://css-tricks.com"> <span class="btn__price">$9.99</span> <span class="btn__text">Subscribe</span> </a> لو كتب مطور آخر هذه الشيفرة ولم يكن لدينا خبرة في CSS، فيجب أن نأخذ فكرة جيدة عن أي الأصناف ومسؤوليتها وكيف تعتمد على بعضها بعضًا. يستطيع المطورون آنذاك بناء مكوناتهم الخاصة وتعديل الحاويات (الكتل) الموجودة إلى المحتوى الذي يريدونه. يحتمل بدون كتاب مثل شيفرة CSS هذه أن يتمكن المطورون من كتابة عدة مجموعات مختلفة من الأزرار ببساطة عبر تعيير صنف في الشيفرة: في البداية، قد تبدو هذه الصياغة أبطأ من بناء صنف جديد لكل نوع من أنواع الأزرار، ولكن هذا غير صحيح لعدة أسباب سنذكرها. لماذا علينا أخذ BEM في الحسبان؟ إذا كنا نريد إنشاء تصميم جديد لعنصر معين، يمكننا أولًا النظر في الحاويات البرمجية لمعرفة أي المُعدِّلات، والعناصرالفرعية موجودة بالفعل. ربما ندرك أننا لسنا بحاجة لكتابة أي تعبير CSS برمجي آخر لأن هناك معدّل موجود مسبقًا يفي بالغرض. إذا كنا نقرأ الوسوم بدلاً من تعابير CSS البرمجية، يجب أن نكون قادرين على الحصول بسرعة على فكرة حول العنصر الذي يعتمد على عنصر آخر؛ في المثال السابق، يمكننا رؤية أن العنصر .btn__price يعتمد على العنصر .btn، حتى لو لم نكن على دراية بعمل أي من تلك العناصر. يمكن للمصممين والمطورين تسمية العناصر لتسهيل الاتصال بين فريق المطورين بمعنى آخر، يوفر BEM لكل مطور في المشروع تسمية صيغة إعلانية للعناصر يمكنه مشاركتها بحيث تكون في نفس الصفحة. حدد Harry Roberts فائدة رئيسية أخرى لاستخدام صيغة برمجية مثل BEM حين كتب التالي عن تحسين ثقة المطور: "هذا هو السبب الرئيسي الذي يجعلنا ننتهي من قواعد الشفرة المتضخمة المليئة بشيفرات برمجية قديمة وغير قابلة للتعديل من CSS. نحن نفتقر إلى الثقة في أن نكون قادرين على العمل مع الأنماط الحالية وتعديلها لأننا نخشى عواقب أن تعمل CSS عالميًا، إذ هي مشهورة بحد ذاتها. تتلخص كل المشكلات تقريبًا المتعلقة بـ CSS على نطاق واسع في الثقة (أو عدم وجودها)، لذلك لا يقومون بإجراء تغييرات لأنهم لا يعرفون ما هي الآثار لهذه التغييرات على المدى البعيد." وبالمثل، يناقش Philip Walton حل لهذه المشكلة وهو إلتزام عدد كاف من المطورين بمبادئ BEM: "على الرغم من أن الشيفرة القابلة للتنبؤ بنسبة 100٪ غير متوافرة، من المهم فهم المفاضلات التي تجريها مع الطريقة المتبعة في كتابة التعابير البرمجية التي تختارها. إذا كنت تتبع طريقة BEM الصارمة، فستكون قادرًا على تحديث تعابير CSS وإضافة الشيفرات البرمجية الخاصة بك إليها في المستقبل بثقة تامة بأن هذه التغيرات لن يكون لها أي آثار جانبية." لذلك، إذا كان بإمكان المطورين العمل في مشروع بطريقة أكثر ثقة، فإنهم على يقين من اتخاذ قرارات أكثر ذكاءً حول كيفية استخدام هذه العناصر المرئية. قد لا تكون هذه الطريقة حلًا مثاليًا لجميع هذه المشاكل، لكنها بالتأكيد تمنح المطورين معيارًا لكتابة جمل برمجية أفضل وأكثر قابلية للصيانة في المستقبل. هناك جانب آخر جيد في منهجية BEM، وهو أن كل مجموعة جمل برمجية خاصة بعنصر معين توجد داخل حاوية خاصة به ولا يوجد شيء متداخل مما يجعل خصوصية CSS سلسة جدًا ومنخفضة وهذا هو المطلوب. أي أنك لن تجهد نفسك فيما يتعلق بخصوصية تعابير CSS البرمجية. دعونا نلقي نظرة على بعض المشاكل مع BEM. مشاكل مع BEM CSS لن يقوم أحد بمعاقبتك بالطبع إذا خرجت عن قواعد BEM. لا يزال بإمكانك كتابة محدد CSS كالتالي: .nav .nav__listItem .btn--orange { background-color: green; } يبدو أن المثال السابق يحتوي أجزاء من منهجية BEM، لكنه ليس كذلك فهو يحتوي على محددات متداخلة، ولا يصف المُعدِّل (modifier) بدقة ما يحصل في هذه الجمل البرمجية. إذا فعلنا ذلك، فسنكون فشلنا في تحقيق مبدأ الخصوصية وهذا مفيد جدا لمنهجية BEM. يجب ألا تَبطل كتلةٌ (مثل .nav) عمل كتلة أخرى أو معدل آخر (مثل .btn --orange) وإلا فإن هذا سيجعل من المستحيل تقريبًا قراءة ملف HTML وفهم عمل هذا العنصر. في هذه العملية، نحن ملزمون الى حد كبير أن نكون أهلًا لثقة مطور آخر في البرنامج، هذا ينطبق على HTML أيضًا. ماذا تتوقع إذا رأيت الشيفرة التالية؟ <a class="btn" href="http://css-tricks.com"> <div class="nav__listItem">Item one</div> <div class="nav__listItem">Item two</div> </a> ربما ما يحدث في المثال السابق، هو أن عنصرًا في الحاوية البرمجية block يشتمل على الشيفرة التي يحتاجها المطور، لكن العناصر الأبناء لا تحتاج إلى الصنف .nav على أنه عنصر أب. هذه المشكلة تجعل البرنامج استثنائيًّا وغير متسق ومتناقض مع نفسه وينبغي تجنبها بأي ثمن. لذلك، يمكننا تلخيص هذه المشكلات في: عدم استخدام المُعدلات في الحاويات البرمجية block غير المترابطة. تجنب صنع عناصر رئيسية غير ضرورية عندما يكون العنصر الفرعي موجودًا بشكل جيد بدون أي مشاكل. المزيد من الأمثلة العملية على BEM قائمة قابلة للطي: في هذا المثال، هناك حاوية برمجية block واحدة وعنصران ومُعدِّل واحد. يمكننا هنا إنشاء مُعدِّل مثل .accordion__copy-open يتيح لنا معرفة أنه يجب ألا نستخدمه في حاوية برمجية block أو عنصر آخر. قائمة تنقل: يحتوي هذا المثال على كتلة block واحدة و 6 عناصر ومُعدِّل واحد. من الجيد تمامًا إنشاء حاويات برمجية blocks بدون معدلات على الإطلاق. يمكن للمطور في مرحلة ما في المستقبل أن يربط هذه الحاوية بمعدلات جديدة طالما بقيت ثابتة. عيوب BEM ربما لا تحب استخدام شرطة مزدوجة. حسنًا، استخدم شيئًا آخر فريدًا تستخدمه باستمرار. هنا رأي آخر: هذه الثلاثة محددات الأخيرة جميعها لها مستويات خصوصية مختلفة. تحتمل أن يكون لها عنصر رئيسي أو لا بدون أي قواعد معمول بها، هل من الممكن أن يكون هذا المثال الصغير جيد بالنسبة لك؟ ربما. ولكن كلما زاد عدد تعابير CSS البرمجية في المشروع، زاد عدد الأشياء الصغيرة مثل هذه، وبالتالي زادت مشاكل الخصوصية والتعقيد. ليس بالضرورة اختيار رأي صموئيل هنا، ولكن آراءه شاركها الكثير من الناس لذلك فهو مثال جيد. بخصوص من يرفضون BEM تمامًا فلا بأس بذلك، لكنني أعتقد أنه سيكون من الصعب القول بأن وجود مجموعة من القواعد التي تساعد في الفهم والحفاظ على CSS قابل للتعديل هو فكرة سيئة. في منهجية SMACSS، من المحتمل أن تجد اسم صنف CSS متكون من ثلاثة أحرف. تتبع المُعدِّلات بعدئذٍ اسم الوحدة النمطية باستخدام شرطة: /* مثال عن وحدة */ .btn { } /* btn معدِّل الصنف */ .btn-primary { } /* مع حالة Btn الوحدة */ .btn.is-collapsed { } هذه مجرد طريقة تسمية مختلفة لنفس المشكلة. إنه مشابه إلى حد ما، لكن تكون أكثر تحديدًا بشأن التبعيات والحفاظ على خصوصية التفاصيل. في OOCSS، الحاويات البرمجية هي عامة بالغالب. /* مثال عن وحدة */ .mod { } /* جزء من الوحدة */ .inner { } /* Talk الوحدة */ .talk { } /* تغيير جزء داخل الوحدة */ .talk .inner { } لذلك، يمكنك استخدام أصناف متعددة في HTML للحالات المختلفة. لم يتم تسمية الجزء الداخلي مثل التابع له، لذلك فهو أقل وضوحًا ولكنه قابل لإعادة استخدامه. ستقوم BEM بعمل .mod__inner و .mod--talk و mod-talk__inner.. هذه مجرد اختلافات في المنهجية. تذكر أن لا أحد يجبرك على استخدامها، فهذه قواعد مفروضة ذاتيًا حيث تأتي القيمة من متابعتها. BEM و Sass لأولئك الذين يستخدمون Sass ويستمتعون بتشعيب (nesting) العناصر كوسيلة لتحديد النطاقات لتنسيقات، لازال بإمكانك أن تكون المسؤول عن الصيغة المتشعبة ولكن يمكنك الحصول على CSS غير متشعب، باستخدام @at-root: ملف Scss: .block { @at-root #{&}__element { } @at-root #{&}--modifier { } } يولد ملف CSS التالي: .block { } .block__element { } .block--modifier { } ويمكنك الحصول على ملخص كما تريد! تحقق من منشئ BEM الذي يخص Danield Guillan أو Anders Schmidt Hansen من أجل BEM التعبيرية. الخلاصة أعتقد أنه من الإنصاف القول إنه على الرغم من أن BEM لن يحل جميع مشاكلنا، إلا أنه مفيد بشكل كبير في بناء واجهات قابلة للتطوير والصيانة حيث يجب أن يكون لدى كل فرد في الفريق فكرة واضحة عن كيفية تطوير تلك الأشياء. ذلك لأن الكثير من التطوير في الواجهة الأمامية لا يتعلق فقط بالخدع اللطيفة التي تحل مشكلة صغيرة واحدة على المدى القصير؛ نحتاج إلى اتفاقات وعقود ملزمة بين المطورين بحيث يمكن لبرنامجنا التكيف مع مرور الوقت وعلى المدى البعيد. بشكل عام، أود أن أفكر في BEM كإجابة على سؤال نيكولاس غالاغر: ترجمة -وبتصرف- للمقال BEM 101 لصاحبها Robin Rendle.1 نقطة
-
هذا الدرس عبارة عن مدخل إلى SVG. سنتعلم فيه المعلومات الأولية التي تحتاجها لرسم أشكال SVG بسيطة باستخدام لغة XML الأساسية. فإن لم تستخدم SVG من قبل فهذا هو الدرس المناسب لك. في الماضي كان تنسيق الصور الوحيد المعتمد من قبل جميع المتصفحات هو تنسيق GIF وهو ملف صورة مطوّر من قبل CompuServe. ثم ظهرت ملفات صور JPEG التي تسمح بضغط الصور دون ضياع للتفاصيل مع حجم صغير مقارنة بملفات GIF. بعد فترة من الزمن ظهرت الحاجة إلى رسومات فكتور ثنائية الأبعاد على الإنترنت. وبعد دخول عدد من تنسيقات الصور إلى المنافسة في رابطة الويب W3C تم تطوير تنسيق SVG أخيرًا عام 1999. سوف أريكم الآن كيفية إنشاء أشكال باستخدام SVG. هذا الدرس سيشرح كيفية استخدام SVG في صفحات الويب. ما هو SVG؟ إن مصطلح SVG هو اختصار إلى Scalable Vector Graphics أي رسومات فكتور القابلة لتغيير الحجم وهو عبارة عن تنسيق صور قائم على لغة XML من أجل الويب. وعلى عكس تنسيقات GIF وPNG وJPEG فإن تنسيق SVG قابل لتعديل الحجم بسهولة دون أي ضياع للدقة ولتفاصيل الرسومات. إن ملف XML الذي يحوي صور SVG يمكن إنشاؤه وتخصيصه وتكامله مع بقية لغات W3C القياسية الأخرى مثل DOM وXSL باستخدام أي محرر نصوص. كما يمكن إنشاء صور SVG بصريًّا باستخدام برامج الرسم والفكتور كبرنامج أدوبي إليستريتور Adobe Illustrator. لماذا نستخدم SVG؟ يمكن إنشاؤها وتعديلها باستخدام أي محرر نصوص. يمكن طباعتها بدقة عالية جدًّا. يمكن استخدامها من أجل الرسومات المتحركة. موصى بها من قبل W3C. تعمل مع بقية لغات W3C القياسية مثل DOM. مظهرها عظيم للعروض البصرية. يمكن تعديل حجمها إلى أي حجم نريد بدون أي تشوه قد يطرأ عليها. SVG على صفحات HTML يمكن تضمين SVG بسهولة داخل ملفات HTML باستخدام وسم SVG. كما في أسطر الأوامر التالية. <svg width=" " height=" "> [element code here..] </svg> كماترى فإن SVG لها وسمها الخاص. يجب أن يتم تحديد طول وعرض الرسم وذلك لاحتواء عنصر الرسم. العناصر التالية يمكن أن تستخدم للرسم داخل مساحة العمل الخاصة. Circle Rectangle Ellipse Line Polyline Polygon Path Text فلنبدأ الآن بتعلم استخدام هذه العناصر في الرسم. إنشاء الدائرة في SVG دوائر الـ SVG يمكن تنفيذها باستخدام وسم circle. كهذا المثال. <svg height="300" width="300"> <circle cx="60" cy="60" r="50" style="fill: blue; stroke: black; stroke-width: 2px;" /> </svg> النتيجة هي صورة الدائرة الظاهرة في الصورة التالية. في هذا المثال استخدمت وسم circle ثم cx (إحداثيات x) وcy (إحداثيات y) والتي تحدد مركز الدائرة المرسومة. ثم وضعت القيمة 50 للمتغير r (نصف القطر) والذي سيحدد طول الخط الواصل بين مركز الدائرة ومحيطها. أخيرًا أضفت أنماط للون التعبئة والحدود وعرض الحدود. إنشاء مستطيل في SVG يمكن تنفيذ رسم المستطيل عبر وسم rectangle ويمكن تحديد قيم مختلفة من الارتفاع والعرض. <svg height="300" width="300"> <rect width="250" height="100" rx="11" ry="11" style="fill: yellow; stroke: green; stroke-width: 4px;"/> </svg> النتيجة النهائية هي صورة SVG عبارة عن مستطيل كما في الصورة التالية. قمتُ بتحديد العرض والارتفاع باستخدام وسم rectangle. ثم أضفت rx وry التي تحدد استدارة حدود المستطيل. إذا قمت بإزالة القيمتين الأخيرتين فستحصل على حواف حادة. ثم نضيف أنماطًا عبر لون التعبئة والحدود وعرض الحدود. إنشاء القطع الناقص (شكل بيضوي) في SVG يتم تنفيذ هذا الشكل عبر وسم ellipse. القطع الناقص ليس له ارتفاع وعرض متساويين وعلى عكس الدائرة فإن نصف القطر cx وcy مختلفان. انظر الكود البرمجي التالي. <svg height="300" width="300"> <ellipse cx="190" cy="70" rx="100" ry="50" style="fill:red; stroke:green;stroke-width:2"/> </svg> صورة القطع الناقص (الشكل البيضوي) النهائية كما في الصورة التالية. إن cx وcy يحددان مركز شكل القطع الناقص بينما rx وry يحددان قطري الشكل. وكما ترى فإن rx يحدد عرض الشكل بينما ry يحدد ارتفاع الشكل. ثم نضع الأنماط من ألون التعبئة والحدود وعرضها كما في كل مرة. إنشاء خط في SVG يمكن إنشاء خط SVG باستخدام وسم line. كما هو واضح من الاسم فإن هذا الوسم يرسم خطوطًا، كما في المثال التالي. <svg height="300" width="300"> <line x1="0" y1="0" x2="100" y2="150" style="stroke:#000; stroke-width:5" /> </svg> النتيجة النهائية ستظهر كما في الصورة التالية. في هذا المثال يمثّل x1 الاحداثي x فيما يمثّل y1 الاحداثي y وهو ما يحدد نقطة بداية الخط. فيما تحدد الاحداثيات x2 وy2 نقطة النهاية. وباستخدام ميزات الانماط وضعتُ لون الحدود أسود ولعرض 5بكسل. إنشاء الخطوط المتعددة في SVG يمكن تنفيذ هذه الخطوط عبر وسم polyline. حيث يستخدم لرسم الأشكال المكونة من خطوط مستقيمة كهذا المثال. <svg height="300" width="300"> <polyline points="60,50 150,120 100,220 200,170" style="fill:none;stroke:black;stroke-width:3" /> </svg> والنتيجة كما هي واضحة في الصورة. وباستخدام الاحداثيات x وy يمكنك تعيين كل نقطةمن النقاط الفردية لإنشاء أي شكل تريده. وكما ترى هنا لدي أربع نقاط تم وصلها ببعضها لتشكيل الخطوط. وأضفت حدودًا سوداء وبعرض 3 بكسل. إنشاء شكل مضلع في SVG يمكن تنفيذه عبر وسم polygon. هذا العنصر سيرسم شكلًا مكونًا من أكثر من ثلاثة جوانب. شاهد الترميز التالي. <svg height="300" width="300"> <polygon points=" 60,20 100,40 100,80 60,100 20,80 20,40" style="fill:cyan;stroke:red;stroke-width:1" /> </svg> النتيجة النهائية كما في الصورة. في هذا الشكل نقاط يتم تحديدها عبر الاحداثيات x وy لكل جانب من جوانب الشكل من النقطة الأولى إلى النقطة الأخيرة. في المثال، أنشأتُ شكلًا مسدّسًا بست جوانب. كما ترى هناك 6 نقاط متصلة ببعضها محددة بالاحداثيات x وy. ثم حددت لون التعبئة باللون السماوي مع لون حدود أحمر وبعرض 1 بكسل. إنشاء مسارات في SVG يمكن تنفيذه عبر وسم path. هذا العنصر سيرسم مسارًا مخصّصًا وأشكالًا تتكون من منحنيات، خطوط وأقواس. من بين جميع عناصر SVG هذا هو العنصر الأصعب للتعلم. مسارات SVG تستخدم الأوامر التالية. M للحركة L للخط V للخط العمودي H للخط الأفقي C للمنحني S للمنحني الناعم T لمنحنيات البيزير التربيعية A للأقواس البيضوية Z لإغلاق المسار وتحدد الأحرف الكبيرة الموقع بدقة بينما تحدد الأحرف الصغيرة الموقع بشكل تقريبي. كما في المثال التالي. <svg height="300" width="300"> <path d="M 30 40 C 140 -30 180 90 20 160 L 120 160" style="fill: none; stroke: black; stroke-width: 6px;" /> </svg> النتيجة ستكون كالصورة التالية من خلال الترميز في الأعلى سترى بأنني استخدمت حرف d. ميزة حرف d هذا ستكون دائمًا أمر التحريك. ثم استخدمت حرف M الذي يعني التحرك نحو اتجاه معين. وقبل رسم أي شيء عليك أن تحرك المؤشر الافتراضي إلى الموقع المفضّل. في هذا المثال حرّكتُ المحور x إلى 30 والمحور y إلى 40. المنحني يبدأ عند 140,-30 كنقطة للبداية. تاليًا نزلت نقاط المنحني للأسفل واليمين عند النقطة 180,90 وتنتهي عند 20,160 ولإنهاء المسار أنشأت خطًّا عند النقطة 120,160. إنشاء النص في SVG يمكن تنفيذ هذا الأمر من خلال الوسم المغلق text. هذا العنصر يستخدم لرسم النص في صورة SVG. <svg height="300" width="300"> <text x="20" y="30" fill="blue" font-size="20">This a great sample for Text SVG! </text> </svg> النتيجة ستكون كالتالي. في المثال استخدمت موقع المحور x للنص عند 20. هذا سيضع النص على بعد 20 بكسل من اليسار بينما وضعت المحور y عند 30 ما يجعل النص على بعد 30 بكسل عن الحافة العلوية. ثم لوّنت النص باللون الأزرق وجعلت حجمه عند 20 بكسل. هذا سيظهر العبارة التالية "This a great sample for Text SVG!". ترجمة -وبتصرف- للمقال How to Create Simple Shapes with SVG لصاحبه Editorial Team1 نقطة