-
المساهمات
0 -
تاريخ الانضمام
-
تاريخ آخر زيارة
آخر الزوار
338 زيارة للملف الشخصي
إنجازات Omar Kall

عضو مساهم (2/3)
4
السمعة بالموقع
-
نشرح في هذه المقالة التوابع الجديدة للتعامل مع المجموعة set في لغة جافا سكريبت JavaScript، والتي أصبحت متوفرة ومتاحة على جميع متصفحات الويب منذ النسخة Firefox 127. فمن المفيد الاطلاع على توابع المجموعات لاسيما للمطورين الجدد الذين يتعاملون مع بنية المجموعة set لأول مرة ويتطلعون لاستخدامها في بناء تطبيقاتهم، حيث سنسلط الضوء على المزايا الرئيسية لهذه التوابع ونوضح سبب الحاجة لاستخدامها في بناء التطبيقات من خلال الأمثلة العملية. ما هي التوابع الجديدة للتعامل مع المجموعات نستعرض فيما يلي أهم التوابع الجديدة للمجموعة set المدعومة من أغلب المتصفحات الحديثة وهي كالتالي: التابع ()intersection : يعيد مجموعة جديدة تحتوي على العناصر الموجودة في كلا المجموعتين أي تقاطع المجموعتين التابع ()union: يعيد مجموعة جديدة تضم جميع العناصر الموجودة في المجموعتين أي اجتماع المجموعتين التابع ()difference: يعيد مجموعة جديدة تحتوي على العناصر غير المتشابهة في المجموعتين أي فرق المجموعتين التابع ()symetricDifference: يعيد مجموعة جديدة تحتوي على العناصر الموجودة في أحد المجموعتين وغير الموجودة في المجموعة الثانية التابع ()isSubsetOf: يعيد قيمة منطقية تشير فيما إذا كانت جميع عناصر المجموعة الأولى مجموعة فرعية محتواة ضمن المجموعة الثانية أي هل المجموعة الأولى مجموعة جزئية من المجموعة الثانية. التابع ()isSupersetOf: يعيد قيمة منطقية تشير إلى ما إذا كانت المجموعة الأولى مجموعة شاملة للمجموعة الثانية، أي هل جميع العناصر في المجموعة الثانية موجودة في المجموعة الأولى. التابع ()isDisjointFrom: يرجع قيمة منطقية تشير فيما إذا كانت المجموعتان مستقلتين أي المجموعة الأولى لا تحتوي على عناصر مشتركة مع المجموعة الثانية. سنوضح لك في الفقرات القادمة معنى واستخدام هذه التوابع باستخدام الأمثلة العملية. ما هي المجموعة في لغة جافا سكريبت JavaScript المجموعة Set هي بنية بيانات مشابهة للمصفوفة Array، ولكنها تختلف عنها في أنها لا تسمح بتخزين القيم المكررة. بمعنى آخر، يمكن تخزين كل قيمة في Set لمرة واحدة فقط، حتى إذا كانت القيمة مكررة عند إضافتها. على سبيل المثال، إذا أخذنا قائمة من العناصر وأضفناها إلى مجموعة Set، ثم فحصنا عناصر المجموعة، سنلاحظ أن العناصر المكررة في القائمة قد حذفت تلقائيًا. إليك مثال توضيحي: قد يبدو هذا المثال بسيطًا ولا يعكس بالكامل أهمية استخدام المجموعات، ولكن من المفيد للغاية أن توفر لغة البرمجة طريقة لإنشاء مجموعات فريدة ومميزة من العناصر، خاصة عندما تتعامل مع أنواع أو كائنات أكبر وأكثر تعقيدًا. على سبيل المثال، يمكنك إضافة العناصر إلى مجموعة set بالطريقة التالية: const dogs = new Set(); const yoshi = { name: "Yoshi", personality: "Friendly" }; dogs.add(yoshi); هناك ميزة أخرى لاستخدام المجموعات sets وهي أن التحقق من وجود عنصر في المجموعة أسرع من التحقق من وجوده في مصفوفة، مما يجعلها مثالية في الحالات التي تتطلب مراقبة الأداء عند التعامل مع البيانات الضخمة. كما يمكنك إنشاء مجموعات جديدة ذات خصائص منطقية محددة بناء على مجموعات موجودة، وهو ما سنناقشه في الفقرات القادمة. اجتماع مجموعتين يمكننا باستخدام دمج المجموعات التحقق من العناصر الموجودة في إحدى المجموعتين أو كليهما. لدينا في المثال التالي مجموعتان ونريد الحصول على مجموعة ثالثة تضم جميع عناصر المجموعتين ولكن بدون تكرار، سنكتب كالتالي: // إنشاء اجتماع المجموعتين const unionSet = set1.union(set2); // عرض العناصر في الاجتماع unionSet.forEach((item) => { const li = document.createElement("li"); li.textContent = item; unionList.appendChild(li); }); نستخدم عناصر المحتوى النصي textContent في لغة HTML لكل عنصر من عناصر القائمة، مما يشكل لدينا مجموعات من السلاسل النصية sets of strings. وتكمن أهمية استخدام المجموعات عندما نخزن أنواع متعددة من البيانات داخلها، مثل المصفوفات والكائنات. إن استخدام المجموعات sets مفيد للغاية لأننا لا نحتاج إلى إجراءات إضافية لإزالة التكرار أو التحقق من المساواة أو أي مقارنات أخرى، فكما شرحنا سابقًا عند استخدام المجموعة، ستكون جميع عناصر المجموعة قيم فريدة وغير مكررة. وبالتالي، فإن التابع union سينتج لنا مجموعة ثالثة تحتوي على العناصر الفريدة الموجودة في إما المجموعة الأولى أو الثانية أو كليهما. تقاطع مجموعتين يمكننا باستخدام تقاطع المجموعات معرفة العناصر الموجودة في كلتا المجموعتين وتحديد العناصر المشتركة بينهما. في المثال التالي، سنستخدم تقاطع المجموعات لاستخراج العناصر المشتركة بين المجموعتين وعرضها بدلاً من دمجها في مجموعة ثالثة كما فعلنا باستخدام التابع union في الفقرة السابقة: // إنشاء مجموعة تقاطع المجموعتين const intersectionSet = set1.intersection(set2); // المرور على العناصر في القوائم وتحديد العناصر المشتركة allListItems.forEach((item) => { if (intersectionSet.has(item.textContent)) { item.className = "match"; } }); الفرق التناظري لمجموعة سنستعرض في ما يلي مفهوم الفرق التناظري أو المتماثل الذي يسمح لنا بالتحقق من العناصر الموجودة في إحدى المجموعتين فقط وليس كليهما، قد يبدو المفهوم للوهلة الأولى معقدًا بعض الشي .لكنه سيتضح من خلال المثال التالي: const setNotBoth = set1.symmetricDifference(set2); allListItems.forEach((item) => { if (setNotBoth.has(item.textContent)) { item.className = "match"; } }); استخدامنا في هذا الكود التابع symmetricDifference لحساب الاختلاف التناظري بين المجموعتين set1 و set2. والنتيجة مجموعة تحتوي على العناصر التي توجد في إحدى المجموعتين ولكن ليس في كليهما. قد يكون ما سبق جديد وغير مفهوم بالنسبة لك، لفهم الموضوع قارن الفرق التناظري للمجوعات مع تقاطع المجموعات، وعندها ستجد فالتابع symmetricDifference يعمل بطريقة منطقية معاكسة للتابع intersection، وهنا سينتهي هذا الالتباس بالنسبة لك. فرق المجموعات يمكننا استخدام تابع الفرق للتحقق من العناصر الموجودة في إحدى المجموعات وغير الموجودة في المجموعة الأخرى. في المثال التالي، سننشئ مجموعتين بدلاً من مجموعة واحدة. بداية ننشئ المجموعة الأولى set1only باستخدام التابع set1.difference(set2)، مما يعطينا العناصر الموجودة في المجموعة الأولى فقط. بعد ذلك، سنقوم بنفس العملية مع المجموعة الثانية set2only لمعرفة العناصر الموجودة في المجموعة الثانية وغير الموجودة في المجموعة الأولى. ويمكننا باستخدام التابع difference لكل مجموعة تحديد العناصر التي لا تظهر في المجموعة الأخرى. const set1only = set1.difference(set2); const set2only = set2.difference(set1); allListItems.forEach((item) => { if (set1only.has(item.textContent)) { item.className = "setOneMatch"; } else if (set2only.has(item.textContent)) { item.className = "setTwoMatch"; } }); المجموعة الفرعية والمجموعة الشاملة والمجموعة المنفصلة آخر التوابع والمفاهيم الجديدة الخاصة بالمجموعات التي سنتظرق إليها في هذا المقال هي توابع المجموعة الفرعية Subset والمجموعة الشاملة Superset والمجموعة المنفصلة Disjoint from Set. فالنتيجة في جميع توابع المجموعات التي تعرفنا عليها سابقًا كانت عبارة عن مجموعة جديدة، أما النتيجة في التوابع الحالية فهي عبارة عن قيمة منطقية تشير إلى حالة منطقية معينة. سنتعرف في البداية على التابعين subset و superset، حيث نحصل على النتيجة من خلال بعض عبارات التحقق المنطقية. على سبيل المثال، يمكننا التحقق مما إذا كانت المجموعة الأولى مجموعة جزئية من المجموعة الثانية، ومن ثم إضافة نص منطقي True أو False للعنصر بناءً على نتيجة التحقق. if (set1.isSubsetOf(set2)) { oneSubTwo.textContent += " [TRUE]"; } else { oneSubTwo.textContent += " [FALSE]"; } يمكننا باستخدام التابع isSubsetOf() التحقق مما إذا كانت جميع عناصر المجموعة الأولى تظهر بالكامل في المجموعة الثانية. بينما يمكننا باستخدام التابع isSupersetOf() التحقق من العكس أي معرفة إذا كانت المجموعة الأولى تحتوي على جميع عناصر المجموعة الثانية بالإضافة إلى عناصر أخرى. أما التابع الأخير الذي سنتعرف عليه فهو isDisjointFrom()، حيث يمكننا من خلاله معرفة ما إذا كانت المجموعتان لا تملكان أي عناصر مشتركة بينهما. لاحظ في المثال أدناه أن المجموعتين الأولى والثانية لا تحققان شروط التابع isDisjointFrom()، لأن هناك عنصر مشترك بينهما وهو العنصر C، أما المجموعة الثالثة فهي منفصلة عن المجموعتين الأولى والثانية لأنها لا تحتوي على أي عناصر مشتركة مع أي منهما. الخلاصة إن مفهوم المجموعات sets في لغة جافا سكريبت هو مفهوم مهم ويفترض أنه قد أصبح مفهومًا بالنسبة لك، حيث ستحتاجه في تطبيقات عديدة مثل إزالة التكرار من قائمة تحتوي على بعض القيم المكررة أو التحقق بسرعة مما إذا كان عنصر موجودًا في مجموعة أو إجراء العمليات الرياضية والمنطقية بين المجموعات. نتمنى لكم الاستفادة الجيدة من هذا المقال، وفي حال كان لديكم أي تساؤل يمكن تركه في قسم التعليقات أسفل المقال أو كتابته في قسم الأسئلة والأجوبة في الأكاديمية. ترجمة وبتصرف للمقال New JavaScript Set methods لكاتبه Brian Smith. اقرأ أيضًا القوائم lists والمجموعات sets في جافا مدخل إلى البيانات وأنواعها: التجميعات Collections هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت التعامل مع الصفوف والمجموعات والقواميس في بايثون
-
تتضمن متصفحات الويب الواجهة البرمجية Page Visibility API التي تسمح لنا بالكشف عن حالة ظهور الصفحة في نافذة المتصفح، ومعرفة هل هذه الصفحة مرئية وظاهرة للمستخدم أم مخفية عنه كأن تكون مصغرة أو موجودة في علامة تبويب غير نشطة، فهذه المعلومة مفيدة جدًا للمطورين وتساعد في تحسين تجربة المستخدم وأداء تطبيق الويب. فالتحقق من رؤية صفحة الويب يوفر لنا فكرة واضحة حول كيفية تفاعل الزوار مع صفحات الموقع، ويتيح معرفة ما إذا كانت الصفحة مرئية للمستخدم أم لا، كما يساعدنا على إعداد مستمعي الأحداث Event Listener لتنفيذ إجراءات معينة عندما تتغير حالة رؤية الصفحة. يشرح هذا المقال معنى رؤية الصفحة، ويوضح كيفية استخدام الواجهة البرمجية Page Visibility API، وأهم المشكلات الشائعة التي ينبغي تجنبها عند التعامل معها. ما هي الواجهة البرمجية Page Visibility طُوِّرت واجهة برمجة التطبيقات Page Visibility في البداية كميزة مستقلة منفصلة عن مواصفات HTML الرسمية التي تتولى تحديد كيفية بناء صفحات الويب، وسرعان ما تم دمجها مع مواصفات HTML تحت قسم page visibility، توفر هذه الواجهة طريقة فعّالة لتحديد حالة رؤية صفحة الويب، حيث يمكن من خلالها معرفة ما إذا كانت صفحة الويب مرئية visible أو مخفية hidden. تكون الصفحة مخفية إذا صغرنا نافذة متصفح الويب الذي يحتوي على تلك الصفحة، أو إذا حجبنا الصفحة بشكل كامل من قبل صفحة أخرى، أو إذا انتقلنا إلى علامة تبويب أخرى، كما تتغير حالة رؤية الصفحة إلى مخفية عندما تقفل شاشة حاسوبك أو هاتفك وتنشط شاشة قفل نظام التشغيل، فعند قفل الشاشة ستعد صفحة الويب التي يشاهدها المستخدم مخفية لأن شاشة القفل تمنع من رؤية الصفحة أو التفاعل معها. من ناحية أخرى، تعد الصفحة مرئية طالما أنها تظهر بشكل جزئي على الشاشة، حتى إذا كانت نافذة المتصفح غير مكبرة بالكامل أو كانت هناك تطبيقات أخرى تغطي جزءًا من النافذة. ما يهم هو أن الصفحة تظل ضمن نطاق رؤية المستخدم وليست مخفية بالكامل كما يحدث عند التبديل إلى علامة تبويب أخرى أو تصغير نافذة المتصفح بالكامل. طريقة التحقق من تغير حالة رؤية الصفحة يمكنك التحقق من حالة رؤية الصفحة باستخدام الخاصية document.visibilityState، التي تعيد إما القيمة visible عندما تكون الصفحة مرئية أو hidden عندما تكون الصفحة مخفية. كما يمكن معرفة حالة رؤية الصفحة أيضًا التحقق من قيمة الخاصية المنطقية document.hidden كما يلي: console.log(document.visibilityState); // "visible" console.log(document.hidden); // false ومن الملائم أكثر استخدام الحدث visibilitychange، حيث يمكنك من خلاله تشغيل كود معين عندما تتغير حالة رؤية الصفحة، بدلاً من التحقق من الحالة يدويًا: document.addEventListener("visibilitychange", (event) => { // تنفيذ الكود عند تغيير حالة رؤية الصفحة }); بهذه الطريقة يمكنك التحقق من رؤية الصفحة عندما تتغير الحالة، وتنفيذ كود معين بالاعتماد على النتيجة كما يلي: document.addEventListener("visibilitychange", () => { if (document.hidden) { // ينفذ الكود هنا عندما تتغير حالة الرؤية إلى مخفية } else { // ينفذ الكود هنا عندما تتغير حالة الرؤية إلى مرئية } }); تجدر الإشارة إلى أن الخاصية document.visible غير موجودة في الواجهة البرمجية، وبالتالي كي تعرف إن كانت الصفحة مرئية فيمكنك استخدام الكود التالي document.visibilityState === "visible" أو استخدام الخاصية !document.hidden كما يلي: if (document.visibilityState === "visible") { // الصفحة مرئية } if (!document.hidden) { // الصفحة مرئية } فائدة رؤية الصفحة تفيدنا ميزة رؤية الصفحة في عدة حالات، ولكن الفائدة الأكبر تكون في التحليلات، واستخدام موارد الحاسوب، وإضافة الوظائف التي يمكن من خلالها تحسين تجربة المستخدم على مجموعة من الأجهزة، وسنناقش كل هذه الفوائد بشيء من التفصيل في الفقرات التالية. استخدام دورة حياة الجلسة Session lifecycle في التحليلات عندما تجمع البيانات المتعلقة بتفاعل المستخدم مع الموقع أو التطبيق وتحللها ستحتاج لتسجيل اللحظة التي تتغير فيها حالة الصفحات من مرئية visible إلى مخفية hidden، فقد يكون تغير حالة الصفحة إلى مخفية hidden هو الحدث الأخير الذي يمكننا من خلاله مراقبة الصفحة، ولذلك يتعامل المطورون في أغلب الأحيان مع هذا الحدث على أنه نهاية لجلسة المستخدم user session. بالرغم من إمكانية ذلك، إلا أن تحديد نهاية الجلسة يعتمد بشكل كبير على تعريف مفهوم الجلسة. إذ يمكن تحديد الجلسة على أنها فترة زمنية ثابتة من عدم النشاط، بدلاً من الاعتماد فقط على حالة إخفاء الصفحة، وهذا يعتمد بالطبع على احتياجاتك عند تطوير الموقعك: document.addEventListener("visibilitychange", () => { if (document.visibilityState === "hidden") { navigator.sendBeacon("/log", analyticsData); } }; إدارة الموارد بفعالية تفيدنا معرفة حالة رؤية الصفحة القدرة على التوقف عن إجراء شيء ما عندما يتوقف الزائر عن مشاهدة الصفحة، وهذه ميزة مفيدة للغاية وتساعد على إدارة موارد جهاز العميل أو حتى الخادم بفعالية. فمتصفح الويب يلعب دورًا مهمًا في إدارة الموارد من خلال توفير عدة ميزات كميزة إلغاء تحميل التبويب Tab Unloading، وميزة تحسين أداء علامات التبويب الموجودة في الخلفية Background Tabs. وبالرغم من أن المتصفحات تقدم بعض التحسينات التلقائية في إدارة الموارد، إلا أن بإمكاننا أيضًا تحسين أداء وكفاءة تطبيقات الويب بأنفسنا من خلال دمج اختبارات الأداء في مراحل مبكرة من عملية تطوير الموقع، أو تعزيز الأداء يدويًا في الجوانب التي لا تحسنها المتصفحات تلقائيًا. على سبيل المثال، عندما تكون متصلاً بالخادم في الزمن الحقيقي باستخدام تقنيات مثل WebSockets وWebRTC، فمن الأفضل أن تستخدم مقاطع فيديو منخفضة الدقة نوعًا ما لتقليل الحمل على الشبكة، كما يمكنك تحسين كيفية تعامل المتصفح مع قواعد البيانات المدمجة مثل IndexedDB فهذا يؤدي بدوره إلى تحسين الأداء فمتصفحات الويب لا تفرض قيودًا على العمليات التي تستخدم قواعد البيانات المدمجة وتسمح بالقراءة والكتابة منها دون التأثر بالانقطاع أو البطء في الشبكة. تحسين تجربة المستخدم عندما تتغير حالة الصفحة من مخفية إلى مرئية، من المفترض أن المستخدم قد عاد لزيارة الصفحة مرة أخرى. هذا يتيح لنا استئناف الأنشطة أو العمليات التي كانت متوقفة عندما كانت الصفحة مخفية في الخلفية. لكن هناك بعض التحديات التي قد تواجهها في هذه الحالة، خصوصًا عند التعامل مع الوسائط مثل مقاطع الفيديو أو ملفات الصوت، فقد لا يرغب المستخدم بإيقاف الوسائط عند إخفاء الصفحة، وسنناقش المزيد عن هذه التحديات في الفقرات التالية. حالات يجب تجنبها عند التحقق من رؤية الصفحة ينبغي منح المستخدمين القدرة على التحكم في بدء أو استئناف أو تجاهل الوسائط عند تغير حالة رؤية الصفحة، فهذا أمر أساسي لتوفير تجربة مستخدم سهلة. لذا، من الضروري استخدام واجهة برمجة التطبيقات Page Visibility API بحذر لمراعاة راحة المستخدمين أثناء التصفح. فلا يجب أن نفترض بأن المستخدمين يرغبون في إيقاف الوسائط تلقائيًا عند تغيير حالة الصفحة من مرئية إلى مخفية، ونتيح لهم تخصيص هذه الخيارات حسب رغبتهم. يوضح الكود البرمجي التالي كيفية تأثير الحدث visibilitychange على التحكم في الوسائط. فعند تغيير حالة الصفحة إلى مخفية سيتوقف تشغيل الصوت، وعند عودة الصفحة إلى مرئية سيستأنف تشغيله. <audio controls src="https://mdn.github.io/webaudio-examples/audio-basics/outfoxing.mp3"></audio> const audio = document.querySelector("audio"); document.addEventListener("visibilitychange", () => { if (document.hidden) { audio.pause(); } else { audio.play(); } }); لدينا في كود HTML السابق عنصر <audio> مع عناصر تحكم تمكّن المستخدم من تشغيل أو إيقاف الصوت كما يشاء. ونحدد في كود جافا سكريبت هل سنشغل الصوت أو نوقفه استنادًا إلى حالة رؤية الصفحة عبر الحدث visibilitychange. إذا كانت الصفحة مخفية أي تم التبديل إلى تبويب آخر أو تم تصغير النافذة، سنوقف تشغيل الصوت إذا كانت الصفحة مرئية أي عاد المستخدم إلى التبويب أو أعاد تكبير النافذة نستأنف تشغيل الصوت على الرغم من أن هذه الطريقة قد تبدو منطقية للوهلة الأولى، إلا أن هناك بعض المشكلات التي قد تنشأ في الحالات التالية: عند التبديل بين التبويبات فإذا أراد المستخدم الانتقال إلى تبويب آخر ثم العودة إلى التبويب الذي يحتوي الصوت، قد يؤدي ذلك إلى تشغيل الصوت أو إيقافه بشكل غير متوقع، وهذا قد يكون مزعجًا للمستخدم عند تصغير نافذة المتصفح ثم تكبيرها، فقد تتغير حالة الصفحة من مرئية إلى مخفية ويتوقف الصوت دون أن يتفاعل المستخدم معه بشكل مباشر إذا كان الصوت غير مرئي عند تحميل الصفحة كأن يكون موجودًا في أسفل الصفحة وغير مرئي عند تحميلها، عندها لن يعرف المستخدم بوجود الصوت وقد يتفاجأ بأن الصوت يعمل دون أن يكون متأكدًا من مصدره ويبدأ بالبحث عن التبويب الذي يحتوي على الصوت، مما يؤدي إلى تجربة مزعجة لتجنب هذه المشكلات وتحسين تجربة الاستخدام، يُفضل أن تتحقق أولًا إذا كان المستخدم قد تفاعل مع الصوت قبل أن نقرر استئناف تشغيله عند العودة إلى الصفحة. يمكن تحقيق ذلك من خلال تخزين حالة تشغيل الصوت عندما تكون الصفحة مخفية وعند عودتها لمرئية مرة أخرى، وبهذا نستأنف تشغيل الصوت فقط إذا كان في حالة تشغيل عند إخفاء الصفحة. تضمن هذه الطريقة أن الصوت لن يبدأ بشكل غير مرغوب فيه. لاحظ الكود التالي كيف عدّلنا مستمع الحدث visibilitychange ليخزن حالة عنصر الصوت عند إخفاء الصفحة باستخدام المتغير المنطقي playingOnHide وكيف سنستخدمه عندما تتغير حالة رؤية الصفحة إلى مخفية hidden: // تعيين المتغير playingOnHide ليكون false في البداية لأنه عند تحميل الصفحة لا نشغل الصوت تلقائيًا let playingOnHide = false; // إضافة مستمع للحدث visibilitychange الذي يتم إطلاقه عندما تتغير حالة الصفحة من مرئية إلى مخفية أو العكس document.addEventListener("visibilitychange", () => { // التحقق إذا أصبحت الصفحة مخفية if (document.hidden) { // إذا كانت الصفحة مخفية، سنخزن حالة الصوت هل هو مشغل أم لا playingOnHide = !audio.paused; // إذا كان الصوت مشغلاً نخزن القيمة true وإذا كان متوقفاً نخزن القيمة false // نوقف الصوت مؤقتًا عندما تصبح الصفحة مخفية audio.pause(); } }); ملاحظة: عند التعامل مع حالة إخفاء الصفحة، نجد أنه لا يمكننا الاعتماد على خاصية audio.playing لمعرفة إذا كان الصوت يعمل أم لا. بل نستخدم بدلاً من ذلك الخاصية !audio.paused التي تخبرنا إذا كان الصوت غير متوقف مؤقتًا. لاحظ الكود التالي عندما تصبح الصفحة مخفية document.hidden، سنتحقق إذا كان الصوت يعمل باستخدام !audio.paused فإذا كان الصوت يعمل فعلًا عندها نخزّن الحالة true في المتغير playingOnHide وإذا كان متوقف نخزن الحالة false. بعد ذلك، نوقف الصوت باستخدامaudio.pause() . وعندما تعود الصفحة مرئية مرة أخرى، نستخدم المتغير playingOnHide للتحقق إذا كان الصوت قد بدأ في التشغيل قبل اختفاء الصفحة. فإذا كان الصوت يعمل سنستأنف تشغيله باستخدام audio.play(). let playingOnHide = false; document.addEventListener("visibilitychange", () => { if (document.hidden) { playingOnHide = !audio.paused; audio.pause(); } else { // استئنف تشغيل الصوت إذا كان تشغيله بدأ بالفعل أثناء إخفاء الصفحة if (playingOnHide) { audio.play(); } } }); إليك الكود كاملًا، مع إضافة كود برمجي صغير يتحقق من حالة الصوت عندما تكون الصفحة مخفية: <audio controls src="https://mdn.github.io/webaudio-examples/audio-basics/outfoxing.mp3"></audio> const audio = document.querySelector("audio"); let playingOnHide = false; document.addEventListener("visibilitychange", () => { if (document.hidden) { playingOnHide = !audio.paused; audio.pause(); } else { if (playingOnHide) { audio.play(); } } }); See the Pen play by Hsoub Academy (@HsoubAcademy) on CodePen. الخلاصة وصلنا لنهاية مقالنا الذي شرحنا فيه تقنية رؤية الصفحة Page Visibility وأهميتها في إدارة الموارد وتحسين تجربة المستخدم. واستعرضنا كيفية استخدام هذه التقنية والاستفادة منها في سياقات متعددة مثل تحسين أداء الصفحة وإدارة الوسائط وتعزيز تجربة المستخدم. كما تناولنا بعض التحديات التي قد تواجه المطورين عند استخدام هذه الميزة، وقدمنا بعض الحلول البرمجية لتحسين أدائها. ترجمة وبتصرف للمقال Using the Page Visibility API لكاتبه Brian Smith اقرأ أيضًا الواجهات البرمجية للتعامل مع الصوت والفيديو في جافا سكريبت إضافة محتوى سمعي ومرئي في صفحة HTML سهولة وصول جميع الزوار لمواقع وتطبيقات الويب التركيز على عناصر صفحة الويب
-
لغة #C إحدى أبرز لغات البرمجة التي تطورها شركة مايكروسوفت ضمن بيئة التطوير المتكاملة Visual Studio. تعمل هذه اللغة ضمن بيئة تنفيذ اللغة المشتركة Common Language Runtime أو CLR اختصارًا والتي توفر ميزات عديدة مثل التكامل بين اللغات المختلفة ومعالجة الاستثناءات وتعزيز مستويات الأمان. وتعد لغة #C النموذج الأبسط والأكثر انتشارًا للغات التي تعمل ضمن بيئة CLR وهي تستخدم في مختلف أنواع التطبيقات سواء تطبيقات سطح المكتب أو تطبيقات الهاتف المحمول أو تطبيقات الجوال أو حتى ضمن خوادم البيانات. لغة #C هي لغة كائنية التوجه OOP وصارمة في تحديد الأنواع أي يجب تحديد نوع البيانات لكل متغير، وتساعد عمليات التحقق الصارمة من أنواع البيانات خلال مرحلة التصريف compile إلى اكتشاف أغلب الأخطاء البرمجية وتحديد مواقعها بدقة وتسريع معالجتها، مقارنة بلغات البرمجة التي تعتمد أسلوب تتبع الأخطاء في مرحلة التنفيذ runtime مما يزيد من صعوبة معرفة السبب الأساسي للخطأ، ومع ذلك يتجاهل العديد من مبرمجي لغة #C الفوائد الكبيرة من الكشف المبكر عن الأخطاء الأمر الذي قد ينجم عنه بعض المشكلات والأخطاء. يناقش هذا المقال أبرز أخطاء لغة #C على وجه الخصوص، لكن الأخطاء المطروحة هنا تعمم على جميع لغات البرمجة التي تعمل ضمن بيئة CLR، أو اللغات التي تستخدم مكتبة الأصناف القياسية FCL. الخطأ 1: استخدام المرجع كقيمة أو العكس يمكن للمطورين بلغة ++C -وغيرها من لغات البرمجة- التحكم في القيم المسندة إلى المتغيرات سواء كانت قيم مباشرة values أو مراجع references تشير إلى لموقع الذاكرة لمتغير أو كائن موجود مسبقًا، أما في لغة البرمجة #C فمن يتحكم بهذا القرار هو المبرمج الذي كتب الكائن object وليس المبرمج الذي أنشأ نسخة instance من هذا الكائن وأسندها للمتغير، وهذا هو الخطأ الأول الشائع للأشخاص الذين يتعلمون البرمجة. لنأخذ المثال التالي الذي يوضح بعض المفاجآت التي يمكن أن تحدث إذا لم تكن على دراية بنوع الكائن الذي تستخدمه في برنامجك هل هو من نوع قيمة أم من نوع مرجع كما يلي: Point point1 = new Point(20, 30); Point point2 = point1; point2.X = 50; Console.WriteLine(point1.X); // 20 (هل تفاجأت من هذه النتيجة) Console.WriteLine(point2.X); // 50 Pen pen1 = new Pen(Color.Black); Pen pen2 = pen1; pen2.Color = Color.Blue; Console.WriteLine(pen1.Color); // Blue (أم تفاجأت من هذه النتيجة) Console.WriteLine(pen2.Color); // Blue لاحظ أن كلًا من الكائنين point وpen أنشئا بنفس الطريقة تمامًا، لكن قيمة point1 بقيت على حالها دون تغيير عند تعيين قيمة جديدة للإحداثي X فيpoint2. أما في الحالة الثانية فقد تعدلت قيمة pen1 عند إسناد قيمة لون جديدة إلى pen2، وبالتالي يمكننا استنتاج أن كلًا من point1 وpoint2 يحتويان على نسخة من الكائن الأصلي point أما pen1 وpen2 فيحتويان على مراجع references تشير للكائن الأساسي pen وهذا ما يمكنه أن نتعلمه من خلال التجربة. لمعرفة الفرق بين الحالتين يمكننا العودة إلى التعريف الخاص بكل كائن وهو ما يمكننا القيام به بسهولة في بيئة التطوير Visual Studio من خلال وضع المؤشر على اسم الكائن والضغط على المفتاح F12. public struct Point { ... } // نوع قيمة value public class Pen { ... } // نوع مرجع reference تستخدم الكلمة المفتاحية struct في لغة البرمجة #C لتعريف النوع كقيمة value في حين تستخدم الكلمة المفتاحية class لتعريف النوع كمرجع reference. غالبًا ما يقع المطورون الذين لديهم معرفة مسبقة بلغة البرمجة ++C في هذا الخطأ فهناك تشابه كبير بالكلمات المفتاحية بينها وبين لغة #C، فإذا كنت ستعتمد في بعض البرامج على حالات قد تتطلب التمييز بين التمرير بالقيمة والتمرير بالمرجع (مثل القدرة على تمرير كائن ما كوسيط لتابع وجعل هذا التابع يغير من حالة الكائن) سيتوجب عليك اختيار النوع الصحيح للكائن لتجنب الوقوع في مثل هذه المشكلة. الخطأ 2: عدم فهم القيم الافتراضية للمتغيرات غير المهيأة عند تعريف متغير من النوع قيمة value عليك إسناد قيمة له، ولا يمكن إسناد القيمة null لهذا المتغير بشكل افتراضي، فالمتغيرات غير المهيأة من النوع قيمة يجب أن تحتوي على قيمة افتراضية معينة وهي القيمة الافتراضية لنوع القيمة المسندة لها. لذا عليك التحقق دومًا من وجود قيمة افتراضية للمتغير غير المهيَّأ قبل استخدامه، كما في المثال التالي: class Program { static Point point1; static Pen pen1; static void Main(string[] args) { Console.WriteLine(pen1 == null); // True Console.WriteLine(point1 == null); // False (لماذا؟) } } لاحظ أنه لا يمكن إسناد null إلى point1 لأن point من النوع قيمة والقيمة الافتراضية لها هي (0,0) وليست null، إن التعرف على هذا الخطأ في #C أمر بسيط وسهل للغاية لأن معظم الأنواع (وليس كلها) من النوع قيمة تمتلك الخاصية IsEmpty التي تمكنك من التحقق فيما إذا كان الكائن يمتلك قيمة افتراضية أم لا. Console.WriteLine(point1.IsEmpty); // True عليك تهيئة أي متغير بقيمة افتراضية محددة، أو التأكد ما هي القيمة الافتراضية التي يأخذها بشكل افتراضي. الخطأ 3: استخدام توابع مقارنة النصوص بشكل خاطئ هناك العديد من الطرق لمقارنة النصوص في لغة #C، ويعد العامل == أقلها تفضيلًا، فعلى الرغم من استخدامه من قبل كثير من المبرمجين إلا أنه لا يحدد بشكل صريح نوع المقارنة المطلوبة في الكود ويفضل ان تستعيض عنه بالتابع Equals لاختبار تساوي النصوص كما يلي: public bool Equals(string value); public bool Equals(string value, StringComparison comparisonType); في السطر الأول من الكود السابق يعمل التابع Equals بطريقة مشابهة تمامًا للمقارنة باستخدام ==، لكن تطبيقه يكون خاصًا بالسلاسل النصية فهو يجري مقارنة ترتيبية بين النصوص بناءً على المقارنة بايت بايت، وهذا مناسب في العديد من الحالات مثل مقارنة أسماء الملفات أو متغيرات البيئة. لكن في هذه الحالات التي نحتاج فيها لإجراء مقارنة معتمدة على الترتيب الأبجدي، فإن العيب الوحيد لاستخدام التابع Equals دون المعامل comparisonType هو أن القارئ قد لا يعرف نوع المقارنة المطبقة، أما السطر الثاني الذي يتضمن المعامل comparisonType لتحديد نوع المقارنة بدقة فهو يجعل الكود أوضح، ويجبرك على التفكير في نوع المقارنة التي ترغب في إجرائها. قد لا تتضح فائدة هذا المعامل في بعض اللغات كاللغة الإنجليزية إذ لا توجد اختلافات كبيرة عند استخدام المقارنات الترتيبية، لكن تؤدي المقارنة بدونه في لغات أخرى لحدوث أخطاء كالألمانية فقد يفسر الحرفان "ss" و"ß" على أنهما متساويين في بعض الحالات. لتوضيح ذلك، انظر المثال التالي: string s = "strasse"; // outputs False: Console.WriteLine(s == "straße"); Console.WriteLine(s.Equals("straße")); Console.WriteLine(s.Equals("straße", StringComparison.Ordinal)); Console.WriteLine(s.Equals("Straße", StringComparison.CurrentCulture)); Console.WriteLine(s.Equals("straße", StringComparison.OrdinalIgnoreCase)); // outputs True: Console.WriteLine(s.Equals("straße", StringComparison.CurrentCulture)); Console.WriteLine(s.Equals("Straße", StringComparison.CurrentCultureIgnoreCase)); بالتالي الطريقة العملية الآمنة للمقارنة بين النصوص هي تزويد التابع Equals بالمعامل comparisonType دائمًا، وفيما يلي بعض الإرشادات الأساسية: استخدم المقارنة الحساسة للاختلافات بين اللغات عند مقارنة النصوص المدخلة من المستخدم أو التي ستعرض على المستخدم (CurrentCulture أو CurrentCultureIgnoreCase). استخدم المقارنة الترتيبية عند مقارنة النصوص المسندة برمجيًا (Ordinal أو OrdinalIgnoreCase). لا تستخدم المعاملين InvariantCulture و InvariantCultureIgnoreCase إلا في حالات محدودة جدًا لأن المقارنات الترتيبية في هذه الحالة أكثر فعالية. ملاحظة: هناك تابع آخر لمقارنة النصوص بالإضافة إلى التابع Equals وهو التابع Compare الذي يقدم لك معلومات جيدة حول الترتيب النسبي للسلاسل النصية، وهو مناسب عند استخدام عوامل المقارنة < و=> و=<. الخطأ 4: استخدام العبارات التكرارية بدل التصريحية لمعالجة المجموعات لقد غيرت استعلامات LINQ من طريقة الاستعلام عن المجموعات Collections ومعالجتها بشكل جذري، ولا يجوز استخدام العبارات التكرارية ضمن لغة الاستعلام LINQ. ومع ذلك لازال هناك عدد قليل من مبرمجي #C لا يعرفون بوجود لغة الاستعلام LINQ أو يعتقدون أن استخدامها يقتصر على التعامل مع قواعد البيانات بسبب التشابه الكبير بينها وبين لغة SQL. تعمل تعليمات LINQ على أي مجموعة قابلة للتعداد enumerable بغض النظر عن نوع الكائن أو مجموعة البيانات التي تستخدمها، فإذا كانت البيانات قابلة للتعداد -أي يمكن المرور عبر العناصر داخلها واحدًا تلو الآخر- فيمكنك استخدام LINQ للقيام بعمليات استعلام وتصفية وتحليل لهذه البيانات. على سبيل المثال إذا كان لدينا مصفوفة تخزن حسابات مستخدمين فيمكن استعراضها من خلال التعليمة foreach كما يلي: decimal total = 0; foreach (Account account in myAccounts) { if (account.Status == "active") { total += account.Balance; } } يمكننا كتابة الكود التالي بدلًا من الكود السابق: decimal total = (from account in myAccounts where account.Status == "active" select account.Balance).Sum(); المثال السابق هو مثال بسيط جدًا لتجنب الوقوع في مثل هذه المشكلة في #C، ولكن عند الانتقال إلى أمثلة أو حالات معقدة فيمكن لعبارة واحدة من عبارات LINQ أن تحل مكان العشرات من عبارات الحلقات التكرارية أو الحلقات المتداخلة في الكود. بالطبع عليك استخدام القرار المناسب لبرنامجك سواء استخدام عبارات لغة الاستعلام المتكاملة LINQ أو استخدام العبارات التكرارية، هذا يعتمد على أداء البرنامج لذا يمكنك إجراء مقارنة لأداء الطريقتين واختيار الأنسب. الخطأ 5: الفشل في فهم الكائنات الأساسية في عبارة LINQ لغة الاستعلامات التكميلية اللغوية LINQ مهمة جدًا في معالجة مجموعات البيانات سواء كانت هذه البيانات كائنات في الذاكرة، أو في جداول قاعدة البيانات، أو حتى في ملفات XML، عادة لا يهمنا معرفة الكائنات الأساسية objects التي تعالجها تعليمات هذه اللغة، ولكن عمليًا يمكن لاستعلامات LINQ المتطابقة أن ترجع نتائج مختلفة عند تطبيقها على البيانات نفسها إذا اختلف تنسيق تلك البيانات. لاحظ الكود التالي: decimal total = (from account in myAccounts where account.Status == "active" select account.Balance).Sum(); السؤال المطروح هنا ماذا يحدث عندما تكون قيمة account.Status لأحد الكائنات "Active" أي الحرف A كبير؟ الجواب هو كالتالي: إذا كانت المصفوفة myAccounts كائن من النوع DbSet وهو بشكل افتراضي غير حساس لحالة الأحرف فستبقى عبارة where مطابقة لحالة العنصر وتكون النتيجة النهائية صحيحة، أما إذا كانت المصفوفة myAccounts موجودة في الذاكرة فلن تتطابق مع حالة العنصر وتكون النتيجة خطأ. قد يتبادر لذهنك سؤال آخر: ذكرنا أن العامل == يجري مقارنة حساسة لحالة الأحرف في السلاسل النصية، فلماذا قام هنا بمقارنة غير حساسة للأحرف؟ الجواب عندما تكون الكائنات التي تتعامل معها عبارات LINQ مرتبطة بجدول SQL مثل كائن DbSet في إطار عمل الكيانات Entity Framework، سيحوَّل الاستعلام إلى عبارة T-SQL وفي هذه الحالة يتبع العامل == قواعد لغة SQL وهي غير حساسة لحالة الأحرف، ولن يتبع قواعد #C. لذلك تصبح المقارنة غير حساسة لحالة الأحرف. إذًا تستخدم لغة LINQ لتبسيط عملية الاستعلام عن البيانات ومعالجتها بغض النظر عن مصدرها، ولكن الفهم الخاطئ للكائنات التي تطبق عليها قد يتسبب بنتائج غير متوقعة، لذا يتوجب فهم كيفية تنفيذ LINQ بناءً على نوع مصدر البيانات. وإذا كانت العبارات التي تكتبها ستترجم إلى لغة أخرى غير #C ضمن إطار العمل عليك تأكد من عمل البرنامج بشكل صحيح عند التنفيذ. الخطأ 6: الغموض عند استخدام توابع التمديد كما ذكرنا سابقًا، تعمل لغة الاستعلام LINQ على أي كائن من النوع IEnumerable فعند استخدام استعلامات LINQ أو توابعها على كائن من النوع IEnumerable، فقد يبدو أن هذه التوابع جزء من تعريف الواجهة IEnumerable نفسها. على سبيل المثال ستعمل الدالة البسيطة Sum في الكود التالي على إضافة الأرصدة إلى مجموعة من الحسابات كما يلي: public decimal SumAccounts(IEnumerable<Account> myAccounts) { return myAccounts.Sum(a => a.Balance); } وسيط الدالة myAccounts في هذا الكود من النوع <IEnumerable<Account، وبما أن myAccounts يشير للتابع ()Sum فقد تتوقع أن التابع معرف ضمن الواجهة <IEnumerable<T، لكن تعريف الواجهة <IEnumerable<T لا يتضمن التابع Sum ويبدو كما يلي: public interface IEnumerable<out T> : IEnumerable { IEnumerator<T> GetEnumerator(); } فالسؤال المطروح هنا أين يتواجد تعريف التابع ()Sum؟ فكما شرحنا في فقرات سابقة لغة #C هي لغة صارمة في تحديد الأنواع، وإذا كانت الإشارة للتابع Sum غير صحيحة فإن مصرّف لغة #C سيعلن عن وجود الخطأ ولن ينفذ الكود، لكن هذه الفرضية خاطئة والتابع موجود ولكن أين؟ أين توجد تعريفات جميع التوابع الأخرى التي تزودها لغة LINQ؟ الجواب هو أن التابع ()Sum غير معرف ضمن الواجهة <IEnumerable<T وإنما هو تابع ثابت يدعى تابع التمديد Extension Method وهو معرّف ضمن الصنف System.Linq.Enumerable كما هو موضح تاليًا: namespace System.Linq { public static class Enumerable { ... // الإشارة إلى this IEnumerable<TSource> source // هي التي توفر الوصول إلى تابع التمديد Sum public static decimal Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal> selector); ... } } السؤال الآن: لماذا يختلف تابع التمديد عن أي تابع ثابت آخر، وكيف يمكن الوصول إليه من أصناف أخرى؟ الجواب هو أن أهم ما يميز تابع التمديد هو وجود المتغير this ضمن المعامل الأول للتعريف، فهو بمثابة المفتاح السحري الذي يخبر مصرّف اللغة أن هذا التابع هو تابع تمديد، ويشير هذا النوع من المعاملات إلى الصنف أو الواجهة وهي في حالتنا IEnumerable<TSource> وبهذا، يمكن الوصول إلى التابع وكأنه جزء من الكائن نفسه، رغم تعريفه في الواقع في مكان آخر. ملاحظة: التشابه بين اسم الواجهة IEnumerable واسم الصنف Enumerable الذي يحتوي على تابع التمديد هو مجرد تشابه في الأسماء فقط ولا علاقة مباشرة بينهما. يمكننا وباعتماد هذا الفهم أن نكتب التابع sumAccounts كما يلي: public decimal SumAccounts(IEnumerable<Account> myAccounts) { return Enumerable.Sum(myAccounts, a => a.Balance); } في الحقيقة كان بإمكاننا تطبيق هذه الطريقة بدلًا من الطريقة السابقة مما يدفعنا إلى سؤال جديد عن سبب وجود تابع التمديد والفائدة منه؟ الإجابة هي أن تابع التمديد مناسب بشكل كبير للغة البرمجة #C ويسمح بإضافة توابع إلى أنواع موجودة مسبقًا دون الحاجة إلى تعديل تلك الأنواع أو إنشاء أنواع جديدة. يمكن تضمين تابع التمديد في نطاق الكود عن طريق إضافة العبارة using [namespace] في بداية الملف. ومن السهل معرفة المجالات التي تحتوي على توابع التمديد التي تحتاجها في #C. فعندما يستدعي مصرف #C تابعًا لم يتم تعريفه في الكائن، سيبحث في توابع التمديد ضمن المجالات المستوردة. وإذا وجد التابع، يستخدمه ويمرر الكائن كمعامل أول ثم يمرر المعطيات الأخرى. وإذا لم يجد التابع، سيعطي خطأ. تعد توابع التمديد ميزة في #C لكونها تساعد في كتابة كود أسهل وأوضح وتحقق جمالية الصياغة syntactic sugar وتجعل الكود أسهل في القراءة والفهم. ولكن، إذا لم تكن معتادًا على استخدامها قد تشعر بالارتباك في البداية. لكن مع الوقت والخبرة ستعتاد استخدامها والتعامل معها فقد أصبح استخدام توابع التمديد شائعًا في مكتبات #C البرمجية، مثل مكتبة LINQ، ومحرك الألعاب Unity، وإطار Web API، وغيرها من المكتبات. وكلما كان الإطار البرمجي حديثًا، زادت احتمالية استخدامه لهذه التوابع. وبالرغم من المميزات التي توفرها هذه التمديد إلا أنها قد تتسبب ببعض الاخطاء في الكود لاسيما عند استخدام نماذج كود الجاهزة على الإنترنت أو أكواد جاهزة مكتوبة مسبقًا وتحتاج كمطور لفهمها بشكل جيد لتجاوز هذه الأخطاء. فعندما ينتج مصرف الكود أخطاء لأنه يستدعي توابع غير معرفة ضمن الأصناف المستدعاة فمن المرجح أن هذا يحدث بسبب تطبيق هذا الكود على إصدار مختلف من المكتبة أو على مكتبة أخرى مختلفة تمامًا مما يجعل المطورين يقضون الكثير من الوقت في البحث عن المكتبة المناسبة والإصدار المناسب للكود، وقد يواجه المطورون بعض الأخطاء عند استخدام توابع التمديد خصوصًا عندما يكون اسم التابع مشابهًا لاسم الكائن. في هذه الحالة، قد يحدث لبس بين التابع العادي وتابع التمديد وقد يكون الخطأ ناتجًا عن عدم التمييز بينهما. يمكنك أيضًا كتابة توابع التمديد الخاصة بك، ولكن انتبه جيدًا لأنه وبالرغم من أن توابع التمديد تُستدعى بنفس طريقة استدعاء التوابع العادية، إلا أن هناك اختلافًا في التنفيذ، فلا يمكن لتوابع التمديد على سبيل المثال الوصول إلى العناصر الخاصة private أو المحمية protected في الصنف. لذا لا يمكن لتوابع التمديد أن تحل محل الأصناف الأساسية التقليدية التي تستخدم الوراثة. الخطأ 7: اختيار نوع المجموعة غير المناسب للمهمة المطلوبة توفر لغة البرمجة #C الكثير من المجموعات collection وفيما يلي بعض منها: المصفوفة Array المصفوفة الديناميكية ArrayList جدول التعمية HashTable القاموس <Dictionary<K,V القائمة <List<T المكدس Stack الرتل Queue القائمة المرتبة SortedList القاموس النصي StringDictionary أحيانًا قد يكون وجود العديد من الخيارات أمام المطور محيرًا، ولكن عندما يتعلق الأمر بكائنات المجموعات، فإن تعدد الخيارات ميزة لكونها تمنحه القدرة على اختيار النوع الأنسب لاحتياجات برنامجه وتحقيق أداء أفضل وأخطاء أقل، فإذا كان هناك نوع مجموعات مصمم خصيصًا للتعامل مع نوع معين من البيانات، فمن الأفضل لك استخدامه فهو مصممة للعمل بكفاءة أكبر مع هذا النوع من البيانات. ولتجنب الأخطاء وضمان أمان الكود، استخدم المجموعات المعممة Generic Interfaces في #C التي تستلزم تعيين نوع العناصر التي ستتعامل معها المجموعة عند التصريح عنها وتتعامل مع نوع محدّد بدلاً من المجموعات غير المعممة Non-generic Interfaces التي لا تحدد نوع العناصر وتجعل عملية التحقق من صحة الأنواع صعبة على مصرف #C. فعند التعامل مع أنواع البيانات الأساسية مثل السلاسل النصية أو الأعداد الصحيحة أو الأعداد الحقيقية، سيؤدي استخدام مجموعة غير معممة إلى عدم تحديد نوع البيانات التي ستحتفظ بها المجموعة.وبالتالي عندما تضيف أو تسترجع العناصر، سيحول مصرف اللغة البيانات إلى نوع الكائن العام، وهذا يسمى التعليب boxing وإلغاء التعليب unboxing. ويمكن أن تؤثر هذه العمليات على أداء البرنامج بشكل كبير مقارنة باستخدام مجموعة معممة تحتوي على نوع البيانات المناسب. ولا تحاول كتابة كود خاص لإنشاء كائنات المجموعات بنفسك، فإطار العمل .NET يوفر بالفعل العديد من المجموعات الجاهزة التي توفر الكثير من الوقت والجهد كما تقدم مكتبة C5 في #C مجموعة من الأدوات وهياكل البيانات المتقدمة التي تسهل التعامل مع مجموعات البيانات مثل الأشجار الدائمة persistent trees وارتال الأولوية priority queues والقوائم المرتبطة linked lists وغيرها.. فهذه المجموعات تساعد في تنظيم البيانات وتسهل التعامل مع إضافتها وحذفها واسترجاعها. الخطأ 8: إهمال تحرير الموارد غير المستخدمة تستخدم بيئة تنفيذ CLR أداة كنس المهملات Garbage Collector لإدارة الذاكرة تلقائيًا، فلست بحاجة لتحرير الذاكرة التي حجزتها عند إنشاء الكائنات. وعلى عكس لغات البرمجة مثل لغة ++C التي تحتوي على العامل delete أو لغة C التي تستخدم التابع free لتحرير الذاكرة، لا تتضمن لغة #C شيئًا مشابهًا. هذا لا يعني ذلك أن بإمكانك ترك مهمة حذف الكائنات لكانس المهملات بمجرد الانتهاء من استخدامها فبعض الكائنات قد تحتوي على موارد إضافية مثل ملفات على القرص، أو اتصالات بقواعد البيانات أو منافذ الشبكة، ويجب عليك تحرير هذه الموارد يدويًا لأن تركها دون تحرير يمكن أن يؤدي إلى نفاد موارد النظام وقد يسبب مشاكل في الأداء وأخطاء في البرنامج. على الرغم من إمكانية تعريف تابع الهدم destructor في أي صنف من أصناف #C، فإن المشكلة تكمن في أننا لا نعرف متى يستدعى التابع بدقة فهو يستدعى من كانس المهملات عبر خيط thread منفصل وهذا قد يسبب مشكلات إضافية، لأن توقيت الاستدعاء غير معروف، ولحل لهذه المشكلة يمكن استخدام كانس المهملات بصورة قسرية باستخدام التابع ()GC.Collect لكن هذه الطريقة غير عملية أيضًا لأنها ستوقف مجموعة من الخيوط threads عن التنفيذ لفترة غير محددة ريثما يتم جمع البيانات غير المهمة. في الواقع توجد عدة استخدامات مفيدة لتابع الهدم في لغة #C، ولكن استخدامه لإجبار تحرير الذاكرة ليس أحد هذه الاستخدامات. يحتوي إطار العمل .NET على واجهة باسم IDisposable تتضمن تابعًا واحدًا فقط يسمى Dispose(). وأي كائن ينفذ هذه الواجهة سينفذ تابع Dispose() الذي يساعد في تحرير الموارد بفعالية. فعندما تريد إنشاء وتحرير كائن object في كتلة الكود نفسها استخدم التابع ()Dispose بشكل أساسي، ويمكنك استخدام العبارة using للتأكد من استدعاء ()Dispose بغض النظر عن الطريقة التي ستخرج فيها من الكود سواء بسبب وقوع استثناء أو بسبب عبارة return أو إغلاق كتلة الكود، والعبارة using المذكورة هنا هي نفس تلك المستخدمة لتضمين فضاء الأسماء namespace أعلى ملف #C، وتجدر الإشارة هنا بأن للعبارة using استخدامًا آخر لا يعرفه الكثير من المطورين وهو ضمان استدعاء التابع ()Dispose للكائن عند الخروج من كتلة الكود كما في المثال التالي: using (FileStream myFile = File.OpenRead("foo.txt")) { myFile.Read(buffer, 0, 100); } في المثال السابق وبعد إنشاء العبارة using، سيتأكد المطور أنه التابع ()myFile.Dispose سيستدعى بمجرد انتهاء العمل بالملف سواء أصدر التابع ()Read استثناء أو لم يصدر. الخطأ 9: تجنب الاستثناءات تطبق لغة البرمجة #C مبدأ يسمى أمان النو ع Type safety أثناء تشغيل البرنامج، وهو يساعد المطورين على اكتشاف الأخطاء بسرعة أكبر مقارنة ببعض اللغات الأخرى مثل ++C التي قد يؤدي التحويل الخاطئ بين أنواع البيانات فيها إلى إسناد قيم غير صحيحة أو عشوائية للمتغيرات. لكن مبرمجي #C لا يستفيدون بشكل كامل من هذه الميزة، مما يؤدي إلى بعض الأخطاء في الكود. فلغة #C تقدم طريقتين لمعالجة الأخطاء: الأولى تعتمد على إصدار استثناءات try/catch`، والثانية لا تستخدم الاستثناءات لإدارة الأخطاء وتتعامل معها بطريقة أخرى مثل التحقق من القيم أو التأكد من صحة المدخلات قبل إجراء العمليات عليها. فبعض المبرمجين يتجنبون استخدام الاستثناءات معتقدين أن ذلك يقلل من حجم الكود، لكن هذا قد يؤدي إلى تجاهل الأخطاء المهمة. على سبيل المثال، هناك طريقتان مختلفتان للتحويل القسري بين أنواع البيانات أي لتحويل الكائنات بين أنواع البيانات المختلفة كما في الكود التالي: // طريقة1: // سيرمى استثناء إذا لم نتمكن من تحويل account إلى SavingsAccount SavingsAccount savingsAccount = (SavingsAccount)account; // طريقة2: // لن يرمى استثناء في حال لم نتمكن من التحويل // سيتم تعيين savingsAccount إلى null بدلاً من ذلك SavingsAccount savingsAccount = account as SavingsAccount; الخطأ الأكبر الذي قد يحدث عند استخدام الطريقة الثانية في الكود السابق هو نسيان التحقق من القيمة المرجعة. إذا كانت القيمة null، ولم تتحقق منها، فقد يؤدي ذلك إلى حدوث استثناء من النوع NullReferenceException في وقت لاحق، مما يصعب عليك تحديد السبب الفعلي للمشكلة. أما في الطريقة الأولى، فسيطلق استثناء مباشر من نوع InvalidCastException إذا كان التحويل غير ممكن ممل يساعدك على تحديد مكان المشكلة ومعرفتها بسرعة. بالنسبة للطريقة الثانية، حتى إذا قام المبرمج بالتحقق من القيمة المرجعة، فإن المشكلة تبقى إذا كانت القيمة فارغة null. فماذا يفعل المبرمج في هذه الحالة؟ هل من الأفضل له معالجة هذه الأخطاء داخل التابع؟ وهل هناك طريقة أخرى للتعامل مع فشل عملية التحويل؟ إذا كانت الإجابة لا على هذه الأسئلة، فإن أفضل خيار هو إطلاق استثناء مباشرة عند حدوث المشكلة. هذا يسهل التعامل مع الأخطاء في وقت حدوثها بدلاً من انتظار حدوث مشاكل لاحقة. لاحظ المثال التالي حيث ستصدر الطريقة الأولى استثناء أما الطريقة الثانية فلا تصدره: int.Parse(); // يرمي استثناء إذا لم يكن بالإمكان تحويل الوسيط int.TryParse(); // يعيد قيمة من نوع bool للدلالة على ما إذا كان التحويل قد نجح IEnumerable.First(); // يرمي استثناء إذا كانت السلسلة فارغة IEnumerable.FirstOrDefault(); // يعيد null أو القيمة الافتراضية إذا كانت السلسلة فارغة يكره بعض مطوري لغة #C الاستثناءات ويعتقدون أن الطرق التي لا تطلق استثناءات هي الأفضل، لكن بالطبع لا يمكن تعميم هذا الحكم على جميع الحالات. فهناك مواقف معينة قد يكون فيها هذا الرأي صحيحًا، ولكن ليس دائمًا. إذا كان لدينا خيار بديل لاستبدال إطلاق استثناء في حالة معينة، وكان هذا الخيار مناسبًا وصحيحًا، فمن الأفضل استخدامه. ولتوضيح ذلك، يمكننا كتابة الكود التالي: if (int.TryParse(myString, out myInt)) { // استعمل myInt } else { // استعمل القيمة الافتراضية } بدلًا من الكود التالي الذي يطلق استثناء: try { myInt = int.Parse(myString); // استعمل myInt } catch (FormatException) { // استعمل القيمة الافتراضية } لا يمكن اعتبار تابع التحويل TryParse في المثال السابق الطريقة الأفضل فقد يكون هذا الكلام صحيحًا في بعض الحالات وقد يكون غير صحيح في حالات أخرى، لذا عليك اختيار الطريقة المناسبة حسب الحالة. الخطأ 10: السماح بتراكم تحذيرات المصرّف هذه المشكلة شائعة في جميع لغات البرمجة، لكنها أكثر خطورة في لغة #C لأنها تؤدي إلى تجاهل المصرف لميزة التحقق الصارم من النوع وهذا التحقق مهم في #C. والتحذيرات أو التنبيهات warning هي رسائل يصدرها المترجم ليخبرك بوجود مشاكل محتملة في الكود، لكنها لا تمنع الكود من التنفيذ. أما الأخطاء، فهي تشير إلى مشكلة واضحة يجب على المبرمج إصلاحها. الفرق هو أنه عندما يحصل الكود على تحذير، سينفذ الكود لكنه ينبهك لاحتمالية حدوث شيء غير طبيعي، بينما الأخطاء تمنع الكود من العمل نهائيًا. من أبسط الأمثلة على هذا النوع من الأخطاء هو نسيان حذف السطر الذي عرفت فيه متغير بعد تعديل خوارزمية ما وتوقفها عن استخدام هذا المتغير. في هذه الحالة، سيعمل البرنامج بشكل طبيعي، لكن سيصدر المترجم تحذيرًا بأن هذا المتغير غير مستخدم في الكود وغالبًا ما يتجاهل المبرمجون هذا التحذير ولا يعالجونه، كما يخفي معظم المبرمجين التحذيرات في النافذة Error List الموجودة في بيئة التطوير Visual Studio ويهتمون فقط بمعالجة الأخطاء مما يؤدي إلى تراكم هذه التحذيرات وهذا هو الخطأ بعينه. في حال تجاهل التحذيرات فإننا سنرى في القريب العاجل شيء يشبه الكود التالي: class Account { int myId; int Id; // يطلق المصرف تحذير هنا لكنك لم تنتبه له // الباني Account(int id) { this.myId = Id; // خطأ } } يوجد خطأ كبير في الكود السابق بالرغم من أن مصرف اللغة وضعه كتحذير فقط وهو تعيين قيمة المتغير Id للمتغير myId باستخدام الأمر this.myId=Id والذي يجب أن يكون this.myId=id، إذا تجاهلت التحذير ستضيع الكثير من الوقت في تتبع مثل هذا الخطأ حسب درجة تعقيد البرنامج، أما إذا عالجته من البداية فستكتشف الخطأ بوقت قصير. فيجب عليك كمبرمج عدم تجاهل التحذيرات التي تستغرق بضع ثوانٍ لحلها في بداية ظهورها، في حين ستحتاج مع تقدمك في البرنامج إلى ساعات لمعرفة سبب التحذيرات وحلها، تأكد اولًا بأول من أن النافذة Error List الموجودة في بيئة التطوير Visual Studio لا تحتوي أي أخطاء أو تحذيرات وعالجها على الفور. من المعروف أن لكل قاعدة استثناءات، ففي بعض الأحيان قد تحتاج إلى كتابة كود يصدر تحذير ولكنك ستتجاهل هذا التحذير ليتنفيذ البرنامج بالطريقة التي تريدها. في هذه الحالات النادرة، يمكنك استخدام الأمر [pragma warning disable [warning id# في الكود نفسه الذي يصدر التحذير ليتوقف عن تشغيل التحذير المحدد، مع إمكانية تنبيهك لأي تحذيرات جديدة إذا كانت هناك حاجة لذلك. الخلاصة تعد لغة البرمجة #C من اللغات القوية والمرنة التي تساعد المطورين على تحسين أدائهم وإنتاجيتهم. ولكن الفهم المحدود لأساسيات اللغة سيعيق تطورك ويجعلك تكتب برامج ضعيفة تحتوي على أخطاء من الصعب معرفة سببها. نرجو أن يكون هذا المقال قد ساعدك على التعرف على التفاصيل الدقيقة في أساسيات #C وتجنب الأخطاء الشائعة والتحديات التي قد يواجهونها عند البرمجة بهذه اللغة. ترجمة وبتصرف للمقال Buggy C# Code: The 10 Most Common Mistakes in C# Programming لكاتبه Patrick Ryder. اقرأ أيضًا الاستثناءات ومعالجة الأخطاء في لغة سي شارب #C كيفية التعامل مع الأخطاء البرمجية الأخطاء العشرة الأكثر شيوعًا في شيفرة بايثون Python البرمجية الأخطاء العشرة الأكثر شيوعًا في شيفرة PHP
-
هناك العديد من العقبات والأخطاء التي قد يتعرض لها مطورو لغة البرمجة ++C، يمكن لهذه العقبات أن تصعب عملية البرمجة وترفع تكاليفها، فتعلم قواعد البرمجة ووجود مهارات في لغات البرمجة الأخرى مثل جافا Java وسي شارب #C ليست كافية وحدها للاستفادة الكاملة من إمكانيات لغة ++C وتجنب الأخطاء. حيث يتطلب إتقان ++C عدة سنوات من الخبرة العملية لتجنب الوقوع في الأخطاء الدقيقة بسبب عدة عوامل متعلقة بطبيعة اللغة، وسنلقي الضوء في هذه المقالة على أهم الأخطاء التي يقع بها مطورو ++C سواء المبتدئون منهم أو الخبراء إن لم يكونوا حذرين في التعامل مع هذه اللغة. الخطأ 1: الاستخدام الخاطئ للمعاملين new و delete بداية سنشرح مشكلة شائعة في إدارة الذاكرة في لغة ++C، فمن الصعب جدًا -مهما حاولنا- تحرير كل الذاكرة المخصصة ديناميكيًا، حتى ولو نجحنا في ذلك ستبقى النتيجة غير آمنة وتصدر الاستثناءات أثناء التنفيذ. سنلقي الضوء هنا على المثال البسيط التالي: void SomeMethod() { ClassA *a = new ClassA; SomeOtherMethod(); // it can throw an exception delete a; } إذا أصدر هذا الكود البسيط استثناء فلن يتم حذف الكائن a أبدًا. ففي هذا الكود تنشئ الدالة SomeMethod كائن من الصنف ClassA باستخدام التخصيص الديناميكي للذاكرة new ClassA. بعد ذلك، تستدعي الدالة SomeOtherMethod والتي قد تتسبب في حدوث استثناء أثناء تنفيذها. فإذا وقع هذا الاستثناء قبل تنفيذ delete a، فلن يتم تحرير الكائن `` a من الذاكرة، مما يؤدي إلى ما يعرف بتسريب الذاكرة Memory Leak. يقدم المثال التالي طريقة أقصر وأكثر أمانًا لتنفيذ هذه المهمة، حيث نستخدم هنا طريقة المؤشر الذكي auto_ptr الذي لم يعد موجودًا في النسخة 11 من لغة البرمجة ++C ولكنه لا يزال يستخدم على نطاق واسع في النسخ القديمة. ملاحظة: يمكن استبدال المؤشر auto_ptr في النسخة 11 من ++C بالمؤشر unique_ptr أو المؤشر scoped_ptr من خلال المكتبة Boost التي توفر بعض الأدوات المفيدة لإدارة الذاكرة. void SomeMethod() { std::auto_ptr<ClassA> a(new ClassA); // deprecated, please check the text SomeOtherMethod(); // it can throw an exception } هنا سيحذف الكائن a مباشرة بمجرد انتهاء البرنامج من تنفيذ الكود الموجود بين قوسين. هذا المثال هو الأبسط للمشكلات التي يمكن تحدث في ++C، فهناك العديد من الأمثلة التي يجب فيها أن يتم الحذف في مكان آخر (مثال: دالة خارجية أو خيط thread آخر)، لذلك لا ينصح أبدًا باستخدام المعاملين new وdelete مع بعضهما البعض وإنما يجب استخدام المؤشرات الذكية بدلًا من ذلك. الخطأ 2: عدم استخدام الهادم الوهمي Virtual Destructor هذا من الأخطاء الأكثر شيوعًا التي تؤدي إلى استهلاك حجم الذاكرة داخل الأصناف المشتقة من أصناف أخرى، فعند إنشاء صنف أساسي base class وصنف مشتق derived class يحتوي على ذاكرة مخصصة، فمن الضروري أن تكون دالة الهدم في الصنف الأساسي وهمية virtual لأن ++C تعتمد على نوع المؤشر عند استدعاء الدوال، وبالتالي إذا حذفنا كائنًا من خلال مؤشر إلى صنف أساسي لا يحتوي على دالة هادم وهمية فلن تُستدعى دالة الهدم للصنف المشتق عند حذف الكائن. وهذا يؤدي إلى عدم تحرير الذاكرة بشكل صحيح وظهور مشكلة تسرب الذاكرة. هناك بعض الحالات التي لا يرغب فيها بوجود دالة الهدم الوهمية virtual destructor، على سبيل المثال عندما يكون الصنف لا يقبل الوراثة وحجمه وأداؤه في غاية الأهمية، حيث تضيف دالة الهدم الوهمية -أو أي دالة وهمية أخرى- بيانات إضافية لبنية الصنف بمعنى أنها تضيف مؤشر إلى جدول وهمي مما يؤدي إلى زيادة حجم الصنف class. ولما كان من الممكن توريث الأصناف في معظم الحالات -حتى وإن لم نكن نريد توريثها- لذا يفضل أن يضيف المبرمج دالة الهدم الوهمية عند التصريح عن الصنف class. من ناحية أخرى، في حال عدم رغبة المبرمج بإضافة أي دوال وهمية للكود لتعزيز أداء البرنامج فيتوجب عليه وضع ملاحظة عند التصريح عن الصنف تخبر باقي المطورين بأن هذا الصنف يجب أن لا يوسع عن طريق الوراثة منه. ومن أفضل الخيارات لتجنب الوقوع في هذه المشكلة هو استخدام بيئة التطوير المتكاملة IDE التي تدعم إنشاء دالة هدم افتراضية أثناء عملية إنشاء الصنف. هناك نقطة إضافية أخرى في هذا الموضوع وهي الأصناف والقوالب التي تكون موجودة ضمن المكتبة بشكل ضمني built -in فلا يمكن توريث هذه الأصناف كما أنها لا تحتوي على هادم وهمي افتراضي، على سبيل المثال إذا أنشأنا صنف سلسلة نصية string class فإنه سيرث تلقائيًا من الصنف std::string وهنا تكمن المشكلة ففي حال استخدم أحد المطورين هذا الصنف كمؤشر أو مرجع للصنف std::string فإن هذا سيؤدي إلى تسريب الذاكرة. class MyString : public std::string { ~MyString() { // ... } }; int main() { std::string *s = new MyString(); delete s; // May not invoke the destructor defined in MyString } لتجنب الوقوع في مثل هذه المشكلات في لغة ++C، يجب على المطورين استخدام الوراثة الخاصة private inheritance والتي تعتبر الطريقة الأكثر أمانًا لإعادة استخدام الأصناف أو القوالب من المكتبة الأساسية القياسية. الخطأ 3: حذف مصفوفة باستخدام delete أو باستخدام المؤشر الذكي ستحتاج في كثير من التطبيقات العملية إلى إنشاء مصفوفات مؤقتة بحجم متغير وهو ما يسمى المصفوفات المخصصة ديناميكيًا، ويتطلب التعامل مع هذه المصفوفات عناية خاصة لتحرير الذاكرة بعد الانتهاء من استخدامها. فمن المهم جدًا تحرير الذاكرة المخصصة لهذه المصفوفات بعد الانتهاء من استخدامها، ولكن المشكلة الكبيرة في لغة ++C هي أنها تتطلب عامل الحذف ذي الأقواس المربعة delete[] والذي ينساه المبرمج غالبًأ ، وعامل الحذف لن يقوم بحذف الذاكرة المخصصة للمصفوفة فحسب وإنما سيستدعي في البداية تابع الهدم destructor لجميع الكائنات أو العناصر ضمن المصفوفة. بالمقابل فإن استخدام أمر الحذف بدون أقواس مربعة [] أمر غير صحيح خاصة بالنسبة للأنواع الأساسية الأولية على الرغم من عدم وجود تابع هدم لهذه الأنواع، لذا يجب الانتباه لاستخدام الأقواس لضمان تحرير الذاكرة بشكل صحيح. وعند تخصيص مصفوفة ديناميكيًا، يتأكد المصرف compiler عادة من أن المؤشر يشير إلى أول عنصر في المصفوفة ويمكننا الوصول إلى باقي العناصر عن طريق التحرك من خلال هذا المؤشر.لكن لا يضمن كل مترجم وضع المؤشر على العنصر الأول من المصفوفة ولذلك فإن استخدام الكلمة المفتاحية delete بدون أقواس يمكن أن يؤدي إلى سلوك غير متوقع عند تحرير الذاكرة المخصصة للمصفوفة. إن استخدام المؤشرات الذكية مثل auto_ptr و<unique_ptr<T و shared_ptr أمر غير صحيح أيضًا فعندما يخرج المؤشر الذكي خارج المجال فإنه سيستدعي عامل الحذف بدون أقواس مما يؤدي إلى حدوث المشكلة نفسها التي شرحناها في الفقرة السابقة. عندما يكون استخدام المؤشر الذكي للمصفوفة أمرًا مطلوبًا، توجب على البرنامج استخدام الصنف scoped_array أو الصنف shared_array من المكتبة Boost أو الصنف <[]unique_ptr<T بالتحديد. إذا كنا غير محتاجين لاستخدام تقنية إدارة الذاكرة المسماة عد المراجع reference counting عند التعامل مع المصفوفات، فإن الحل الأفضل في هذه الحالة هو استخدام متجهات مكتبة القوالب المعيارية STL بدلًا عنها لأن تلك المتجهات لا تتعامل مع تحرير الذاكرة فحسب وإنما تحتوي على وظائف إضافية مهمة لا مجال لذكرها الآن. الخطأ 4: إعادة مرجع إلى كائن محلي داخل دالة هناك خطأ شائع يرتكبه المطورون المبتدئون عند التعامل مع المراجع في دوال ++C، وهو إرجاع مرجع إلى كائن محلي أنشأه داخل الدالة. يحدث هذا الخطأ غالبًا من قبل المطورين المبتدئين عند محاولة تحسين الأداء عن طريق تجنب النسخ غير الضروري للكائنات وسنلقي الضوء عليه نظرًا لكمية التعليمات البرمجية القديمة التي تعاني من هذه المشكلة. لنأخذ المثال التالي والذي يريد المبرمج من خلاله إدخال بعض التحسينات على الكود وتجنب عمليات النسخ غير الضرورية: Complex& SumComplex(const Complex& a, const Complex& b) { Complex result; ….. return result; } Complex& sum = SumComplex(a, b); في هذه الحالة سيشير الكائن sum إلى الكائن المحلي result وستعيد الدالة الكائن sum، ولكن السؤال المطروح: أين سيتواجد الكائن result بعد تنفيذ الدالة SumComplex؟. الجواب هو أن الكائن result كان مخزنًا في المكدس، ولكن بعد تنفيذ الدالة وإرجاع القيمة فإن ذاكرة المكدس ستستخدم لتخزين الكائنات والمتحولات المحلية ضمن الدالة ومن ثم ستدمر كل هذه الكائنات، مما سيؤدي في النهاية إلى نتيجة غير متوقعة، وهذا ينطبق حتى على أنواع البيانات الأساسية. هناك حالات يمكنك فيها الاستفادة من تقنية تحسين القيمة المعادة Return Value Optimization أو اختصارًا RVO وهي تقنية يستخدمها المصرف compiler لمنع حدوث مشكلات تتعلق بأداء البرنامج: Complex SumComplex(const Complex& a, const Complex& b) { return Complex(a.real + b.real, a.imaginar + b.imaginar); } Complex sum = SumComplex(a, b); بالنسبة للنسخ الحديثة من المصرفات compilers، عندما تتضمن عبارة إرجاع قيمة باني الكائن constructor فإن المصرف سيعمل على تحسين الكود لقائيًا لتجنب عمليات النسخ الزائدة. فبدلاً من إنشاء كائن مؤقت أولاً داخل الدالة ثم نسخه لاحقًا إلى المكان النهائي مثل المتغير sum في الكود أعلاه، يعمل المترجم على إنشاء الكائن النهائي مباشرة في موقعه المطلوب. هذا يعني أن sum سيبنى مباشرة باستخدام القيم المرجعة من الدالة دون الحاجة إلى كائن وسيط أو عملية نسخ إضافية مما يحسن الأداء ويوفر الذاكرة. الخطأ 5: استخدام مرجع لمورد أو مصدر محذوف تحدث مثل هذه الأخطاء في لغة ++C بشكل كبير وخصوصًا في التطبيقات متعددة الخيوط multithread، وللتوضيح سنأخذ الكود التالي: الخيط رقم 1: Connection& connection= connections.GetConnection(connectionId); // ... الخيط رقم 2: connections.DeleteConnection(connectionId); // … الخيط رقم 1: connection.send(data); في هذا المثال، ستكون نتيجة التنفيذ غير متوقعة في حال استخدم كلا الخيطين نفس المعرف connection ID، وغالبًا ما يجد المطور صعوبة في إيجاد سبب وقوع أخطاء انتهاك الوصول access violation هنا. ففي مثل هذه الحالات وعند وصول عدة خيوط إلى نفس المصدر، سيكون من المخاطرة الاحتفاظ بمؤشرات أو مراجع لهذا المورد لأنه يمكن لمؤشر آخر أن يحذفه، ولتجنب هذا علينا استخدام المؤشرات الذكية مع تقنية العد المرجعي والتي تعتبر الطريقة الأكثر أمانًا مثل المؤشر shared_ptr من المكتبة Boost، إذ يستخدم هذا النوع من المؤشرات العمليات الذرية atomic operations لزيادة أو إنقاص عداد المراجع reference counter مما يجعله آمنًا للاستخدام في بيئة متعددة الخيوط. ملاحظة: يتعقب عداد المراجع في البرمجة عدد المراجع لكائن معين في الذاكرة فكلما تم إنشاء مرجع جديد إلى الكائن يزداد بقيمة 1 وكلما تم حذف مرجع ينقص بقيمة 1. الخطأ 6: السماح برمي توابع الهدم لاستثناءات ليس من الضروري أن يرمي تابع الهدم destructor استثناءً، وحتى لو كان ذلك ضروريًا فيمكن إيجاد عدة طرق أفضل للتعامل مع هذه الحالة. إذ لا تصدر توابع الهدم عادة الاستثناءات بشكل متعمد، وإنما يمكن تسجيل أو معرفة الحدث الذي تسبب في وقوع الاستثناء عند تدمير كائن معين في البرنامج للتعامل معه بشكل صحيح، وللتوضيح سنلقي نظرة على المثال التالي: class A { public: A(){} ~A() { writeToLog(); // could cause an exception to be thrown } }; // … try { A a1; A a2; } catch (std::exception& e) { std::cout << "exception caught"; } إذا حدث الاستثناء في المثال السابق مرتين في نفس الوقت كما في حالة تدمير كلا الكائنين فإن العبارة catch لن تنفذ ابدًا، والسبب في ذلك هو أنه عند وجود استثنائين يعملان على التوازي (في نفس الوقت) سواء كانا من نفس النوع أو من نوعين مختلفين فإن بيئة تشغيل اللغة ++C لن تكون قادرة على تحديد طريقة التعامل معهما، ونتيجة لذلك تستدعي بيئة التشغيل دالة الإنهاء abort مما يؤدي إلى تعطل البرنامج. تنص القاعدة العامة في هذه الحالة على عدم السماح برمي الاستثناءات من توابع الهدم في البرنامج حتى لو لم تبدو التعليمات البرمجية جذابة من الناحية الجمالية، فمن المهم هنا التعامل مع الاستثناءات المحتملة داخل تابع الهدم لمنعها من الانتشار بشكل أكبر try { writeToLog(); // could cause an exception to be thrown } catch (...) {} الخطأ 7: استخدام المؤشر auto_ptr بشكل خاطئ توقف دعم المؤشر أو القالب auto_ptr منذ صدور النسخة 11 من لغة البرمجة ++C وذلك لعدة أسباب (لا داعي لذكرها الآن)، ولكنه لا يزال يستخدم على نطاق واسع لأن معظم المشاريع المطورية سابقًا تعمل في النسخة 98++C. يتمتع القالب auto_ptr بميزة مهمة قد لا يعرفها الكثير من مطوري ++C، وهي أنه عند نسخ كائن من نوع auto_ptr، تنتقل ملكيته من الكائن الأصلي إلى الكائن الجديد. هذا يعني أن الكائن الأصلي يصبح فارغًا ولا يمكن الوصول إلى بياناته أو استخدامه بعد ذلك. إذا لم تستخدم هذا القالب بحذر وبطريقة احترافية، قد يتسبب ذلك في مشكلات كبيرة. للتوضيح ألقِ نظرة على الكود التالي: auto_ptr<ClassA> a(new ClassA); // deprecated, please check the text auto_ptr<ClassA> b = a; a->SomeMethod(); // will result in access violation error سيؤدي الكود السابق إلى خطأ انتهاك الوصول، حيث يحتوي الكائن b فقط على مؤشر للكائن Class A بينما سيكون الكائن a فارغًا، وستؤدي محاولة الوصول إلى الكائن a إلى ظهور خطأ انتهاك الوصول. توجد عدة طرق لاستخدام الكائن auto_ptr بشكل خاطئ لذا يجب على المطور تذكر الأمور الأربعة التالية: 1.يجب عدم استخدام المؤشر auto_ptr ضمن حاويات مكتبة القوالب المعيارية STL، لأن نسخ الحاويات سيؤدي إلى وضع بيانات غير صحيحة في الحاويات الأصلية. بالإضافة إلى ذلك، قد تتسبب بعض خوارزميات مكتبة القوالب المعيارية في حدوث أخطاء في المؤشرات الخاصة بـ auto_ptr. عدم استخدام المؤشر auto_prt كوسيط دالة لأن هذا سيؤدي إلى نسخ القيمة وستصبح القيمة الأصلية الممررة إلى الوسيط خاطئة بعد استدعاء الدالة. إذا استخدم المبرمج المؤشر auto_ptr ضمن بيانات الصنف، يجب عليه التأكد من أن عملية النسخ داخل تابع البناء constructor تنجز بشكل صحيح وذلك إما من خلال إنشاء نسخة صحيحة داخل منشئ النسخة copy constructor وعامل الإسناد assignment operator أو عدم السماح بتطبيق هذه العمليات عن طريق جعلها private مما يمنع النسخ غير المقصود للبيانات. استخدام المؤشرات الذكية الأحدث من المؤشر auto_ptr إذا كان الأمر ممكنًا. الخطأ 8: استخدام المؤشرات والمراجع غير الصالحة هذا الخطأ شائع جدًا حتى أن يمكننا تأليف كتاب كامل حوله، حيث تحتوي كل حاوية من مكتبة القوالب المعيارية STL على بعض الحالات الخاصة التي تبطل عمل المؤشرات والمراجع، ومن المهم لكل مبرمج أن يكون على علم بهذه الحالات عند استخدام المؤشرات والمراجع. يمكن أن تحدث إحدى هذه المشكلات بشكل متكرر في بيئات التطوير التي تمتلك خاصية الخيوط المتعددة multiple threads، ولذلك من الضروري اعتماد آليات المزامنة synchronization mechanisms لمنع حدوث هذه المشكلة. سنلقي نظرة على الكود التالي الذي يستخدم متجه vector لتخزين السلاسل النصية: vector<string> v; v.push_back(“string1”); string& s1 = v[0]; // assign a reference to the 1st element vector<string>::iterator iter = v.begin(); // assign an iterator to the 1st element v.push_back(“string2”); cout << s1; // access to a reference of the 1st element cout << *iter; // access to an iterator of the 1st element للوهلة الأولى ومن وجهة نظر منطقية يتبين لنا أن الكود صحيح ولا يوجد فيه مشكلات، ولكن عند إضافة عنصر ثاني إلى المتجه فسيحتاج لإعادة توضع الذاكرة وهذا يمكن أن يجعل كل من مؤشر ومرجع المتجه غير صالحين (يشيران إلى قيمة أخرى)، ونتيجة لذلك يمكن أن تتسبب محاولة الوصول لهم في السطرين الأخيرين من الكود بعد إضافة العنصر الجديد إلى حدوث خطأ انتهاك الوصول Access Violation لأن المؤشر والمرجع لم يعودا يشيران إلى الأماكن الصحيحة في الذاكرة. الخطأ 9: تمرير كائن عن طريق القيمة من المعروف لدى معظم المبرمجين أن عملية تمرير الكائن عن طريق القيمة by value (وهي عبارة عن أخذ نسخة من الكائن وتمريرها إلى الدالة) فكرة سيئة فقد يكون لها تأثير سلبي على أداء البرنامج، ومع ذلك نجدهم يؤجلون معالجة هذا الموضوع إلى وقت لاحق لتجنب كتابة كود إضافي مما يؤدي تقليل فاعلية الكود المكتوب وجعله يتسبب في لمشكلات غير متوقعة class A { public: virtual std::string GetName() const {return "A";} … }; class B: public A { public: virtual std::string GetName() const {return "B";} ... }; void func1(A a) { std::string name = a.GetName(); ... } B b; func1(b); يصرّف هذا الكود بشكل صحيح وبدون مشكلات، ومع ذلك فإن الدالة func1 ستنسخ جزء من الكائن b الذي ينتمي إلى الصنف A ضمن الكائن a عند استدعائها، وهذا ما يعرف باسم مشكلة التقطيع slicing problem. بالنتيجة سوف يستدعي البرنامج عند تنفيذه التابع من الصنف A بدلًا من الصنف B، ويعتبر هذا سلوكًا غير متوقع للشخص الذي يستدعي الدالة. تحدث مشكلة أخرى مشابهة عند محاولة التقاط catch الاستثناء كما في المثال التالي: class ExceptionA: public std::exception; class ExceptionB: public ExceptionA; try { func2(); // can throw an ExceptionB exception } catch (ExceptionA ex) { writeToLog(ex.GetDescription()); throw; } في المثال السابق، عندما تطلق الدالة func2 استثناء من النوع ExceptionB، سيتلقط الاستثناء ويعالج في كتلة try/catch. ولكن بسبب مشكلة النسخ، ينسخ الاستثناء إلى كائن من النوع الأساسي ExceptionA بدلاً من النوع الفرعي ExceptionB. هذا يؤدي إلى فقدان المعلومات الخاصة بنوع الاستثناء الفرعي مما يتسبب في استدعاء تابع غير صحيح. وعند إعادة رمي الاستثناء باستخدام throw، سيرمى استثناء من النوع ExceptionA بدلاً من النوع الأصلي ExceptionB. النصيحة في نهاية هذه الفكرة هي يجب على المبرمج تمرير الكائنات حسب المرجع reference وليس حسب القيمة وأن يلتقط الاستثناءات بالمرجع & وليس بالقيمة. هذا يحافظ على نوع الاستثناء الفرعي ويمنع المشاكل التي تحدث عند فقدان التفاصيل الخاصة بالاستثناء. الخطأ 10: استخدام التحويلات المعرفة من المستخدم عن طريق توابع البناء وعوامل التحويل تشير التحويلات المخصصة أو المعرفة من قبل المستخدم في ++C إلى إمكانية تعريف تحويلات مخصصة بين أنواع البيانات المختلفة، يمكن إنشاء هذه التحويلات باستخدام توابع البناء constructors وعوامل التحويل conversion operators في الأصناف. وبشكل عام فإن هذا النوع من التحويلات يمنح المبرمج تحكمًا أكبر في طريقة تحويل الكائنات من أنواع مختلفة في كود ++C، وهو مفيد جدًا في بعض الأحيان لكنه قد يؤدي إلى تحويلات غير متوقعة يكون من الصعب تحديد موقعها. لنفترض أن أحدهم أنشأ مكتبة تحتوي على صنف string فيه دالتا بناء لإنشاء السلاسل النصية الأولى وسيطها عدد والثانية وسيطها محارف كما يلي: class String { public: String(int n); String(const char *s); …. } نستطيع من خلال التابع الأول إنشاء سلسلة نصية طولها n، ونستطيع من خلال التابع الثاني إنشاء سلسلة نصية تحتوي على الحروف المدخلة من المستخدم، ولكن تبدأ المشكلة عندما يكون لدينا كود مثل الكود التالي: String s1 = 123; String s2 = ‘abc’; في المثال الأول أعلاه، ستستدعى دالة البناء الأولى التي تنشئ سلسلة نصية s1 حجمها هو 123 محرف وليس سلسلة تحتوي على الأحرف 123، أما المثال الثاني فيحوي على علامات اقتباس مفردة بدلًا من علامات اقتباس مزدوجة والتي ستؤدي إلى استدعاء دالة البناء الأولى أيضًا وإنشاء سلسلة نصية ذات حجم كبير جدًا (لن يفهم 'abc' كسلسلة نصية). توضيح عند استخدام علامات الاقتباس الفردية يعامل كل حرف على أنه قيمة محرفية character literal، وتمثل قيمة كل حرف بناءً على ترميز الحرف وبالتالي ستحول 'abc' إلى قيمة عددية باستخدام قيمة ASCII المقابلة لكل حرف في السلسلة وتدمج معًا لتكوين قيمة عددية كبيرة الحجم. تعتبر هذه الأمثلة بسيطة جدًا، وهناك العديد من الأمثلة الأكثر تعقيدًا التي تؤدي إلى الالتباس وحدوث نتائج غير متوقعة التي قد يكون من الصعب تحديدها أو التنبؤ بها. توجد قاعدتان بشكل عام لتجنب حدوث هذه المشكلات: تعريف التابع المنشأ باستخدام الكلمة المفتاحية explicit لمنع حدوث التحويلات الضمنية استخدام توابع تحويل صريحة بدلًا من استخدام عوامل التحويل، وهذا الأمر يتطلب جهد إضافي في كتابة الكود ولكنه أبسط في القراءة ويمكن أن يساعد في تجنب النتائج غير المتوقعة. الخلاصة تعتبر لغة ++C لغة قوية جدًا، وفي الحقيقة فإن العديد من التطبيقات التي نستخدمها يوميًا مبنية على لغة ++C. وبالرغم من أن لغة البرمجة ++C توفر مرونة كبيرة للمطورين من خلال ما توفره من ميزات متطورة وباعتبارها من لغات البرمجة كائنية التوجه، إلا أن هذه الميزات المتقدمة يمكن أن تسبب بعض الالتباس والإحباط لبعض المطورين إذا لم تستخدم بشكل صحيح واحترافي. نتمنى لكم الاستفادة الجيدة من هذا المقال، وفي حال كان لديكم أي تساؤل يمكن تركه في قسم التعليقات أسفل المقال أو كتابته في قسم الأسئلة والأجوبة في أكاديمية حسوب. ترجمة وبتصرف للمقال Top 10 Most Common C++ Mistakes That Developers Make لكاتبه Vatroslav Bodrozic اقرأ أيضًا تحسين الشيفرات المكتوبة بلغة Cpp وتشخيصها أنظمة التصريف المستخدمة لبناء البرامج المكتوبة بلغة ++C وأهم أخطاء عملية البناء قواعد البرمجة ببساطة للمبتدئين الأخطاء العشرة الأكثر شيوعًا في شيفرة PHP الأخطاء العشرة الأكثر شيوعًا في شيفرة بايثون Python
-
تعد الاختبارات جزءًا أساسيًا من عملية تطوير البرمجيات فهي تضمن تقديم منتج برمجي عالي الجودة ويلبي توقعات المستخدمين، ومن المتعارف عليه أن يضع المبرمجون آلية معينة لأتمتة اختبار البرمجيات الخاصة بهم باستخدام أدوات وأكواد برمجية مختلفة. ولكن من الملاحظ أن الأدوات المستخدمة لاختبار البرمجيات لم تتطور بنفس الوتيرة التي تطورت بها البرمجيات نفسها، وتدل هذه الفجوة على مقدار الحاجة لتطوير وتحديث طرق أتمتة الاختبارات (test automation methods) لتواكب سرعة تطور البرمجيات نفسها. سنسلط الضوء في هذا المقال على الطرق التقليدية والحديثة لاختبار تطبيقات الويب والهواتف المحمولة، حيث سنبدأ باستعراض الطرق التقليدية الشائعة والتعرف على مميزاتها وعيوبها، ثم ننتقل لاستعراض الطرق الحديثة مثل منصة الاختبار السحابية TestGrid وهي أداة تبسط اختبار البرمجيات من البداية للنهاية عن طريق أتمتة عمليات الاختبار، وتوفر دعمًا كبيرًا لأدوات الاختبار الشائعة التي تسمح بإنشاء اختبارات تلقائية للتطبيقات عبر محاكاة تصرفات المستخدم مثل أداة سيلينيوم Selenium لتطبيقات الويب وأداة أبيوم Appium لتطبيقات الهواتف. استخدام الطرق التقليدية لأتمتة الاختبارات تركز أتمتة الاختبارات على كتابة سيناريوهات للاستخدامات المختلفة للتطبيق ووظائفه للتحقق من أنه يعمل كما هو متوقع. وتعتمد الطريقة التقليدية لأتمتة الاختبارات بشكل أساسي على كتابة سكربتات أو نصوص البرمجية لتنفيذ الاختبارات. أحد العيوب الرئيسية لهذه الطريقة هو صعوبة صيانة سكريبتات الاختبارات عندما يزداد حجم هذه التطبيقات وتضاف لها وظائف إضافية، مما يؤدي إلى ارتفاع تكاليف الصيانة، كما أنه قد يجعل هذه السكريبتات غير مستقرة وقد تعطي نتائج خاطئة. في الفقرات القادمة سنلقي نظرة على طريقتين معتمدتين بشكل واسع في أتمتة عملية الاختبار باستخدام إطار عمل أتمتة الاختبارات سيلينيوم Selenium وإطار أبيوم Appium. استخدام الأداة سيلينيوم Selenium لأتمتة اختبار تطبيقات الويب تعتبر أداة سيلينيوم Selenium الأداة الأكثر شيوعًا لأتمتة عمليات الاختبار التي يستخدمها المطورون والمبرمجون لكتابة اختبارات واجهة المستخدم المؤتمتة لتطبيقات الويب. يمكن تنفيذ هذه الاختبارات في جميع متصفحات الويب ويمكن كتابة هذه الاختبارات بإحدى لغات البرمجة الكثيرة التي تدعمها الأداة سيلينيوم Selenium. على سبيل المثال سنكتب اختبار مؤتمت باستخدام الأداة سيلينيوم حيث سنستخدم موقع ويب تجريبي لتنفيذ الخطوات على النحو التالي: تشغيل متصفح الويب (أي متصفح من اختيارك). الانتقال إلى صفحة تسجيل الدخول للموقع المراد اختباره. إدخال اسم المستخدم وكلمة السر. الضغط على زر تسجيل الدخول Login. التأكد من تسجيل دخول المستخدم عن طريق ظهور اسم المستخدم على الشاشة. ملاحظة1: سنستخدم الموقع demoqa.com وهو موقع تجريبي مفيد جداً للمطورين والمهتمين بتعلم أتمتة الاختبارات ويوفر مجموعة متنوعة من الأدوات والصفحات الوهمية التي يمكن استخدامها لأتمتة الاختبارات باستخدام أدوات مثل Selenium و Appium ويمكن للمستخدمين اختبار سيناريوهات مختلفة مثل تسجيل الدخول وتعبئة النماذج والتفاعل مع عناصر الواجهة وغيرها من العمليات الشائعة في مواقع الويب. ملاحظة2: يمكنك إنشاء مستخدم جديد بالضغط على زر مستخدم جديد New User بحيث يكون لدينا بيانات اعتماد صحيحة لسيناريو أتمتة عملية الاختبار. سنستخدم كود Selenium التالي لتنفيذ خطوات أتمتة اختبار تسجيل الدخول إلى موقع ويب: public class Login { @Test public void loginTest() throws InterruptedException { WebDriver driver = new ChromeDriver(); // Instantiating the webdriver instance driver.get("https://demoqa.com/login"); // Launching the website driver.manage().window().maximize(); // Maximizing browser window String user = "your user name"; String pass = "your password"; // Locating web elements for username, password, and the Login button driver.findElement(By.id("userName")).sendKeys(user); driver.findElement(By.id("password")).sendKeys(pass); driver.findElement(By.id("login")).click(); // Using sleep to load web elements on page Thread.sleep(5000); // Capturing the displayed username after login String uName = driver.findElement(By.id("userName-value")).getText(); // Asserting that the logged-in username matches the expected username Assert.assertEquals(uName, user); // Locating the web element for the Logout button and closing the webdriver instance driver.findElement(By.id("submit")).click(); driver.quit(); } } يفتح الكود السابق صفحة تسجيل الدخول ويدخل اسم المستخدم وكلمة المرور وويحاول تسجيل الدخول والتحقق من نجاح عملية التسجيل، ثم يسجل الخروج ويغلق المتصفح. فكما توضح التعليقات الموجودة في الكود سيُفتَح موقعنا الإلكتروني وتُحدد عناصر الويب ثم تنفذ الإجراءات المطلوبة عليها، وبعد تنفيذ الكود سترى سجلات التنفيذ الناجحة. ينجز كود الاختبار السابق وظيفة واحدة فقط وهي التأكد من تسجيل دخول المستخدم بطريقة صحيحة، ومن الملاحظ أن إنشاء مثل هذه الاختبارات وصيانتها يستغرق الكثير من الوقت، ففي حال حدث أي تغيير في التطبيق سيتوجب عليك تحديث الكود السابق في جميع مواضع الاختبار وهذا طبعًا يؤدي إلى زيادة النفقات. بالإضافة إلى كل ما سبق فإن عملية الدمج بين بيئات الاختبار المختلفة تكون محدودة نوعًا ما في الطرق التقليدية لأتمتة الاختبارات. استخدام الأداة Selenium مع الأداة TestGrid لأتمتة اختبار تطبيقات الويب لنحاول تشغيل كود الاختبار السابق الذي كتبناه باستخدام Selenium على أجهزة حقيقية متوفرة عبر سحابة TestGrid، حيث تسمح لنا الأداة TestGrid بإعادة استخدام سكريبتات الاختبار الموجودة في الأداة Selenium وتوسيع نطاق تنفيذ الاختبار المؤتمت. ملاحظة: TestGrid هي خدمة تقدم سحابة من الأجهزة الحقيقية التي يمكنك استخدامها لتشغيل اختبارات البرمجيات، فبدلاً من استخدام أجهزتك المحلية يمكنك استخدام هذه السحابة لاختبار التطبيقات على مجموعة متنوعة من المتصفحات والأنظمة الأساسية. بعد تسجيل الدخول إلى الموقع TestGrid وتنفيذ الخطوات الموجودة على الرابط التالي الذي يشرح كيفية تشغيل اختبارات Selenium المكتوبة محليًا على بيئة متصفح موجهة عبر TestGrid ستحصل على الكود التالي: public class TestGridDemoQaLogin { @Test public void loginTest() throws InterruptedException, MalformedURLException { // Setting the desired capabilities to use the TestGrid platform DesiredCapabilities dc = new DesiredCapabilities(); dc.setCapability("browserName", "firefox"); dc.setCapability("platformName", "linux"); dc.setCapability("tg:userToken", "Your Token"); dc.setCapability("tg:udid", "201"); // Instantiating the remote webdriver instance and passing desired capabilities to it WebDriver driver = new RemoteWebDriver(new URL("Your Browser Run URL(Public)"),dc); // Remaining steps are the same as in the previous Selenium code driver.get("https://demoqa.com/login"); driver.manage().window().maximize(); String user = "your user name"; String pass = "your password"; driver.findElement(By.id("userName")).sendKeys(user); driver.findElement(By.id("password")).sendKeys(pass); driver.findElement(By.id("login")).click(); Thread.sleep(5000); String uName = driver.findElement(By.id("userName-value")).getText(); Assert.assertEquals(uName, user); driver.findElement(By.id("submit")).click(); driver.quit(); } } عندما تنفذ هذا الكود على جهازك المحلي، ستلاحظ أن نفس الكود سيُنفَّذ على الجهاز الافتراضي للأداة TestGrid عبر الإنترنت لرؤية كيفية تنفيذ الاختبارات على السحابة. وبهذه الطريقة يمكنك مراقبة عملية تنفيذ الكود عن بعد من خلال البوابة الإلكترونية للأداة وتشخيص أي مشكلات تتعلق بتنفيذه، كما يمكنك أيضًا مشاهدة حالة التنفيذ (Execution Status) والتسجيلات (Recording) وتشغيل الملخص (Run Summary) الموجود في التبويب Automation Sessions ببساطة عن طريق اختيار الجهاز الذي ستنفذ عليه عملية الاختبار مما يوفر لك إمكانية تتبع الأداء والتحقق من صحة نتائج الاختبارات عن بُعد. استخدام الأداة أبيوم Appium لاختبار تطبيقات الهاتف المحمولة بشكل مؤتمت تعرف الأداة أبيوم Appium على أنها إطار عمل مفتوح المصدر وشائع الاستخدام يصلح لأتمتة اختبار تطبيقات الجوال الأصيلة (Native Apps) والتطبيقات الهجينة (Hybrid Apps) وتطبيقات الويب على الجوال (Mobile Web Apps). وتتطلب كتابة السكريبتات الخاصة بالأداة Appium معرفة جيدة بأنظمة تشغيل الهواتف المحمولة وفهم عميق للغات البرمجة مثل لغة جافا ولغة ++C ولغة بايثون. قد يكون اختبار تطبيقات الجوال بشكل آلي وصيانة سكربتات الاختبارات تحديًا بسبب التغير السريع في البرمجيات وأطر العمل وتحديثات الأجهزة وأنظمة التشغيل. على سبيل المثال سنستخدم تطبيق الآلة الحاسبة الأساسي، ونقوم بأتمتة عملية الاختبار للحالة التالية باستخدام أبيوم Appium. نشغل تطبيق الآلة الحاسبة. ننفذ عملية الضرب بين عددين. نسجل نتيجة العملية الحسابية. التأكد من النتيجة. في هذه الحالة سوف نستخدم الأداة Appium مع لغة البرمجة Java، حيث نشغل الاختبار على الجهاز الافتراضي (المحاكي) وبإمكاننا استخدام جهاز حقيقي لتنفيذ عملية الاختبار، وهذا هو الكود المستخدم package appium; import java.net.MalformedURLException; import java.net.URL; import org.openqa.selenium.remote.DesiredCapabilities; import org.testng.Assert; import org.testng.annotations.Test; import io.appium.java_client.AppiumBy; import io.appium.java_client.android.AndroidDriver; public class Calculator { @Test public void calculator() throws MalformedURLException { DesiredCapabilities dc = new DesiredCapabilities(); // Setting the desired capabilities for the android device dc.setCapability("platformName", "android"); dc.setCapability("platformVersion","14"); dc.setCapability("deviceName", "Pixel6_TestGrid"); dc.setCapability("automationName", "UiAutomator2"); // Setting capability for the application we want to test dc.setCapability("app", "/Users/macair/Downloads/calculator.apk"); // Instantiating Android Driver and using Appium server host and port AndroidDriver driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), dc); // Locating numbers, mathematical operators, and the equals button in the Calculator app driver.findElement(AppiumBy.id("com.google.android.calculator:id/digit_4")).click(); driver.findElement(AppiumBy.id("com.google.android.calculator:id/op_mul")).click(); driver.findElement(AppiumBy.id("com.google.android.calculator:id/digit_9")).click(); driver.findElement(AppiumBy.id("com.google.android.calculator:id/eq")).click(); // Storing the result in a string variable String result = driver.findElement( AppiumBy.id("com.google.android.calculator:id/result_final") ).getText(); System.out.println("The result is - "+result); Assert.assertEquals(result, "36"); driver.quit(); } } يعمل كود الاختبار الذي كتبناه في الأعلى بسلاسة، وهو يتطلب مع ذلك أشخاص محترفين يمتلكون خبرات كبيرة ومعرفة قوية لتفيذه بشكل احترافي وهذا يشكل عائقًا لاتباع هذه الطريقة، يكمن العائق الآخر في التوفر المحدود للأجهزة اللازمة لتنفيذ لهذه الاختبارات مما يتسبب في صعوبات وتحديات كبيرة للوصول إلى الحالة المثالية لهذه الاختبارات. استخدام Appium مع TestGrid لأتمتة اختبار تطبيقات الهواتف المحمولة سنرى في هذه الفقرة طريقة تنفيذ سكريبت اختبار الأداة Appium على السحابة الحقيقية للأجهزة إن طريقة استخدام اختبارات الأداة Appium المحلية الخاصة بتطبيقات الهاتف المحمول تشبه تمامًا ما تعرفنا عليه سابقًا في حالة تطبيقات الويب. يمكنك اتباع التعليمات الموجودة على هذا الرابط لتنفيذ كود Appium المحلي على سحابة الأجهزة. سنلقي نظرة على الكود بعد تحديثه بحيث يتضمن الصنف DesiredCapabilities وهو صنف برمجي يُستخدم لتحديد معاملات معينة عند إعداد بيئة الاختبار، وتستخدم هذه المعاملات لضبط خصائص جهاز TestGrid الذي سيجري عليه الاختبار، على سبيل المثال حددنا هنا المعاملات udid وsmartPort وuserToken لتحديد الجهاز الذي نريد التواصل معه: package appium; import java.net.MalformedURLException; import java.net.URL; import org.openqa.selenium.remote.DesiredCapabilities; import org.testng.Assert; import org.testng.annotations.Test; import io.appium.java_client.AppiumBy; import io.appium.java_client.android.AndroidDriver; public class CalculatorTestGrid { @Test public void calculator() throws MalformedURLException { DesiredCapabilities dc = new DesiredCapabilities(); // Setting the desired capabilities for the android device dc.setCapability("platformName", "android"); dc.setCapability("platformVersion","12"); dc.setCapability("deviceName", "Samsung Galaxy S21"); dc.setCapability("automationName", "UiAutomator2"); dc.setCapability("udid", "R5CRC28KGKB"); dc.setCapability("tg:systemPort", "4005"); dc.setCapability("tg:userToken", "Your user token"); // Since the application is installed on the device, we directly use // the package name and main activity dc.setCapability("appPackage", "com.google.android.calculator"); dc.setCapability("appActivity", "com.android.calculator2.Calculator"); AndroidDriver driver = new AndroidDriver(new URL("Your TestGrid Appium URL"), dc); // Remaining steps are the same as in the previous Appium code driver.findElement(AppiumBy.id("com.google.android.calculator:id/digit_4")).click(); driver.findElement(AppiumBy.id("com.google.android.calculator:id/op_mul")).click(); driver.findElement(AppiumBy.id("com.google.android.calculator:id/digit_9")).click(); driver.findElement(AppiumBy.id("com.google.android.calculator:id/eq")).click(); String result = driver.findElement( AppiumBy.id("com.google.android.calculator:id/result_final") ).getText(); System.out.println("The result is - "+result); Assert.assertEquals(result, "36"); driver.quit(); } } يقوم الكود أعلاه بتشغيل تطبيق الآلة الحاسبة على جهاز Android عبر TestGrid حيث ينفذ عملية ضرب عددين، ثم يتحقق من صحة النتيجة ويعرض النتائج في وحدة التحكم. يجب أن تكون قادرًا على رؤية سجلات التنفيذ في بيئة التطوير المتكاملة الخاصة بك IDE بعد تنفيذ هذا الكود المحدث، كما يمكنك أيضًا رؤية نتائج التنفيذ من خلال الذهاب إلى علامة التبويب Automaton Sessions في لوحة التحكم اليسرى واختيار نوع الجهاز الذي تريد تنفيذ الكود عليه. ستكون قادرًا أيضًا بعد تنفيذ هذا الكود على رؤية حالة كل مرحلة من مراحل التنفيذ مع لقطات الشاشة الخاصة بتلك المرحلة، كما ستكون قادرًا على الاطلاع على السجلات ونتائج الاختبار المرئية لهذه الاختبارات. الاستفادة من حلول اختبار الأتمتة الحديثة للأداة TestGrid تسمح منصة أتمتة الاختبار بدون كود من الأداة TestGrid للمستخدمين الذين لا يملكون خبرات برمجية أو خبراتهم الفنية ضعيفة نوعًا ما بالبدء بأتمتة الاختبارات. يمكنك باستخدام الأتمتة بدون كود تسجيل تفاعلات المستخدم بشكل مرئي دون الحاجة لكتابة أي كود برمجي، وبعد تسجيل التفاعلات يقوم النظام بتوليد سكربتات اختبار تلقائيًا بناءً على التسجيلات. وبالتالي لا داعي لكتابة كود برمجي يدويًا، تساعد هذه الميزة الرائعة على تقليل تكاليف صيانة السكريبت البرمجي بشكل كبير وأتمتة اختباراتك بشكل أسرع وأسهل، دون الحاجة لمعرفة تفصيلية بالبرمجة. استخدام مولد سكريبت الاختبار للأداة TestGrid لاختبار تطبيقات الويب لنرى كيف يمكننا أتمتة نفس حالة تسجيل الدخول للموقع التجريبي demoqa الذي اختبرناه في الفقرة السابقة ولكن هذه المرة بدون كود باستخدام منصة اختبار الويب بدون كود التي توفرها أداة TestGrid. اتبع الخطوات كما هو موضح عند إضافة حالة اختبار باستخدام مولد حالات الاختبار. وأدخل عنوان URL واختر متصفحًا وصفحة الويب، ثم أضف الإجراءات أو التفاعلات التي يمكن القيام بها على العناصر المختلفة للصفحة حسب المطلوب. كما هو موضح في الصورة أدناه، عند النقر على عنصر الويب المطلوب التفاعل معه يُكتشَف موقع هذا العنصر تلقائيًا، ستلاحظ أيضًا تحديد مواقع عناصر مختلفة ويمكنك اختيار ما تريده. كما أن لديك خيار كتابة موقع العنصر يدويًا، في حال لم يتمكن النظام من الكشف عن موقع العنصر المطلوب. يمكنك بعد الحصول على بيانات عناصر الاختبار المطلوبة إضافة الإجراءات المتعلقة بعملية تسجيل الدخول مع التفاصيل الضرورية لتوليد دالة الاختبار لحالة الاختبار الخاصة بك، تستطيع بعد ذلك تشغيل حالة الاختبار. يمكنك مع تقدم التنفيذ البدء بمراقبة السجلات وحالة الاختبار التي تم توليدها. اضغط على الأمر Build Summary في لوحة التحكم اليسرى لعرض التفاصيل مثل عدد الخطوات أو المراحل الناجحة والفاشلة ولقطات شاشة لخطوات التنفيذ وزمن البداية والنهاية وحالات الاختبار يمكنك بعد انتهاء عملية التنفيذ الوصول تقارير التحليل المختلفة والموجودة على التبويب Transaction Analysis في لوحة التحكم اليسرى. توجد ميزة أخرى يقدمها لنا مولد الاختبار بدون كود للأداة TestGrid وهي ميزة التسجيل والتشغيل، التي تسمح بتحويل إجراءات التطبيق أو المتصفح إلى اختبارات مؤتمتة وقوية. يمكنك بعد إنشاء المشروع والوحدة النقر على الأمر Add Test Case with Scriptless لاستخدام هذه الميزة، ستلاحظ وجود الزر Start Recording في لوحة التحكم اليمنى الذي يقوم بعد النقر عليه بتسجيل الإجراءات كما يمكن الضغط على الزر Stop عند الانتهاء. نلاحظ قفل الأمرين Live View وElement Picker خلال عملية التنفيذ يمكننا بعد إيقاف التسجيل تعديل المعاملات في العمود Action أو على الأمر Element Picker لإجراء المزيد من التعديلات على الخطوات المضافة باستخدام التسجيل. يمكننا بعد ذلك الانتقال مرة أخرى إلى الأمر Test Cases وتنفيذ الاختبار كما نفذناه المرة السابقة تمامًا، سنكون قادرين على مشاهدة التنفيذ والنتائج في تقارير التحليل المختلفة، نستطيع باستخدام الأمر Network Log عرض الطلبات requests المرسلة إلى النطاق domain والوقت الذي يستغرقه كل منها سكريبتات مخصصة عند الحاجة أو الطلب. وضحنا في هذا القسم كيفية كتابة اختبارات أتمتة بدون كود بسهولة لحالات الاستخدام الخاصة بك، وشرحنا طريقة إضافة سكربتات مخصصة عند الطلب. إذ يمكن لمولد سكربتات الاختبار في منصة TestGrid أن يساعدك في زيادة سرعة وموثوقية اختبارات الأتمتة لتطبيقات الويب الحديثة. استخدام مولد سكريبت الاختبار في الأداة TestGrid لاختبار تطبيقات الهاتف المحمول تعرفنا في الفقرة السابقة على طريقة اختبار تطبيقات الويب ويمكننا بطريقة مشابهة اختبار تطبيقات الهاتف المحمول وذلك باستخدام ميزة كتابة حالات اختبار تطبيقات الجوال دون كود التي توفرها في الأداة TestGrid، حيث يمكنك باستخدام هذه الميزة أتمتة تطبيقات الهاتف المحمول بسرعة دون الحاجة إلى إعداد الخادم والعملاء والأجهزة والإعدادات الأخرى يدويًا المطلوبة لتشغيل الاختبارات التلقائية. سنشاهد عند الضغط على الزر Add Test Function with TestCase Generator نافذة منبثقة تطلب منك إدخال معرف APK/IPA/BUNDLE ID التطبيق الذي تريد اختباره، حمل التطبيق ثم اختر الجهاز الذي سينفذ عليه الاختبار ثم اضغط على الزر Run لتشغيله. الخطوات التالية مشابهة تمامًا للخطوات التي طبقناها في حالة تطبيقات الويب بعد حفظ خطوات تطبيق عملية الضرب على رقمين والتحقق من النتيجة تضاف دالة الاختبار. لاحظ أنه يمكننا كتابة حالة الاختبار مباشرة دون الحاجة إلى كتابة دالة الاختبار كما فعلنا سابقًا، كما يمكننا إنشاء أو كتابة دوال اختبار للخطوات التي تتكرر في حالات الاختبار، والتي يمكن إعادة استخدامها كدوال مشتركة بعد الضغط على الزر Run لتشغيل حالات الاختبار، نختار الإصدار والجهاز الذي نريد تنفيذ الاختبارات عليه، يمكننا بعد الدمج والتجميع رؤية عملية التنفيذ في الوقت الحقيقي بالإضافة إلى السجلات المولدة. نستطيع من خلال الأمر Build Summary معرفة كافة تفاصيل عملية التنفيذ. يمكننا استخدام ميزة التسجيل والتشغيل في تطبيقات الهاتف المحمول بطريقة مشابهة تمامًا لتطبيقات الويب. كما يمكننا الاستفادة بقوة من السجلات والتقارير المتعددة التي توفرها لنا الأداة TestGrid، والتي تجعل تحليل أداء عمليات التنفيذ عملية سهلة وبسيطة للغاية. تُظهر الميزة Insights رسوم بيانية لمعاملات الأداء المختلفة المتوافقة مع عملية التنفيذ مثل التحميل على وحدة المعالجة المركزية (CPU) خلال الاختبارات. ومقدار استخدام الذاكرة خلال تشغيل التطبيق وحجم بيانات الاستجابة والطلب وغيرها الكثير. بالإضافة إلى ذلك يوفر الأمر Transaction Analysis العديد من التقارير القابلة للتحميل، والتي تتعلق بمعايير الأداء بما في ذلك معلومات الجهاز. وضحنا في هذا القسم طريقة الاستفادة القصوى من التحليلات والتقارير التفصيلية وسهولة التنفيذ لزيادة نطاق الأتمتة الخاصة بك، حيث يمكننا باستخدام سحابة الأجهزة الحقيقية للأداة TestGrid توسيع نطاق الاختبارات لتشمل عددًا كبيرًا من الأجهزة. وسواء كنت تمتلك أطر اختبار موجودة مسبقًا مثل مثل Selenium أو Appium دمجتها مع TestGrid واستخدمتها على سحابة الأجهزة الحقيقية أو بدأت من الصفر، فإن TestGrid يمكّنك من متابعة عملك من آخر نقطة وصلت إليها. استخدام ميزة الاختبار المرئي للأداة TestGrid يتحقق الاختبار المرئي Visual testing من كون واجهات المستخدم في التطبيقات تظهر كما هو متوقع حيث يتجاوز هذا النوع من الاختبارات التحقق الوظيفي (أي التحقق من أن العناصر تعمل بشكل صحيح) ويركز على التأكد من أن التصميم المرئي والمظهر العام للتطبيق يتطابق مع التصميم المتوقع والمخطط له ويسمح لنا باكتشاف التغييرات غير المرغوب فيها في التصميم أو المشكلات البصرية التي قد تحدث أثناء عملية تطوير التطبيق بمنهجية التطوير السريعة أجايل agile. تساعد الأداة TestGrid في جعل الاختبار المرئي قابلًا للقياس من خلال الأتمتة الذكية للاختبار، التي تسمح للمبرمجين بالتقاط لقطات للشاشة بشكل تلقائي عبر جميع الأطر أو النماذج وهي تستخدم أيضًا المقارنة بين الصور معتمدة بذلك على الذكاء الاصطناعي للعثور على الاختلافات بين هذه الصور. تساعد الخرائط الحرارية العميقة بالإضافة إلى لوحة تحكم التحليلات فرق الاختبار على أتمتة عمليات التحقق المرئي في تطبيقات الويب والهاتف المحمول، وتساعد هذه الميزة فرق الاختبار بالوصول إلى تجربة رائعة للمستخدم عن طريق التصميم المنظم والمنسق. يمكننا بعد اتباع خطوات إعداد الاختبار المرئي في كود الأداة Selenium أو الأداة Appium مشاهدة نتائج الاختبار المرئي عن طريق الانتقال إلى التبويب Visual Testing الموجود ضمن النافذة Automation Sessions للجهاز المختار. يلعب الاختبار المرئي دورًا أساسيًا في ضمان جودة التطبيقات بشكل عام سواء من حيث الوظائف أو من حيث الشكل والمظهر فعندما تختبر واجهة المستخدم بدقة وتهتم بالتفاصيل، فهذا يحسن من رضا المستخدمين ويزيد من احتمالية احتفاظهم بالتطبيق وتُسهّل عليك منصة TestGrid دمج الاختبارات المرئية في الاختبارات الحالية بشكل سهل وبسيط. وتساعدك في توسيع نطاق التحقق من جودة التطبيق الذي تختبره مما يضمن لك أن التطبيق يعمل كما هو متوقع في جميع جوانبه. الخلاصة يتوجب علينا في ظل تسارع عملية تطوير البرامج أن نتبع طرق أكثر حداثة لاختبار تلك البرامج حيث تتطلب التطبيقات الحديثة معايير عالية الجودة للاختبار وهو ما تفتقر إليه طرق أتمتة الاختبار التقليدية. تعد كتابة سكريبتات الاختبار في الأداتين Selenium و Appium أمرًا معقدًا ومكلفًا ويستغرق الكثير من الوقت مما يجعل التعديلات المتكررة لهذه السكريبتات أمرًا في غاية الصعوبة وغير موثوق دومًا. لذا يمكن الاعتماد على الأدوات المؤتمتة مثل TestGrid التي تحسن الطرق التقليدية للاختبار وتوفر ميزات حديثة مثل الأتمتة بدون كود برمجي والتقارير القوية والتحليلات المتكاملة، فيما تساعد سكريبتات الاختبار باستخدام تقنيات الذكاء الاصطناعي على الوصول إلى نطاق أوسع من الأجهزة بالإضافة إلى تحقيق الثبات والاستقرار. وبهذه الطريقة يستطيع المطورون ومهندسو ضمان الجودة (QA Engineers) تحقيق اختبارات نموذجية عن طريق الأجهزة والمتصفحات وأطر العمل وتصور مشكلات واجهة المستخدم UI قبل حدوثها. ترجمة وبتصرف للمقال Modernizing conventional test automation with TestGrid لكاتبه TestGrid. اقرأ أيضًا مدخل إلى اختبار مشاريع الويب للتوافق مع المتصفحات أتمتة إعداد خادم باستخدام أداة Ansible إعداد البيئة للاختبارات الآلية في مشاريع الويب للتوافق مع المتصفحات عالم الويب ومعاييره
-
عند إنشاء موقع الويب، نختار في البداية ألوانًا أساسية معروفة ومحددة بعناية، ولكن مع مرور الوقت، قد نضيف ألوانًا جديدة أكثر تخصيصًا أو نحتاج لإضافة إضاءة إلى لون زر معين لأسباب تتعلق بإمكانية الوصول وغيرها من الأمور الأخرى دون تخطيط جيد، مما قد يؤدي إلى حدوث فوضى في الألوان وفقدان جمالية التصميم، فكيف نضمن أن الألوان التي نختارها تتناسب مع نظام التصميم الخاص بمشروعنا؟ نكتشف في مقال اليوم دالة مزج الألوان()color-mix الجديدة نسبيًا في لغة CSS وكيف نستطيع من خلالها إنشاء ألوان متنوعة تعزز تصميم الموقع وجماليته. ما هي وظيفة الدالة ()color-mix إن الدالة ()color-mix دالة مهمة جدًا لتصميم المواقع، فهي تسمح بتحديد اللونين اللذين نريد مزجهما معًا لإنتاج لون جديد، كما تمكننا من التحكم في مقدار كل لون في المزيج واختيار مساحة الاستيفاء اللوني (color interpolation space) التي تحدد بدقة كيفية دمج الألوان مع بعضها البعض. فلفهم كيفية مزج الألوان مع بعضها علينا فهم طريقة الاستيفاء اللوني (color interpolation) لكونها معاملًا مطلوبًا لهذه الدالة، وسنناقش هذا الموضوع في فقرة لاحقة، ونعتمد على المعامل srgb في الأمثلة الحالية. سنحدد كمية كل لون في المزيج اللوني على هيئة نسبة مئوية، وفي حال عدم وضع النسب المئوية ستضع الدالة ()color-mix نسبة افتراضية 50% لكل من اللونين كما هو موضح في المثال التالي، فعند دمج اللون الأزرق مع الأحمر بدون وضع نسب مئوية ستكون النتيجة هي اللون البنفسجي. أما في حال تحديد النسبة المئوية لأحد اللونين فستضع الدالة ()color-mix النسبة المئوية للون الآخر بشكل افتراضي بحيث يكون مجموع النسبتين هو 100%. على سبيل المثال إذا أعطينا اللون الأحمر نسبة 10% ستكون النسبة المئوية الافتراضية للون الأزرق هي 90% والعكس صحيح إذا أعطينا اللون الأزرق نسبة 90% ستكون النسبة المئوية الافتراضية للون الأحمر هي 10% وستكون النتيجة نفسها في كلا الحالتين /* Both these will produce the same resultant color */ color-mix(in srgb, blue 90%, red) color-mix(in srgb, blue, red 10%) أما إذا كان مجموع النسبتين المئويتين للونين أقل من 100% فسيكون تصرف الدالة ()color-mix مختلفًا قليلًا، إذ ستحفظ المجموع كمضاعف ألفا (alpha multiplier) ومن ثم تقيّس اللونين بالاعتماد على هذا المضاعف بحيث يصل المجموع إلى 100%. أي سيعوض الفرق بين النسبتين وتوسَّعان لجعل مجموعها 100% مما يضمن حساب اللون النهائي بشكل صحيح. في المثال التالي ستمزج الدالة ()color-mix نفس الكمية من كل لون في كلا الحالتين، وستكون النتيجة نفس اللون في الحالة الثانية (حيث يكون مجموع النسبيتن 40%) ولكن بمضاعف ألفا قيمته 0.4 الذي يعوض انخفاض مجموع هاتين النسبتين لينتج نفس اللون لكنه شفاف جزئيًا لكون مجموع النسب أقل من 100%. /* Result: rgb(128 0 128) */ color-mix(in srgb, blue, red) /* Result: rgb(128 0 128 / 0.4) */ color-mix(in srgb, blue 20%, red 20%) إنشاء تدرجات فاتحة وداكنة للون معين باستخدام الدالة ()color-mix غالبًا ما نحتاج في الواقع العملي إلى إنتاج تدرجات أفتح وأغمق للون معين، ولتحقيق المطلوب يمكننا دمج اللون الأبيض أو الأسود مع اللون الأساسي بنسب متفاوتة حسب الحاجة باستخدام الدالة ()color-mix. نخلط في المثال أدناه كميات مختلفة من اللونين الأبيض والأسود مع اللون الأزرق الأساسي باستخدام الدالة ()color-mix للحصول على تدرجات زرقاء فاتحة وداكنة. /* Initial base color */ .bg-blue { background-color: blue; } /* 50% blue, 50% white */ .bg-blue-light { background-color: color-mix(in srgb, blue, white); } /* 25% blue, 75% white */ .bg-blue-lighter { background-color: color-mix(in srgb, blue, white 75%); } /* 50% blue, 50% black */ .bg-blue-dark { background-color: color-mix(in srgb, blue, black); } /* 25% blue, 75% black */ .bg-blue-darker { background-color: color-mix(in srgb, blue, black 75%); } سنحصل على التدرجات المبينة في الصورة التالية: استخدام خصائص محددة لإعادة استخدام التدرجات اللونية يمكننا تخزين قيم الدالة ()color-mix كخصائص محددة custom properties وإعادة استخدامها ضمن الكود، وهذا الأسلوب مفيد عندما نريد إنشاء تدرجات أفتح أو أغمق للون الأساسي. يوضح الكود التالي طريقة تعريف اللون الأساسي وتخزينه كقيمة للخاصية brand-- وبهذا يسهل تعديل اللون والحصول على درجات مختلفة فاتحة وداكنة منه. :root { --brand: rgb(0 0 255); --brand-light: color-mix(in srgb, var(--brand), white); --brand-lighter: color-mix(in srgb, var(--brand), white 75%); --brand-dark: color-mix(in srgb, var(--brand), black); --brand-darker: color-mix(in srgb, var(--brand), black 75%); } كما يمكن أيضًا إنشاء تدرجات مختلفة من الشفافية عن طريق الخاصية transparent على النحو التالي: :root { --brand: rgb(0 0 255); --brand-alpha-50: color-mix(in srgb, blue, transparent); --brand-alpha-75: color-mix(in srgb, blue 75%, transparent); } مثال عملي لتنسيق تدرجات لونية للزر باستخدام خصائص محددة للدالة ()color-mix سننسق في هذا المثال العملي أزار بسيطة أساسية وثانوية في الموقع، نعرف في البداية خصائص محددة تمثل اللون الأساسي وهو الأزرق، واللون الثانوي الناتج عن دمج الأزرق مع الزهري باستخدام الدالة ()color-mix ونعرف من خلالها أيضًا تدرجات أفتح لكلا اللونين كما يلي: :root { --brand: rgb(0 0 255); --brand-light: color-mix(in srgb, blue, white); --secondary: color-mix(in srgb, var(--brand), pink); --secondary-light: color-mix(in srgb, var(--secondary), white); } في الخطوة الثانية، سنطبق اللون الأساسي والثانوي على الأزرار الأساسية والثانوية في الموقع، ونضيف تأثير لتفتيح ألوان الأزرار عندما نمرر مؤشر الفأرة فوقها لإحداث تأثير (hover states) كما يلي: button { background-color: var(--brand); color: white; } button:where(:hover, :focus) { background-color: var(--brand-light); } button.secondary { background-color: var(--secondary); } button.secondary:where(:hover, :focus) { background-color: var(--secondary-light); } ستظهر نتيجة الكود على النحو التالي: See the Pen Untitled by Hsoub Academy (@HsoubAcademy) on CodePen. لسنا ملزمين بتعريف الخصائص في مستوى الجذر فقط، إذ يمكننا على سبيل المثال تعريف خاصية اللون الأساسي للمكوّن أو الزر، وإنشاء تدرجات مختلفة لهذا اللون ضمن تنسيق المكوّن باستخدام الدالة ()color-mix، ويمكن ببساطة تطبيق لون أساسي مختلف للنسخة الثانوية من المكوّن، كما يوضح الكود التالي: .card { --color: blue; background: color-mix(in srgb, var(--color), white 80%); border-top: 5px solid var(--color); padding: 1rem; } .secondary { --color: deeppink; } وستظهر نتيجة الكود أعلاه على النحو التالي: See the Pen color-mix2 by Hsoub Academy (@HsoubAcademy) on CodePen. وفيما يلي عرض توضيحي لتطبيق هذا المفهوم على مجموعة متنوعة من مكونات واجهة المستخدم، لاحظ انسجام الألوان وتناغمها. See the Pen color-mix-3 by Hsoub Academy (@HsoubAcademy) on CodePen. إنشاء تدرجات الألوان الدافئة والباردة باستخدام الدالة ()color-mix تستخدم الدالة ()color-mix في الغالب لإنشاء تدرجات مختلفة أفتح أو أغمق من لون موجود، كما يمكن استخدامها أيضًَا لإنشاء تدرجات دافئة وباردة من اللون من خلال مزج ألوان دافئة أو باردة مع الألوان الأساسية. في هذا المثال، نحدد لوحة ألوان أساسية مأخوذة من موقع Coolors ثم نضيف الألوان التي نريد مزجها لإنشاء التدرجات اللونية الدافئة والباردة بتعريف خصائص محددة: :root { --yellow: rgb(221 215 141); --peach: rgb(220 191 133); --chocolate: rgb(139 99 92); --khaki: rgb(96 89 77); --grey: rgb(147 162 155); --mix-warm: red; --mix-cool: blue; } .palette > div { --color: var(--yellow); &:nth-child(2) { --color: var(--peach); } &:nth-child(3) { --color: var(--chocolate); } &:nth-child(4) { --color: var(--khaki); } &:nth-child(5) { --color: var(--grey); } } بعد ذلك سنستخدم هذه الخصائص لمزج اللون الثاني مع اللون الأساسي الأصلي مع تحديد كمية هذا اللون، كما سنحدد أيضًا القيم الافتراضية فإذا لم تمرر قيمة افتراضية للدالة ()color-mix فسيتسخدم اللون الأساسي الأصلي. .palette > div { background: color-mix( in srgb, var(--color), var(--mix, var(--color)) var(--amount, 10%) ); } بهذه الطريقة سنكون قادرين على مزج ألوان مختلفة وتطبيق ذلك على كامل اللوحة .cool { --mix: var(--mix-cool); } .cool--20 { --amount: 20%; } .warm { --mix: var(--mix-warm); } .warm--20 { --amount: 20%; } See the Pen color-mix-4 by Hsoub Academy (@HsoubAcademy) on CodePen. تحديد فضاء الألوان المستخدمة في الاستيفاء اللوني ضمن الدالة ()color-mix استخدمنا في الفقرات السابقة نظام الألوان srgb (وهو النموذج اللوني المعياري أحمر أخضر أزرق) كطريقة لتداخل اللون، لكننا نستطيع أن نغير النتيجة بشكل جذري عن طريق تعديل فضاء الألوان color spaces المستخدم للتداخل أو الاستيفاء اللوني interpolation. تعد فضاءات الألوان مفاهيم معقدة وشرحها يتجاوز نطاق هذه المقالة، لكن من الجدير بالذكر بعض مزايا وعيوب فضاءات الألوان عند اتخاذ قرار بشأن استخدامها في الدالة ()color-mix. خيارات فضاءات الألوان يعني استيفاء اللون الطريقة التي ينتقل بها لون إلى لون آخر، ويتم ذلك باستخدام ما يُعرف بالتدرجات اللونية gradients (مثل تحول لون من الأحمر إلى الأزرق تدريجيًا) في النظام التقليدي RGB، قد تظهر الألوان في وسط التدرج بشكل باهت، بينما باستخدام أنظمة ألوان أخرى مثل lch أو oklch ستبقى الألوان مضيئة وحيوية حتى في وسط التدرج، وتكون النتائج مختلفة تمامًا عند تطبيقها على لوحات الألوان الدافئة والباردة في المثال السابق، ويمكنك رؤية الفرق بين هذه الأنظمة في الصورة أدناه. بالرغم من أن نظامي الألوان sRGB و HSL شائعان لتمثيل الألوان، لكنهما لا يقدمان نتائج دقيقة عندما يتعلق الأمر بكيفية رؤية البشر للألوان أما نموذجا oklch وoklab فهما طريقتان أحدث لتمثيل الألوان مصممتان بحيث تكون التغيرات العددية فيها مماثلة للتغيرات التي يدرك فيها الإنسان الألوان بحواسه فإذا غيرت اللون بمقدار معين على محور الإحداثيات، فإن التغيير سيكون ملاحظًا أو مرئيًا بنفس المقدار بغض النظر عن اللون الأصلي، لذا يعطي استخدام oklch وoklab نتائج أكثر دقة في رؤية الألوان عند مزجها أو تدرجها، لهذا يفضل معظم المطورن استخدامهما عند مزج الألوان باستخدام دالة ()color-mix، ولكن الخيار متروك لك في النهاية. مسارات الاستيفاء اللوني الأقصر والأطول يمكننا في فضاءات الألوان الدائرية مثل oklch و oklab و hsl اختيار الاتجاه الذي الذي ننتقل أو نتدرج فيه من لون إلى آخر لتحقيق الدمج، فعندما نمزج لونين بالتساوي ستكون زاوية اللون الناتج في منتصف الطريق بين زاويتي اللونين، وتختلف هذه الزاوية بالاعتماد على مسار الاستيفاء وإن كان يتبع المسار الأطول والأقصر حول دائرة اللون. تخيل دائرة الألوان مثل عجلة ألوان، إذا كان لديك لونان على هذه العجلة، فيمكنك الانتقال من لون لآخر عن طريق الدوران في اتجاه عقارب الساعة أو عكس اتجاه عقارب الساعة. فعند مزج لونين بالتساوي سيكون اللون الناتج في منتصف الطريق بين اللونين على العجلة ولكن يمكن أن يكون هناك مسار أقصر ومسار أطول للوصول إلى هذا اللون، في حال اختيار المسار الأقصر سيكون الدوران عبر جزء صغير من العجلة، بينما في المسار الأطول سيكون الدوران عبر جزء أكبر من العجلة. لذا، فإن اختيار الاتجاه الذي تتبعه يمكن أن يؤثر على النتيجة النهائية للون الناتج. color-mix(in hsl, rgb(255 88 88), rgb(86 86 255)); color-mix(in hsl longer hue, rgb(255 88 88), rgb(86 86 255)); جرب مزج الألوان في فضاءات الألوان وأطوال المسارات المختلفة الموضحة في الإطار التالي، ولاحظ الفرق في النتائج التي ستحصل عليها: See the Pen color-mix-5 by Hsoub Academy (@HsoubAcademy) on CodePen. دعم متصفحات الإنترنت للدالة ()color-mix دُعمت الدالة ()color-mix في جميع المتصفحات الحديثة من منتصف عام 2023، وبالطبع لن يملك جميع المستخدمين نسخًا حديثة من المتصفحات لذا سيكون الحل بوضع قيمة أولية ثابتة للألوان أولاً (بدون الاعتماد على الدالة ()color-mix)، فإذا لم يدعم المتصفح الدالة سيستخدم هذه الألوان المحددة. على سبيل المثال، لن تتمكن المتصفحات التي لا تدعم الدالة ()color-mix من التعامل مع السطر الثاني من هذا الكود: div { /* First declaration is fallback for browsers that do not support color-mix() */ background: rgb(150 0 255); background: color-mix(in srgb, blue, red); } ولمعرفة إذا كان المتصفح يدعم الدالة ()color-mix يمكن استخدام الاستعلام التالي: .card { background: lightblue; } @supports (color-mix(in srgb, blue, white)) { .card { --color: blue; background: color-mix(in srgb, var(--color), white 80%); border-top: 5px solid var(--color); } } كما يمكنك ثثبيت ملحق إضافي يدعى PostCSS يتيح لك كتابة جميع وظائف الدالة ()color-mix دون الحاجة لمعرفة حالة المتصفح حيث يقوم بتحويل الكود تلقائيًا إلى كود CSS متوافق مع جميع المتصفحات. على سبيل المثال يقوم الملحق PostCSS بتحويل الكود التالي: .some-element { background-color: color-mix(in srbg, red, blue); } إلى كود CSS كما يلي: .some-element { background-color: rgb(128 0 128); } الخلاصة تعرفنا في هذا المقال على طريقة استخدام الدالة ()color-mix لإنشاء تدرجات مختلفة للألوان ،وطريقة استخدام هذه الدالة لدمج الألوان بطرق مختلفة والاستفادة منها في تصميم المواقع، كما تعرفنا على دعم المتصفحات لهذه الدالة ويمكن القول أننا مقبلون على عصر جديد من التعامل مع الألوان على مواقع وتطبيقات الويب. ترجمة وبتصرف للمقال Creating color palettes with the CSS color-mix() function لكاتبته Michelle Barker اقرأ أيضًا تعرف على CSS التعامل مع الألوان في CSS التنسيقات الأساسية للعناصر في CSS كيفية اكتشاف دعم المتصفحات للميزات أثناء اختبار مشاريع الويب
-
يسري ابو رجب بدأ بمتابعة Omar Kall
-
هل تريد أن تتعلم طريقة بناء موقع ويب باستخدام لغة التوصيف HTML ولغة التنسيق CSS وإطار بوتستراب Bootstrap، إذا كان الجواب نعم فهذا المقال لك حيث سنشرح لك من خلاله جميع الخطوات اللازمة للانتقال من شاشة فارغة في المتصفح إلى موقع ويب جميل، ونتعلم كيف تنسق عناصره بطريقة احترافية وأنيقة في نفس الوقت. ولكن في البداية لنتعرف بشكل موجز ما هي لغة HTML ولغة CSS وفيم تستخدمان؟ ما هي لغة HTML ولغة CSS يستخدم أي موقع ويب كلًا من HTML و CSS في بناءه، وهاتان اللغتان مرتبطتان مع بعضهما البعض ارتباطًا وثيقًا، أي لا يمكن الاستغناء عن أي منهما في صفحة الويب، وإليك تعريفًا مبسطًا لكل منهما. HTML هي اختصار لعبارة Hypertext Markup Language وهي لغة توصيفية أي أنها تُوصّف بنية صفحة الويب ومحتوياتها. CSS هي اختصار لعبارة Cascading Style Sheets وهي لغة تنسيقية مسؤولة عن تصميم صفحة الويب وتنسيق عناصرها. ملاحظة: يوجد فرق بين مصطلح صفحة ويب ومصطلح موقع ويب فعندما نقول صفحة ويب "web page" فنحن نعني ملف HTML واحد، أما عندما نقول موقع ويب "website" فنحن نعني مجموعة من ملفات HTML مترابطة والتي تكوّن مع بعضها موقع الويب ككل. متطلبات العمل أول أمر ستحتاجه قبل البدء بإنشاء موقع ويب باستخدام HTML و CSS هو خادم أو استضافة لموقع الويب فالخادم هو الحاسوب الذي سيقوم باستضافة ملفات الموقع. وفي هذا المجال يوجد الكثير من أنواع الاستضافات التي يمكنك الاختيار من بينها، وما عليك سوى البحث عن الاستضافة المناسبة لمتطلباتك. بعد الحصول على الخادم، الشيء التالي الذي تحتاجه هو اسم نطاق domain name وهو يمثل عنوان الموقع على الإنترنت، حيث يكتبه المستخدمون في المتصفح للوصول إلى موقعك. على سبيل المثال اسم النطاق لموقع شركة حسوب هو hsoub.com بعد توفر الاستضافة والنطاق عليك ربطهما مع بعضهما البعض، وفي حال حصلت على الاستضافة والنطاق من نفس الشركة ستتولى هذه الشركة العمل النيابة عنك وتقوم بالإعدادات التالية: تجهيز حساب استضافة خاص بك. حجز وتسجيل اسم النطاق. تجهيز بيئة العمل وإعدادات للربط بين الخادم واسم النطاق. إنشاء لوحة تحكم تستطيع من خلالها التحكم الكامل بموقعك. ملاحظة: إذا كنت ترغب فقط في تجربة إنشاء موقع ويب بسيط على حاسوبك بغرض التعلم ولا تنوي جعله عامًا لتصفحه الجميع من خلال شبكة الإنترنت، فيمكنك أن تنشئ كافة الملفات الخاصة بالموقع على جهازك المحلي وتتصفحها مباشرة من أي متصفح ويب، كما يمكنك تحويل حاسوبك إلى خادم ويب محلي تثبيت برنامج يُسمى XAMPP (وهذه الخطوة ضرورية فقط في حال تطوير مواقع ديناميكية وليست إلزامية للمواقع الثابتة كموقعنا الحالي فهو لا يحتاج لمعالجة من طرف الخادم لكن تثبيت الخادم يساعدك على فهم كيفية عمل خوادم الويب وكيفية استضافة الملفات محليًا، مما يسهل فهم عملية تطوير المواقع قبل نشرها على الإنترنت). 1. تعلم أساسيات HTML إن العنصر أو المكون الأساسي في بنية لغة HTML هو ما يعرف بالوسم tag الذي يمكن أن يبدو كما يلي: <b> SOMETHING </b> استخدمنا في هذا المثال الوسم <b> الذي يجعل النص غامقًا، وهذا الوسم يبدأ بوسم الفتح <b> وينتهي بوسم الإغلاق <b/> وستكون النتيجة في هذه الحالة عرض النص بين الوسمين بخط غامق على الصفحة SOMETHING. هنالك طبعًا الكثير من الوسوم tags الأخرى في لغة HTML نذكر منها على سبيل المثال: الوسم <i> -- </i> الذي يحول النص الموجود بين وسمي الفتح والإغلاق إلى نص مائل. الوسم <u> -- </u> الذي يضع سطرًا تحت النص. الوسم <p> -- </p> الذي يتضمن فقرة من النص. الوسم <h1> -- </h1> الذي يحول النص ضمنه إلى عنوان رئيسي. إن هذه الوسوم وسوم بسيطة، ولكن يوجد هناك وسوم أكثر تعقيدًا فعلى سبيل المثال إذا أردت إنشاء القائمة التالية: item 1 item 2 item 3 سيتوجب عليك في هذه الحالة كتابة كود HTML التالي: <ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> وإذا أردت أن تضيف رابطًا لصفحة وليكن مثلًا رابط للصفحة الرئيسية لأكاديمية حسوب ضمن صفحتك فيجب عليك إضافة الوسم التالي إلى كود HTML: <a href="https://academy.hsoub.com">Hsoub Academy</a> وللتعرف على المزيد من المعلومات حول وسوم HTML وطبيعة عملها ننصح بمطالعة توثيق HTML على موسوعة حسوب. 2. فهم بنية ملف HTML يمكنك أن تعتبر صفحة الويب عبارة عن بناء مؤلف من مجموعة من المكعبات المرصوفة بجانب بعضها البعض لنحصل في النهاية على صفحة HTML وفق هيكل معين مؤلف من عدد من الوسوم tags. وفيما يلي أبسط بنية لصفحة HTML: <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Hello, world!</title> </head> <body> <h1>Hello, world!</h1> <p>My first web page.</p> </body> </html> يمكنك أن تنسخ هذا الكود وتلصقه في ملف نصي جديد، وتحفظه باسم index.html وبذلك تكون حصلت على صفحة HTML صالحة للعرض في متصفح الويب. ولنبدأ بشرح هذا الكود بشكل مفصل: الوسم <doctype html!> يخبرنا أن نوع الملف هو ملف HTML أي صفحة ويب. الوسم <"html lang="en> يدل على أن اللغة المكتوب بها هذا الملف هي اللغة الانكليزية. الوسم <head> يدل على بداية ترويسة الصفحة، ونضع فيه جميع الإعدادات والبيانات الوصفية (meta tags) لهذه الصفحة ، ولا يتم عرض معظم تلك المعلومات على الشاشة لكنها تكون موجهة لمحركات البحث. الوسم <"meta charset="utf-8> يحدد نوع ترميز الأحرف المستخدم في الصفحة. الوسم <title>Hello, world!</title> يمثل عنوان الصفحة وهو ما سيراه الزائرون في شريط عنوان متصفحاتهم. الوسم <body> يمثل بداية جسم الصفحة وفيه نضع كل محتوى الصفحة، ونعود ونكرر أن هذا المكان هو الجزء الرئيسي لملف HTML لأنه الجزء المرئي الذي سيظهر لزوار الموقع. الوسم <h1>Hello, world!</h1> يمثل العنوان الرئيسي للصفحة. الوسم <p>My first web page.</p> يعبر عن فقرة نصية. الوسم يدل على نهاية ملف HTML. ملاحظة مهمة: ننصحك باختيار محرر شيفرات برمجية مناسب لكتابة تعليمات HTML مثل فيجوال ستوديو كود Visual Studio Code أو Sublime Text فهي مجانية ولها إصدارات على نظام التشغيل ويندوز ونظام التشغيل mac وتسهل عليك كتابة الكود وتوفر ميزات مثل تلوين التعليمات البرمجية لتسهل عليك قراءتها وتمييزها بصريًا. إليك مثالًا على كود HTML مكتوب ضمن محرر Sublime: تستطيع بعد الانتهاء من كتابة الملف أن تنسخه وتضعه صف ضمن المجلد الرئيسي للخادم المحلي الذي نصبته على جهازك وتستعرض هذا الملف من خلال المتصفح، لن يكون شكل الصفحة جميلًا حاليًا وسيظهر كما في الشكل التالي: تحتاج هذه الصفحة للكثير من التحسينات قبل عرضها على زوار الموقع، وهو ما سننجزه في الفقرات التالية. 3. تعلم أساسيات كتابة محددات CSS لغة CSS هي المسؤولة عن وضع التنسيقات لصفحة HTML وجعلها تبدو أكثر جمالًا وتملك CSS ما يعرف بالمحددات Selectors والتي تصف طريقة ظهور العنصر في الصفحة كما في المثال التالي: p { font-size: 18px; } يعلمنا هذا المحدد أن حجم نص الفقرة في هذه الصفحة هو 18 بكسل، ومع ذلك فإن الطريقة الأفضل لتنسيق العناصر هي إنشاء أصناف classes وتعيينها لكل وسم على حدا. على سبيل المثال يمكن أن نكتب محدد الصنف class selector في لغة CSS بالطريقة التالية: .normal-text { font-size: 18px; } لاحظ أن النقطة (.) تدل هنا على أن هذا التنسيق هو لصنف class يحمل الاسم normal-text وبالتالي أي وسم ضمن الصفحة لديه هذا الصنف سيكون حجم الخط فيه هو 18 بكسل، وهكذا يمكن تطبيق هذا المحدد على وسم الفقرة النصية كما يلي: <p class="normal-text">This text is going to be 18px.</p> لنشرح كود CSS المذكور أعلاه بشيء من التفصيل: عرفنا صنف باسم normal-text. وكل شيء يأتي بعده نضعه ضمن قوسي الفتح والإغلاق {} ونكتب ضمن هذين القوسين كل قواعد التنسيق التي ستحدد شكل العناصر التي تأخذ هذا الصنف . الخاصية font-size هي مثال لأحد خواص CSS وهي تحدد حجم الخط. 18 بكسل هي القيمة المعينة لهذه الخاصية. هناك الكثير من خصائص CSS التي يمكنك الاطلاع عليها من خلال توثيق CSS على موسوعة حسوب 4. دمج تنسيقات CSS ضمن صفحة HTML إن ملف HTML هو ملف مهيكل للغاية، فكل عنصر فيه يجب أن يكون في مكانه، وترتيب العناصر مهم للغاية للبنية النهائية ومظهر صفحة الويب المعنية. أما مستند CSS فهو أقل تنظيمًا. يطلق على مستندات CSS عادة اسم ورقات الأنماط المتتالية وورقة الأنماط هي قائمة تتضمن كل تعاريف الأصناف المستخدمة في مستند HTML المقابل. وترتيب هذه التعاريف ليس مهمًا بالضرورة (على الأقل بالنسبة للتصاميم البسيطة). وبالتالي لتنشئ ورقة أنماط CSS عليك تعريف كل صنف class واحدًا تلو الآخر وتعريف الخصائص والتنسيقات المطلوبة ضمنه، ثم تطبيقه على عنصر HTML وعرضه لاختبار ما إذا كانت النتيجة في تصميم صفحتك هي ما تريده. قد يكون هذا عملًا شاقًا ويستغرق وقتًا، وللسهولة يمكن اللجوء لحل أكثر بساطة والاستعانة بإطار عمل يسمى بوتستراب Bootstrap بدلًا من كتابة كافة أصناف CSS من الصفر، وهو ما سنتطرق لشرحه في الفقرات التالية. 5. تثبيت Bootstrap بوتستراب Bootstrap هو أداة مفتوحة المصدر لإنشاء مواقع الويب باستخدام HTML و CSS، وبتعريف أبسط يوفر بوتستراب Bootstrap إطارًا يضمن أن الهيكل الرئيسي لصفحة الويب جاهز ومحسن لمزيد من التطوير ويسهل عليك تصميم صفحة الويب وتنسيقها فهو يحتوي على عدد كبير من الأصناف classes الجاهزة مسبقة التنسيق التي تزيد من جمالية صفحة الويب الخاصة بك. باستخدامك لإطار عمل Bootstrap لن تضطر إلى البدء من الصفر في التعلم لأنه سوف ينقلك إلى الجزء الممتع من عملية بناء موقع الويب باستخدام HTML و CSS. أمامك طريقتان لتعلم طريقة التعامل مع بوتستراب Bootstrap لتنسيق موقعك: المسار الأول: تعلم Bootstrap من خلال الذهاب إلى الصفحة الرئيسية وتحميل حزمة Bootstrap الرئيسية والبدء في اكتشاف محتوياتها و البناء عليها. المسار الثاني: وهو المسار المختصر ويكون بتحميل تصميم جاهز لصفحة ويب مطبق عليها تنسيقات بوتستراب Bootstrap، مما يوفر الوقت والجهد في تصميم الصفحة من الصفر. قد يتضمن المسار الأول بعض الصعوبة في التعلم في البداية ولكنه خيار جيد لإنشاء صفحات الويب باستخدام HTML و CSS فبمجرد تعلمك التقنيات الأساسية للتعامل مع Bootstrap سيكون من السهل عليك إنشاء مواقع ويب بتصميمات جميلة وجذابة، ولكي تبدأ بهذا المسار ننصحك بقراءة توثيق بوتستراب الرسمي أو يمكنك الاطلاع على توثيق بوتستراب باللغة العربية من موسوعة حسوب. سنختار في مقالنا هذا المسار الثاني بالبدء بهيكل جاهز ومعد مسبقًا يوفر عليك الكثير من الجهد في التعلم ويساعدك على تنسيق المحتوى بسرعة واحترافية. 6. اختيار التصميم عندما تنشئ موقع ويب باستخدام لغة التوصيف HTML ولغة التنسيق CSS يكون لديك الحرية الكاملة في اختيار قالب Bootstrap الذي يناسبك ويناسب موقعك. سوف نستخدم في هذا المثال التوضيحي أحد القوالب البسيطة التي يوفرها موقع startbootstrap لغرض التعلم مع أنه يوجد الكثير من القوالب المجانية الجميلة والتي تم تحسينها والمصممة بشكل ممتاز وتعمل بدون أي مشكلة. سنستخدم في هذا المقال قالبًا مجانًيًا يدعى Creative وستكون النتيجة النهائية للصفحة كما يلي: لتحميل هذا القالب اضغط على الزر تحميل مجاني Free Download الموجود على يمين هذه الصفحة أو حمله من هنا مباشرة startbootstrap-creative-gh-pages.zip ثم احفظ الملف المضغوط على جهازك، وبعد تحميله فك ضغطه وانقل المجلد الناتج إلى المجلد الرئيسي لخادمك المحلي، أو إلى ارفع الملفات على استضافة موقعك وافتح الموقع الآن من خلال المتصفح وستشاهد هذه الصفحة الجميلة بالشكل التالي: بالرغم من أنك ستشاهد صفحة ويب جميلة ومنسقة بالكامل، إلا أن عليك الآن أن تتعلم طريقة الحصول على مثل هذه الصفحة وتخصيصها بالطريقة التي تناسبك باستخدام أكواد HTML و CSS. 7. تخصيص الموقع باستخدام HTML وCSS في البداية سنعمل على تصميم الصفحة الرئيسية والتي من خلالها سنوضح طريقة تنسيق النصوص والصور وكل شيء في الصفحة بشكل عام. تحدثنا في فقرة سابقة بشكل موجز عن قسم <head> في صفحة HTML لنشرح هذا القسم بشيء من التفصيل. عندما تفتح الملف index.html من قالب بوستراب الجاهز سيكون قسم الرأس <head> مشابهًا لما يلي: <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>Creative - Start Bootstrap Theme</title> <!-- Theme CSS - Includes Bootstrap --> <link href="css/creative.min.css" rel="stylesheet"> </head> ملاحظة: للسهولة أزلنا الأشياء غير الأساسية من هذا الكود مثل كود تحميل خطوط جوجل وأيقونات Font Awesome ووحدة Lightbox للصور المعروضة على الصفحة. لقد تعرفنا فيما سبق على معظم أسطر هذا الكود، ولكن سنشرح الأسطر الجديدة: بداية كل ما هو مكتوب بين هذين القوسين <!--...--> هو تعليقات لن تظهر على الصفحة عند عرضها في المتصفح. سطر الكود التالي <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> هو سطر خاص بإطار بوتستراب وهو يحدد حجم إطار العرض viewport لموقع الويب لجعله متجاوبًا مع كافة أحجام الشاشات. السطر <link href="css/creative.min.css" rel="stylesheet"> يربط ملف CSS خارجي مع ملف HTML، ولمزيد من السهولة سنقوم بتعديل هذا السطر ليصبح <link href="css/creative.css" rel="stylesheet"> والاختلاف بين السطرين السابقين هو أن السطر الثاني يحمّل النسخة غير المختصرة non-shortened من ملف CSS ليسهل عليك عملية التعديل. الآن لو انتقلت إلى نهاية الملف index.html ستجد الأسطر التالية قبل وسم الإغلاق <body> مباشرة: <!-- Bootstrap core JavaScript --> <script src="vendor/jquery/jquery.min.js"></script> <script src="vendor/Bootstrap/js/Bootstrap.bundle.min.js"></script> <!-- Plugin JavaScript --> <script src="vendor/jquery-easing/jquery.easing.min.js"></script> <script src="vendor/magnific-popup/jquery.magnific-popup.min.js"></script> <!-- Custom scripts for this template --> <script src="js/creative.min.js"></script> ومهمتها دمج ملفات جافا سكريبت JavaScript ضمن ملف HTML مما يؤدي إلى زيادة التفاعل بالصفحة، على سبيل المثال عندما نقنقر على الرابط About سينقلك بطريقة سلسة ومتدرجة إلى القسم الخاص بها من الصفحة بدلًا من عرضها بطريقة فجائية، طبعًا وظائف لغة جافا سكريبت JavaScript كثيرة جدًا وليست ضمن مجال مقالنا الحالي، إذ سنركز حاليًا على إضافة محتوى وتنسيقات مخصصة إلى صفحة الويب. 8. إضافة محتوى وصور الصفحة إن أول ما سنقوم به في الصفحة هي تغيير عنوانها 1. تغيير عنوان الصفحة ابحث في قسم <head> عن الوسم <title> وغير عنوان الصفحة الموجود ضمن وسمي الفتح والإغلاق إلى العنوان الذي تريده <title>My HTML Site</title> 2. تخصيص ترويسة الصفحة (Hero Section) يطلق اسم قسم البطل أو الهيرو على القسم البارز الظاهر أعلى صفحة الويب، وهو قسم مهم لكونه أول ما يسترعي انتباه الزوار ويجب تصميمه بطريقة لافتة ومميزة، وهو في تصميمنا القسم الظاهر في هذه الصورة: سنضع المحتوى الخاص بنا داخل هذا القسم، ولتعديله عُد إلى ملف index.html وابحث عن القسم التالي: <!-- Masthead --> <header class="masthead"> <div class="container h-100"> <div class="row h-100 align-items-center justify-content-center text-center"> <div class="col-lg-10 align-self-end"> <h1 class="text-uppercase text-white font-weight-bold">...</h1> <hr class="divider my-4"> </div> <div class="col-lg-8 align-self-baseline"> <p class="text-white-75 font-weight-light mb-5">...</p> <a class="btn btn-primary btn-xl js-scroll-trigger" href="#about">Find Out More</a> </div> </div> </div> </header> يتحكم الكود السابق في محتوى قسم البطل، ولدينا في هذا الكود البرمجي بعض الوسوم الجديدة لنوضح دورها: الوسم <header> يخبرنا أن هذا القسم كله هو رأس الصفحة وهو مرتبط بشكل أساسي مع وسوم إخوة sibling أخرى في الصفحة هما الوسم <section> والوسم <footer> الوسم <div> وهو اختصار لكلمة division ويستخدم لفصل جزء الكود الحالي عن الأجزاء الباقية ضمن ملف HTML ويساعد على تمييز الأقسام بصريًا في الصفحة. بالإضافة إلى ذلك سنلاحظ أن بعض الوسوم التي تعرفنا عليها مسبقًا ستصبح أكثر تعقيدًا عند إضافة عدة أصناف CSS لها كما في المثال التالي: <h1 class="text-uppercase text-white font-weight-bold">...</h1> في هذا المثال وضعنا عدة أصناف classes للعنوان الرئيسي <h1> وهي text-uppercase و text-white لتجعل لون النص أبيض و font-weight-bold لجعله سميكًا. أنشئت هذه الأصناف بواسطة بوتستراب ومن قبل مطور قالب Creative ويمكنك أيضًا استخدامها عند تنسيق موقعك الخاص، ويمكنك تخصيص أي وسم تضيفه إلى بنية صفحتك، وإذا كنت ترغب بالاطلاع على كل هذه الأصناف ما عليك سوى الذهاب إلى الملف creative.css الموجود ضمن المجلد CSS الموجود ضمن القالب الذي تحملته من الويب. ربما يصعب عليك فهم جميع هذه الأصناف في البداية ولكن مع الوقت ستكتشف أن الموضوع سهل للغاية. على سبيل المثال طبقنا الصنف font-weight-light على وسم الفقرة، وعندما نذهب إلى الملف creative.css ونبحث عنه سنكتشف أن هذا الصنف مكون من التعليمات التالية: .font-weight-light { font-weight: 300; } وبالتالي ستفهم أن تطبيق هذا الصنف على عنصر ما سيجعل وزن الخط لهذا العنصر خفيفًا light (تمثل القيمة 400 الوزن الافتراضي العادي regular وتمثل أي قيمة أعلى منها الوزن الخفيف light وأي قيمة أعلى منها الوزن العريض bold). كما يعد تعديل النصوص في HTML عملية بسيطة للغاية وهذا ما يتضح من خلال سطر الكود التالي: <h1 class="text-uppercase text-white font-weight-bold">Your Favorite ...</h1> كل ما عليك هو البحث عن وسمي الفتح والإغلاق وتعديل النص الموجود بينهما ليصبح كما يلي: <h1 class="text-uppercase text-white font-weight-bold">Your Favorite ...</h1> يمكن عمل الشيء نفسه في جميع وسوم صفحة HTML، كما يمكن إضافة فقرات جديدة بحريّة تامة. على سبيل المثال نستطيع أن نضيف فقرة موجودة بنسخ ولصق كود فقرة سابقة مع تغيير النص الموجود ضمنها كما يلي: <p class="text-white-75 font-weight-light">Start Bootstrap can ...</p> <p class="text-white-75 font-weight-light">Paragraph 2</p> الآن وبعد أن تعرفت على طريقة التعامل مع النصوص وتخصيصها، لنشرح طريقة التعامل مع الصور ونغير صورة الخلفية في ترويسة صفحتنا. هذا قد يكون أصعب قليلًا لأنه لا يتم ضمن ملف HTML فلا يوجد وسم يشير إلى تضمين صورة في الصفحة بأي شكل لذا سنحتاج للانتقال إلى ملف CSS وإجراء تغيير فيه، فإذا نظرنا إلى وسم <header> في الصفحة سنلاحظ وجود صنف معين له اسمه masthead وهذا الصنف هو المسؤول عن تغيير صورة الخلفية، وإذا فتحنا ملف creative.css وانتقلنا لتعريف الصنف masthead سنجده بالشكل التالي: header.masthead { padding-top: 10rem; padding-bottom: calc(10rem - 72px); background: linear-gradient(to bottom, rgba(92, 77, 66, 0.8) 0%, rgba(92, 77, 66, 0.8) 100%), url("../img/bg-masthead.jpg"); background-position: center; background-repeat: no-repeat; background-attachment: scroll; background-size: cover; } يُطبَّق هذا الكود العديد من التنسيقات على الصور مثل موقع الصورة، وأبعادها، وعدد مرات تكرارها، ومسارها ("url("../img/bg-masthead.jpg وفي أغلب الأحيان يكون هناك مجلد فرعي خاص بالصور ضمن المشروع باسم img. فلتغيير صورة الخلفية في هذا المثال نختار الصورة المناسبة لصفحتنا وننسخها إلى المجلد img ثم ننسخ اسم الصورة الجديدة ونلصقه مكان اسم الصورة القديمة ليصبح مسار الصورة("url("../img/your-file.jpg 3. تخصيص الأقسام الأخرى في الصفحة عند تصفحك للملف index.html ستلاحظ وجود أقسام مختلفة ضمن الصفحة مثل قسم التنقل navigation وقسم حولنا about وقسم الخدمات services وقسم اتصل بنا contact وغيرها من الأقسام. وعلى الرغم من اختلاف المحتوى ضمن هذه الأقسام أو الصفحات إلا أن البنية العامة لكود HTML هي نفسها لا تتغير ولكن تنسيقات CSS تختلف من قسم إلى آخر وهنا يمكنك المرور على كل صفحة وتغيير التنسيقات بالشكل الذي يناسب احتياجاتك. كما يمكنك أيضًا نقل أقسام كاملة (أي الأجزاء بين وسوم <section> في كود HTML) من مكان لآخر يدويًا وذلك عن طريق قصها ولصقها في المكان الذي تريده. 9. ضبط الألوان والخطوط تعد عملية تغيير الألوان والخطوط من أبسط الأمور في HTML وCSS والطريقة الأسهل للقيام بذلك هي كتابة تنسيقات مضمنة باستخدام الخاصية style ضمن الوسم كما في المثال التالي: <p style="color: #FF0000;">Red text</p> تُمثَل الألوان في لغة HTML بقيم ست عشرية hexadecimal فعلى سبيل المثال تمثل القيمة FF0000# اللون الأحمر ويمكنك الاطلاع على جميع قيم الألوان من خلال الرابط standard colors لكن الطريقة الأفضل لتغيير الألوان هي عن طريق كتابتها كتنسيقات منفصلة ضمن ملف CSS وليس كتنسيقات مضمنة ضمن وسوم HTML كما في المثال السابق، فعلى سبيل المثال يمكننا تحقيق نفس تأثير الكود السابق من خلال كتابة الكود التالي في ملف CSS: p.red { color: #FF0000; } ثم استخدام هذا الصنف في كود HTML كما يلي: <p class="red">Red text</p> الطريقة الثانية هي الطريقة التي يعتمدها بوتستراب، فلتغيير لون أي نص على الصفحة، ابحث أولاً عن الوسم المسؤول عن تنسيق هذا النص، ثم انتقل إلى ملف CSS وعدل لون النص في الصنف المقابل، أو أنشئ صنفًا جديدًا لهذا اللون ثم طبقه على العناصر التي تريدها، وإذا لم يكن لهذا الوسم صنف يمكنك إنشاء صنف جديد له في ملف CSS. يوضح المثال التالي لك الفكرة فإذا أردت تغيير لون النص "At Your Service." في العنوان الرئيسي من اللون الأسود إلى اللون البرتقالي: <h2 class="text-center">At Your Service</h2> اذهب إلى ملف CSS وأنشئ الصنف التالي: .text-orange { color: #f4623a !important; } تعني الكلمة المحجوزة important! أن الأولوية للون الذي تم تطبيقه هنا وتجاوز تنسيق أي لون آخر جاء قبله، بعد تعريف هذا الصنف class يمكنك الآن العودة إلى العنوان الرئيسي وتطبيق هذا الصنف عليه كما يلي: <h2 class="text-center text-orange">At Your Service</h2> وعندها سيصبح العنوان الرئيسي باللون البرتقالي كما في الصورة: أما لتغيير نوع الخط وحجمه فيمكننا القيام بشيء مشابه لما قمنا به في حالة اللون حيث سنذهب إلى ملف CSS ونعرف الصنف class التالي: .SOMECLASS { font-family: "Merriweather", Roboto, sans-serif; font-size: 18px; } حددنا في هذا الصنف نوع الخط المناسب وعينا حجم الخط ليكون 18 بكسل، وبالتالي يمكن استخدام هذا الصنف في أي وسم من وسوم HTML، وفي التطبيق العملي نضع عادة كافة إعدادات الخطوط والألوان في صنف واحد ضمن ملف CSS ومن ثم نطبقها على وسوم HTML. وبالعودة إلى مثالنا السابق نستطيع تغيير حجم الخط للعنوان الرئيسي ليصبح 50 بكسل ولكن يجب بدايًة إنشاء الصنف التالي: .text-xxl { font-size: 50px; } ثم نطبقه على وسم العنوان الرئيسي كما يلي: <h2 class="text-center text-orange text-xxl">At Your Service</h2> كما يمكننا تغيير الألوان والخطوط ضمن قالب بوسترات أيضًا من خلال الذهاب إلى الصنف المطلوب في ملف CSS وتعديله بالطريقة التي تناسب مشروعنا. 10. إنشاء صفحات إضافية بعد أن خصصت صفحة موقعك الرئيسية حان الوقت لإنشاء صفحات إضافية للمشروع وربطها بالصفحة الرئيسية لأن أي موقع ويب لا يقتصر عادة على صفحة واحدة بل يتكون من عدة صفحات إضافية ومن هذه الصفحات: صفحة حولنا about page صفحة اتصل بنا contact us صفحة نماذج الأعمال portfolio صفحة المنتجات أو الخدمات products/services صفحة فريق العمل team صفحة السياسات policies 1. البدء بتصميم الصفحة عندما تنشئ صفحة ويب جديدة فإن أول شيء تفكر به هو تصميم الصفحة وتخطيط عناصرها، ولذلك عندما تنشئ موقعك باستخدام HTML وCSS من الأفضل أن تضع بداية التصميم الأولي الذي يناسبك ثم تحول هذا التصميم إلى تعليمات برمجية. قد تكون البداية في عملية إنشاء الموقع من شاشة فارغة باستخدام HTML و CSS صعبة نوعًا ما ولذلك يمكنك كذلك الاستعانة بقوالب بوتستراب الجاهزة ولكن دعونا نأخذ قبل ذلك فكرة عن مبادئ تصميم الصفحات ثم نطبق ذلك التصميم باستخدام Bootstrap. يمكنك تقسيم الصفحة إلى أجزاء ومن ثم تجميعها لتكون صفحة واحدة كما في الشكل: ما يميز بوتستراب هو أنه ينوب عنك في التعامل مع مبادئ التصميم الأساسية وتفاصيل مظهر الصفحة بحيث يمكنك التركيز على وضع هذه التفاصيل في الأماكن الصحيحة. لننشئ الآن صفحة جديدة هي صفحة حولنا about، في البداية سننشئ تصميمًا مبدئيًا على غرار المثال السابق الموضح أعلاه وفيه الأقسام التالية: قائمة التنقل navigation في الأعلى العنوان الرئيسي يمتد على كامل عرض الصفحة قسم المحتوى الرئيسي في منتصف الصفحة تذييل الصفحة ودائمًا يوجد في نهاية الصفحة 2. إنشاء صفحة جديدة إن أبسط طريقة لإنشاء صفحة جديدة هي أخذ نسخة عن صفحة موجودة مسبقًا وبالتالي استخدام نفس القالب. في مثالنا ننسخ الصفحة index.html ونغير اسمها إلى about.html ومن ثم نغير العنوان title لتمييز الصفحات ونختار عنوان لصفحة حول الشركة ليكون على سبيل المثال <title>About Us</title> والآن سنستعرض الملف سطرَا سطرًا ونقرر ما هي الأسطر التي سنتركها وما هي الأسطر التي سنحذفها: قائمة التنقل navigation: يجب الحفاظ عليها لجعل خاصية التنقل موحدة في جميع الصفحات. قسم العنوان الرئيسي: لن نكون بحاجة له وفق تصميم مشروعنا وبالتالي سنقوم بحذفه كاملًا. قسم حولنا about: هو القسم الذي سنستخدمه ليكون العنوان الرئيسي. الأقسام خدمات services ونماذج الأعمال portfolio واتصل بنا contact لن نحتاج لها على الإطلاق ولذلك سنحذفها. قسم التذييل footer سنحتاج له ولذلك يجب الحفاظ عليه. وبعد القيام بهذه التعديلات سنحصل على الكود البسيط التالي: <!DOCTYPE html> <html lang="en"> <head> <!-- all head definitions ... removed for clarity --> </head> <body id="page-top"> <!-- Navigation --> <!-- actual navigation tags removed for clarity --> <!-- About Section --> <!-- ... --> <!-- Footer --> <!-- ... --> <!-- Bootstrap script imports --> <!-- ... --> </body> </html> القسم المفقود هنا هو قسم المحتوى الرئيسي ولذلك سنكتبه عن طريق إعادة استخدام القسم حولنا About Section وذلك بأخذ نسخة من الكود التالي: <!-- About Section --> <section class="page-section bg-primary" id="about"> <div class="container"> <div class="row justify-content-center"> <div class="col-lg-8 text-center"> <h2 class="text-white mt-0">...</h2> <hr class="divider light my-4" /> <p class="text-white mb-0">...</p> </div> </div> </div> </section> ثم نغير أول سطرين في الكود ليصبحا كما يلي: <!-- Main Content Section --> <section class="page-section bg-primary" id="main-content"> وبما أننا لا نحتاج للعنصرين <h2> و <hr> لذا سنحذفهما ليتبقى لدينا الكود التالي: <!-- Main Content Section --> <section class="page-section bg-primary" id="main-content"> <div class="container"> <div class="row justify-content-center"> <div class="col-lg-8 text-center"> <p>A paragraph of text.</p> </div> </div> </div> </section> ستلاحظ عندما تحفظ الملف وتفتحه في المتصفح وجود سطرين فوق بعضهما البعض مع خلفية واحدة كما في الشكل: من الأفضل أن تكون الخلفية بيضاء في قسم المحتوى الرئيسي، ولنحقق ذلك بسهولة نزيل الصنف bg-primary من الوسم الرئيسي <section> ليصبح سطر الكود كما يلي: <section class="page-section" id="main-content"> وتصبح النتيجة كما في الشكل: سنضيف الآن بعض الفقرات إلى الكود لملئها بالمحتوى، ونضيف عنوان فرعي كما يلي: <!-- Main Content Section --> <section class="page-section" id="main-content"> <div class="container"> <div class="row justify-content-center"> <div class="col-lg-8 text-center"> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p> <p>Proin fermentum, felis tempor pharetra lobortis, magna quam hendrerit dolor...</p> <h3>Subhead</h3> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p> </div> </div> </div> </section> ليصبح شكل الصفحة كما يلي: إذا لم ترغب في توسيط النص ما عليك سوى إزالة الصنف text-center من الوسم <div> كما في الشكل: ولإضفاء المزيد من الجمالية على النصوص الموجودة في الصفحة يمكنك إنشاء أصناف جديدة ضمن ملف CSS وتعيينها لهذه الفقرات، أو تعيين أصناف بوتستراب جاهزة وفي مثالنا قمنا بتعيين الأصناف التالية على الوسم <p> كما يلي: <p class="lead text-muted">Lorem ipsum dolor sit amet...</p> <p class="text-muted">Proin fermentum, felis tempor pharetra lobortis, magna quam hendrerit dolor...</p> وعلى الوسم <h3> كما يلي: <h3 class="h3 mt-4">Subhead</h3> ليصبح شكل الصفحة كما في الشكل التالي: لنضف الآن صورة في أعلى الصفحة من خلال الوسم الخاص بالصورة التالي: <img src="img/image.jpg"> إن كتابة الوسم بهذه الطريقة محدود جدًا إذ لا توجد أي تنسيقات به، ولإضافة بعض التنسيقات له يمكننا الاستعانة بأصناف بوتستراب الجاهزة كما يلي: <img src="img/image" class="rounded img-fluid"> بإضافة هذه الأصناف للصورة ستظهر بحواف مستديرة وتضمن بأن حجم الصورة لن يتجاوز حجم الكتلة الموجودة ضمنها. يمكنك الآن إضافة بعض الوسوم إلى قسم المحتوى الرئيسي كما يلي: <p class="lead text-muted">Lorem ipsum dolor sit amet...</p> <img src="img/image.jpg" class="rounded img-fluid" /> <p class="text-muted"> Proin fermentum, felis tempor pharetra lobortis, magna quam hendrerit dolor... </p> <h3 class="h3 mt-4">Subhead</h3> ليكون الشكل النهائي لصفحة حولنا كما يلي: 3. الربط مع صفحة جديدة بعد الانتهاء من عملية إنشاء الصفحة الجديدة، علينا الآن ربطها مع الملف index.html والمكان الطبيعي لعملية الربط هو في قائمة التنقل navigation من خلال البحث عن سطر الكود التالي: <a class="nav-link js-scroll-trigger" href="#about">About</a> وإجراء التغيير عليه ليصبح كما يلي: <a class="nav-link" href="about.html">About</a> أضفنا في السطر السابق الوسم <a> وهو وسم الارتباط التشعبي في HTML فعن طريقه يمكننا ربط الصفحات مع بعضها البعض عن طريق توفير عنوان تلك الصفحة في البارامتر href. الذي يمثل نص الرابط أو الجزء القابل للنقر في الرابط وسيكون هذا النص بين وسمي الفتح والإغلاق <a></a> وبالنتيجة عند تحديث الصفحة الرئيسية سيظهر الرابط الجديد الذي تستطيع من خلاله الآن الانتقال إلى الصفحة حولنا about. خطوات إضافية لتطوير الموقع بعد قراءتك لهذا المقال يفترض أنك تستطيع الآن بناء موقع ويب بسيط مؤلف من صفحتين هما الصفحة الرئيسية homepage وصفحة حولنا about، ومن الجيد أن تطور مهاراتك وتتابع بناء صفحات أخرى متعددة بتنسيقات جميلة وتضيفها إلى موقعك وتربطها بقائمة التنقل أعلى الموقع. كما ننصحك بتعزيز مهاراتك في تطوير مواقع الويب فهو تخصص مميز ومطلوب في سوق العمل، فإذا كنت مهتمًا باحتراف إنشاء المواقع يمكنك مطالعة دورة تطوير الواجهات من أكاديمية حسوب التي تساعدك على فهم اللغات الأساسية لتطوير الويب ( HTML و CSS وجافا سكريبت) كما تشرح لك كل ما يتعلق بإطار بوتستراب Bootstrap وكيفية استخدامه في المشاريع والتعامل مع مكوناته المختلفة لتطوير مواقع احترافية بسرعة وغيرها من التقنيات المفيدة التي تساعدك على بناء معرض أعمال غني يعزز فرصتك في الحصول على عمل. نتمنى لكم الاستفادة الجيدة من هذا المقال، وفي حال كان لديكم أي تساؤل يمكن تركه في قسم التعليقات أسفل المقال أو كتابته في قسم الأسئلة والأجوبة في الأكاديمية. ترجمة وبتصرف للمقال karol krol لكاتبه How to Code a Website اقرأ أيضًا تعرف على أساسيات لغة CSS دليلك الشامل لإعداد واستخدام إطار العمل بوتستراب Bootstrap HTML و CSS للمبتدئين: كيف تصمم أول صفحة ويب لك كيفية تنسيق الموقع الإلكتروني باستخدام تعليمات HTML مدخل إلى تخطيط صفحات الويب باستخدام CSS
-
سنتعرف في مقال اليوم على توابع الواجهة البرمجية للطرفية التي توفرها لغة البرمجة جافا سكريبت JavaScript Console API فهي طرفية قوية توفر للمطور مجموعة من التوابع القادرة على اكتشاف جميع رسائل الخطأ الغامضة و إظهارها له بوضوح مما يسهل عليه فهم سببها وتصحيحها. تُستخدم الطرفية console لتسجيل بعض المخرجات مثل نتائج العمليات الحسابية، والقيم المعادة من الواجهة البرمجية REST API ونتائج العمليات التي تنفذ على النصوص أو إعادة النظر في جزء معين من البرنامج والكثير من الأمور الأخرى، كما يمكنك استخدام الطرفية console لعرض أو إخراج أي نوع من البيانات ترغب بها. فعندما تقوم بتشغيل JavaScript في متصفحك تعرض سجلات الطرفية console logs في طرفية المطور الخاصة بمتصفح الإنترنت. سنتعرف في هذا المقال على المهام التي تستطيع الطرفية القيام بها كما سنتعرف على بعض التوابع غير المشهورة والتي ربما تكون مفيدة لك كمطور JavaScript سواًء كنت مطورًا مبتدئًا أو محترفًا أو كنت تريد فقط معرفة الغرض من هذه الطرفية فقط وسنبدأ بالتوابع المشهورة ثم ننتقل إلى التوابع الأقل شهرة مع الأمثلة العملية على كل منها. تسجيل الدخول إلى الطرفية Logging to the console إذا كنت على دراية بالتابع ()console.log فيمكنك الانتقال إلى الفقرة التالية التي تشرح مستويات التسجيل لأننا سنغطي في هذه الفقرة الأساسيات والتي تتضمن معرفة ما هي الطرفية؟ وكيف تستخدم؟. يستخدم معظم المطورين التابع ()console.log لإرسال معلومات عامة حول الكود الخاص بهم إلى الطرفية console والتي يمكن العثور عليها في أدوات التطوير الخاصة بمتصفح الويب DevTools ملاحظة عامة: يمكنك قص جميع السفرات البرمجية التي سترد في الفقرات التالية ولصقها في طرفية متصفحك وتجريب تنفيذها بنفسك، كل ما عليك هو فتح أدوات المطور في متصفحك والنقر على علامة تبويب Console لفتح الطرفية ولصق الكود وتشغيله. التابع ()log هو التابع الأساسي لكائن الطرفية console، ويمكنك أن تعطي أي قيمة لهذا التابع وسيسجل أو يعرض النتيجة في الطرفية كما في المثال التالي: const hello = "Hi there, welcome to MDN Web Docs!"; console.log(hello); // Hi there, welcome to MDN Web Docs! سيفيدك هذا الأمر كمطور في معرفة فيما إذا الكود الذي تكتبه يعمل بشكل صحيح أم لا من خلال عرض الخرج الناتج من الكود وجعله مرئيًا بالنسبة لك، إليك على سبيل المثال ما يمكن القيام به لاختبار إن كان تابع تنسيق التاريخ والوقت DateTimeFormat يعمل بشكل صحيح: const currentDate = new Date(); const formattedDate = new Intl.DateTimeFormat("en-US").format(currentDate); console.log("Current date:", formattedDate); // Current date: 7/12/2024 يمكنك أيضًا تزويد التابع ()log بعدة قيم أوعناصر لتطبيق بعض التنسيقات الجميلة على الخرج كما في المثال التالي: const currentDate = new Date(); const formattedDate = new Intl.DateTimeFormat("en-US").format(currentDate); console.log("Today's date is %s", formattedDate); // Today's date is 7/12/2024 من الخصائص الرائعة للطرفية أيضًا هو إمكانية استخدامها بشكل تفاعلي كما في حال خاصية الإكمال التلقائي المدعومة من أغلب متصفحات الإنترنت والتي تسمح لك بالتنقل بين الاقتراحات المقدمة، على سبيل المثال في حال لم تكن تعرف ماذا تكتب في التابع log اكتب فقط .console.log(window وستظهر لك العديد من الخيارات لإكمال ما كتبت مثل التابع alert على سبيل المثال. console.log(window.navigator.oscpu); // Intel Mac OS X 10.15 ملاحظة: لا تنس التخلص من بقايا استدعاءات التابع ()console.log الزائدة وغير الضرورية التي أضفتها في الكود البرمجي الخاص بك خلال مرحلة تطويره قبل نشره للعموم كي تتجنب طباعة معلومات حساسة أو غير ضرورية للمستخدمين في بيئة الإنتاج النهائية. عرض سجلات أخرى في الطرفية باستخدام التوابع info, warn, error يمكنك أن تضيف المزيد السجلات المفيدة للطرفية باستخدام توابع أخرى مثل التابع ()conlose.info والتابع ()conlose.warn والتابع ()conlose.error. إن تأثير استخدام هذه التوابع سيكون مشابهًا لتأثير استخدام التابع ()console.log لكن الفرق سيكون في تنسيق الخرج (الذي يعتمد بدوره على نوع المتصفح)، وسيمكنك من خلالها تصفية الخرج الناتج، وهذا مفيد جدًا إذا كنت تريد رؤية سجلات الأخطاء فقط كما في المثال التالي: const browser = window.navigator.userAgent; console.info(browser); // Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0 يجب عليك استخدام التابعين warn و error إذا كنت تتوقع حدوث أخطاء من المستخدمين عند تشغيل الكود console.warn("Unknown device - there may be compatibility issues."); // ⚠️ Unknown device - there may be compatibility issues. console.error("Unsupported device. See <docs url> for more details."); // 🛑 Unsupported device. See <docs url> for more details. // <stack trace> ملاحظة: تعرض بعض المتصفحات تتبعًا لرسائل الخطأ ولكن يمكنك باستخدام الطرفية console استدعاء هذه الميزة بسهولة وذلك عن طريق استخدام التابع ()console.trace والذي سيتم شرحه مفصلًا في فقرة لاحقة. عرض جداول في الطرفية باستخدام التابع ()console.table يعد هذا التابع أحد التوابع المفيدة جدًا للمطورين، وهو يستخدم غالبًا عند الحاجة لنسخ مخرجات الكود ومن ثم لصقها في أحد محررات الأكواد editors، والقيام بعد ذلك ببعض عمليات التنسيق عليها لجعلها قابلة للقراءة. كما يمكنك باستخدام الطرفية console القيام بشيء رائع وهو إنشاء الجداول مباشرة عن طريق التابع ()console.table وهو أمر منطقي لأننا نقوم بتشغيل الكود عادة ضمن المتصفح وهو قادر -أي المتصفح- على التعامل مع إخراج الجداول بشكل جيد جدًأ const dogs = [ { name: "Yoshi", color: "Black", personality: "Calm" }, { name: "Melanie", color: "Brown", personality: "Hyperactive" }, { name: "Peppers", color: "white", personality: "Unpredictable" }, ]; console.table(dogs); سيظهر هذا الكود على المتصفح على على شكل جدول منسق بشكل جميل كما يلي: التعداد باستخدام التابع ()console.count يمكنك إضافة عداد إلى سجلاتك عن طريق التابع ()console.count وذلك لمعرفة عدد مرات حدوث أمر معين كما يلي: function postBoostClicked() { // My post has been boosted, do something here console.count("Boost count"); } postBoostClicked(); // Boost count: 1 postBoostClicked(); // Boost count: 2 إضافة المؤقتات باستخدام التابع ()console.time والتابع ()console.timer يعد التابعان ()console.time و ()console.timeEnd من التوابع المفيدة في الحالات التي تتطلب تشغيل وإيقاف تشغيل كود معين ضمن برنامجك، حيث تستخدم هذه التوابع لقياس المدة التي يستغرقها الكود مثل الوقت الذي يستغرقه تنفيذ تابع ما. في المثال التالي يستغرق التابع ()myFunction وقت 200 ميلي ثانية للتنفيذ: console.time("timerName"); // call myFunction() console.timeEnd("timerName"); // timerName: 200ms - timer ended يمكنك إضافة بعض التفاصيل مع الوقت، فمثلًا يمكنك إضافة ملاحظات من خلال استخدام التابع ()console.timeLog ، ويعج هذا الأمر مفيد جدًا إذا كان برنامجك يحتوي على مراحل منفصلة لتنفيذ المهمة المطلوبة كما في المثال التالي: console.time("MyTimer"); console.timeLog("MyTimer", "Starting application up…"); // MyTimer: 0ms Starting application up… // call myFunction(), for example console.timeLog("MyTimer", "UI is setup, making API calls now"); // MyTimer: 200ms UI is setup, making API calls now // call otherFunction(), for example console.timeEnd("MyTimer"); // MyTimer: 300ms - timer ended تجميع السجلات باستخدام التابع ()console.group تفيد عملية تجميع السجلات باستخدام التابعين ()console.group و ()console.groupCollapsed في تنظيم الخرج في حال إنشاء سجلات كثيرة، خاصة إذا كان الكود يمر عبر عدة مراحل، مثل مراحل الإعداد أو مهام المعالجة فتجميع السجلات مناسب جدًا في هذه الحالة. يمكنك كذلك طي المجموعات، مما يعني أنه بإمكانك توسيع وطي المجموعة ضمن الطرفية console إذا كنت ترغب في عرض أو إخفاء السجلات خاصة إذا كان هناك الكثير من المعلومات التي يجب عليك التدقيق بها. console.group("Grouped Logs"); console.log("Log 1"); console.log("Log 2"); console.groupEnd(); // Grouped Logs // Log 1 // Log 2 console.groupCollapsed("Collapsed Group"); console.log("Log 3"); console.log("Log 4"); console.groupEnd(); // > Collapsed Group التعقب باستخدام التابع ()console.trace يوجد التابع ()trace في معظم لغات البرمجة وهو أحد الخيارات المهمة للمبرمج لمعرفة مراحل تنفيذ الكود ضمن البرنامج، وهو يستخدم في عملية تصحيح الأخطاء ومعرفة مكان تنفيذ الكود. تعتبر عملية التعقب tracing مفيدة جدًا عندما يكون الكود طويلًا ومعقدًا نوعًا ما إذ يصعب في هذه الحالة تعقب الكود كاملًا بشكل منطقي. يوضح المثال التالي طريقة إضافة تعقب إلى الدالة ()example لمعرفة فيما إذا تم استدعاؤها من التابع ()one أو من التابع ()two. كما يوجد لدينا هنا شرط منطقي تتغير حالته بين true و false بالاعتماد على الزمن، لذلك من المستحيل معرفة أي من التابعين ()one أو()two سيقوم باستدعاء الدالة ()example // Is the "currentSeconds" value odd or even? const currentSeconds = new Date().getSeconds(); const condition = currentSeconds % 2 === 0; function one() { example(); } function two() { example(); } function randomChoice() { if (!condition) { // OK, I'm lost! one(); } else { two(); } } function example() { // Where is this function called? console.trace("Trace from example() function"); } randomChoice(); // console.trace() Trace from example function // example debugger eval code:23 // one debugger eval code:6 // randomChoice debugger eval code:16 نستنتج من هذا التعقب ما يلي: ينفذ التابع trace ضمن الدالة ()example في السطر 23 تستدعى الدالة ()example من قبل الدالة ()one في السطر 6 ينتهي التعقب عندما يتم استدعاء ()randomChoice في السطر 16 قد يكون استخدام التابع ()console.trace مفيدًا جدًا في حال كون الشيفرات البرمجية طويلة ومعقدة وغير مفهومة وذلك من أجل اكتشاف مصدر الأخطاء بسهولة أكبر. التنظيف باستخدام التابع ()console.clear يمسح التابع ()console.clear كافة التعليمات السابقة التي كتبتها ضمن الطرفية، وهو مفيد في حالة استخدام عدد كبير من الأوامر ضمن الطرفية والحاجة لتنظيفها وجعلها خالية من أي أمر سابق // Too much information! console.clear(); // Console was cleared. وبهذا الأمر البسيط والمفيد نكون قد وصلنا لختام مقال اليوم. الخلاصة تناولنا في هذا المقال عدة طرق لاستخدام الطرفية في لغة البرمجة جافا سكريبت JavaScript والتي يمكن أن يكون بعضها مألوفًا ومعروفًا بالنسبة لك كمطور، وبعضها الآخر جديدًا عليك سواء كنت في مستوى مبتدئ أو متقدم في تعلم لغة جافا سكريبت والتعامل معها. نتمنى لكم الاستفادة من هذا المقال، وفي حال كان لديكم أي تساؤل حول ما ورد فيه، يمكن تركه في قسم التعليقات أسفل المقال أو كتابته في قسم الأسئلة والأجوبة في الأكاديمية. ترجمة وبتصرف للمقال Developer essentials: JavaScript console methods لكاتبه Brian Smith اقرأ أيضًا كيف تستخدم أدوات المطوِّر في المتصفحات الحديثة أدوات مطوري الويب المدمجة في المتصفحات معالجة المشاكل الشائعة للتوافق مع المتصفحات في شيفرة جافاسكربت هيكل البرنامج في جافاسكريبت
-
سنتعرف في مقال اليوم على مفهوم استعلامات الحاوية Container Queries في لغة CSS التي أصبحت مدعومة مؤخرًا من قبل العديد من المتصفحات الرئيسية، إذا تسمح هذه الميزة بتطبيق تنسيقات CSS على عنصر ما بناءً على حجم هذا العنصر (أو حجم الحاوية الموجود فيها) بدلاً من حجم نافذة المتصفح ككل، أي تمكنك هذه الميزة من تغيير تصميم صفحات الويب بالاعتماد على أبعاد الحاوية التي تحتوي عناصر الصفحة، مما يوفر مرونة أكبر في التصميم وينظم الكود ويسهل صيانته. سنشرح في الفقرات التالية ما هي هذه الاستعلامات؟ وكيف يمكننا استخدامها لبناء تصاميم أكثر أناقة ومرونة؟ وهل ما زلنا بحاجة إلى استعلامات الوسائط Media Queries أم أن استعلامات الحاوية ستحل محلها؟ مشكلة استخدام استعلامات الوسائط في التصميم كي نفهم فائدة استعلامات الحاوية وكيف يمكننا استخدامها لنأخذ المثال التالي لتصميم موقع أخباري، فكما تلاحظ في الصورة يتألف موقعنا الإخباري من مجموعة من الأخبار أو المقالات التي تحتوي على صورة وعنوان ومحتوى نصي، كما يوجد شريط جانبي على الجانب الأيمن للصفحة يحتوي على المقالات الشائعة. يمكننا تقسيم هذا التصميم إلى جزأين أو شبكتين: شبكة على الطرف الأيسر مكونة من 4 أعمدة، وشبكة على الطرف الأيمن مؤلفة من عمود واحد على النحو التالي. لدينا مقالة مميزة كبيرة على اليسار تمتد على أربعة أعمدة، وإلى الأسفل منها يوجد لدينا مقالتان تمتد كل منهما على عمودين، ولكل مقالة من هاتين المقالتين تصميم أفقي مؤلف من صورة على اليسار ونص على اليمين، وتحت هاتين المقالتين يوجد لدينا أربع مقالات أصغر حجمًا تمتد كل منها على عمود واحد. كما يظهر عمود المقالات الشائعة بنفس التصميم كشريط جانبي على الطرف الأيمن للصفحة. نلاحظ في هذا الموقع وجود ثلاث تنسيقات مختلفة ومميزة والتي سنستعرض تاليًا جميع مكوناتها وطريقة تنفيذها. ولكن قبل هذا دعنا نرى ماذا سيحدث للتصميم عند استخدام حجم نوافذ عرض أصغر من خلال الشكل التالي: عندما تعرض الموقع باستخدام الهواتف المحمولة ستظهر الصور فوق النصوص في جميع المقالات بما في ذلك المقالة الرئيسية بحيث لا يمكن التمييز بين تصماميم هذه المقالات، وعند استعراضها في أحجام الشاشات الأكبر قليلًا سيكون للمقالات تخطيط أفقي، أما في نوافذ العرض الأكبر (كالحاسوب اللوحي على سبيل المثال) ستظهر مقالتان أفقيتان تحت المقالة الرئيسية وأربع مقالات موجودة أسفل هاتين المقالتين، وبالمحصلة النهائية سيكون لدينا ثلاثة تصميمات مختلفة للمقالات بحسب حجم نافذة العرض viewport. إذا أردت استخدام استعلامات الوسائط Media Queries لتنفيذ هذا التصميم من خلال الاستعلام عن حجم نافذة العرض، فستحتاج إلى إنشاء مكونات منفصلة للتعامل مع تصميم كل مقالة على حدة. وهذا يجعل الكود المكتوب غير عملي وصعب الصيانة ويتطلب تعديلات مستمرة لكل عنصر من عناصر التصميم بدلاً من الاعتماد على حجم الحاوية المحيطة بكل عنصر بشكل مباشر. وضع المحتوى ضمن المساحة المتوفرة إذا أراد أحد محرري الموقع وضع محتوى مختلف بجانب موجز الأخبار، ستكون النتيجة أن التصميم الذي قمنا به والمؤلف من خمسة أعمدة لن يكون مناسبًا لهذا الموقع، وبالتالي يتوجب علينا إعادة التصميم ووضع المقالات فوق بعضها البعض كما يلي. فعندما نستخدم استعلامات الوسائط لاكتشاف حجم إطار العرض أو نافذة العرض viewport، سيعتمد التصميم على أبعاد نافذة المتصفح بشكل عام، وليس على أبعاد العناصر الفردية داخل تلك النافذة، وهذا قد يؤدي لتصميم غير متجاوب بما فيه الكفاية في حال وجود عناصر ذات أحجام مختلفة ومتفاوتة بشكل كبير ضمن نفس نافذة العرض، والحل الأفضل في هذه الحالة هو استخدام استعلامات الحاوية container queries . ما هي استعلامات الحاوية Container Queries؟ استعلامات الحاوية هي الاستعلامات التي تسمح لك بالاستعلام عن حجم العنصر بدلًا من الاستعلام عن حجم نافذة العرض، ولتنسيق العناصر وفق ذلك يمكننا استخدام هذه الاستعلامات بطريقة مشابهة لاستخدام استعلامات الوسائط media queries لكن ستوفر لنا استعلامات الحاوية container queries مرونة أكبر بكثير عند إنجاز التصميم، كما أن استعلامات الحاوية تبسط الكود بشكل كبير. تخطيط عناصر صفحة الويب باستخدام استعلامات الحاوية لنأخذ على سبيل المثال أحد العناصر من موقعنا الإخباري وليكن أحد المقالات ونتعرف على كيفية إنشاء تصميم متجاوب له باستخدام استعلامات الحاوية container queries. إنشاء الحاوية تحتاج إلى عنصر حاوية للاستعلام عنه قبل البدء بكتابة استعلام الحاوية، وبما أن المثال الذي بين أيدينا هو موقع أخباري فإننا بحاجة إلى عنصر الحاوية الذي يغلف كل مقالة على حدة ويتعامل معها على أنها حاوية container. والاختيار الأنسب في هذه الحالة هو عنصر القائمة <li> نظرًا لأن موقعنا يعرض قائمة من المقالات، ولذلك سنضع صنف لعنصر القائمة يحمل الاسم article-container لتمييزها. <ul class="grid"> <li class="article-container"> <article>...</article> </li> <li class="article-container"> <article>...</article> </li> <li class="article-container"> <article>...</article> </li> ... </ul> سننشئ الحاوية باستخدام خاصيتين من خصائص CSS هما اسم الحاوية container-name و نوع الحاوية container-type، بالنسبة لاسم الحاوية فهو اسم اختياري ولكن يفضل اختيار اسم مناسب خاصة عندما نستخدم أكثر من حاوية ضمن الصفحة، وبما أن صفحتنا تتحدث عن المقالات سنختار لها اسم article. أما بالنسبة للخاصية container-type فيجب أن تأخذ القيمة inline-size وهذه القيمة هي قيمة منطقية تشير إلى العرض عندما نستخدم نظام الكتابة الأفقي وإلى الارتفاع عندما نستخدم نظام الكتابة العمودي (المستخدم في لغات مثل الصينية واليابانية التقليدية). .article-container { container-name: article; container-type: inline-size; } ويمكنك كتابة اسم ونوع الحاوية container في سطر برمجي واحد للاختصار كما يلي: .article-container { container: article / inline-size; } الاستعلام عن الحاوية بمجرد تعريف الحاوية يمكنك كتابة استعلام الحاوية بطريقة مشابهة لاستعلام الوسائط، ويمكنك اختيار اسم الحاوية عند كتابة الاستعلام أو تركها بدون اسم وفي هذه الحالة سيستخدم الاستعلام الحاوية الأقرب بشكل افتراضي. إذا لم تعرف الحاوية المناسبة، فسيكون سلوك الاستعلام مشابهًا لسلوك استعلام الوسائط، وبالتالي سيتم الاستعلام عن حجم إطار العرض viewport لا عن حجم العنصر المحدد. /* Without a named container */ @container (width > 700px) { /* Styles applied to elements within any container when the container is over 700px wide */ } في هذه الحالة ستطبق التنسيقات على أي عنصر داخل أي حاوية container عرضها أكبر من 700 بكسل. من المفيد بالطبع تحديد حاوية ذات اسم مميز حتى نتأكد من الحاوية التي نستعلم عنها ولتجنب المشكلات خاصة عند التعامل مع تصاميم تحتوي على حاويات متداخلة فيما بينها كما يلي: /* Specifying a named container */ @container article (width > 700px) { /* Styles applied to elements within the 'article' container when the container is over 700px wide */ } ويمكنا بشكل مشابه تمامًا لاستعلام الوسائط استخدام عدة استعلامات حاوية، لاحظ الكود التالي استخدام استعلامات الحاوية لجعل تصميم العناصر يتكيف بناءً على حجم الحاوية، إذ حددنا أنماطًا مختلفة للعناصر داخل الحاوية بناءً على عرض الحاوية لتحقيق مرونة أكبر في التصميم واستجابة أفضل للعناصر المتداخلة داخل الحاويات المختلفة. .article-container { container: article / inline-size; } article { display: grid; gap: 1rem; } @container article (inline-size > 500px) { /* Styles for horizontal article */ article { grid-template-columns: 1fr 2fr; } } @container article (inline-size > 800px) { article { /* Styles for article in a large space (e.g. featured article) */ grid-template-columns: 1fr 1fr; gap: 2rem; font-size: 1.2rem; } h3 { font-size: 2rem; } } صيغة استعلام الوسائط الجديد ربما تلاحظ اختلافًا بسيطًا بين طريقة كتابة استعلامات الحاوية وطريقة كتابة استعلامات الوسائط العادية، فبدلًا من استخدام الخاصتين min-width و max-width، نستخدم صيغة مجال استعلام الوسائط، وهذه الميزة تقلل عدد الاستعلامات. /* Old syntax for styles between 700px and 900px */ @container (min-width: 700px) and (max-width: 900px) { } /* New syntax */ @container (700px <= width <= 900px) { } وهذه الاستعلامات ملاءمة أكثر في حال استخدام الخصائص أو الشروط المنطقية، فبدلًا من كتابة تنسيقات تعتمد على الحد الأدنى والحد الأعلى للعرض، نستطيع أن نقول: إذا كان الحجم أكبر من أو أصغر من قيمة معينة طبق التنسيقات التالية عليه. لنعد كتابة استعلام الحاوية في مثالنا السابق باستخدام الخاصية inline-size بدلًا من الخاصة min-width @container article (inline-size > 700px) { article { /* Styles for horizontal article */ } } يطبق التنسيق في هذه الحالة إذا كانت الحاوية article تمتد لعرض يزيد على 700 بكسل. الحاويات المتداخلة Nested Containers أحيانًا يكون لديك تصميم يحتاج إلى التجاوب مع المساحة المتوفرة وليس فقط مع العناصر الفردية، وتغيير الطريقة التي تُرتب بها العناصر ضمن شبكة أو تخطيط معين. في هذه الحالة، يمكن استخدام الحاويات المتداخلة، على سبيل المثال يحتاج تصميم الشبكة في مثالنا للاستجابة للمساحة المتوفرة إضافًة إلى حاجته إلى مكونات article متجاوبة. وهذا يعني أننا بحاجة إلى إنشاء حاوية إضافية محيطة بشبكتنا، وبالتالي سنحتاج إلى تعديل كود HTML لتضمين الحاوية الإضافية <div class="grid-container"> <ul class="grid"> <li class="article-container">...</li> <li class="article-container">...</li> <li class="article-container">...</li> ... </ul> </div> أما كود CSS فسيصبح كما يلي: .grid-container { container: layout / inline-size; } سنكتب في الكود التالي استعلامات حاوية مخصصة لعمودين. واستعلامات حاوية مخصصة لأربعة أعمدة على التناوب في حال توفر المساحة الكافية. /* Initial styles for single column layout */ .grid { display: grid; gap: 1rem; } @container layout (inline-size > 800px) { .grid { grid-template-columns: repeat(2, 1fr); } /* The featured article should span two columns */ .article-container:first-child { grid-column: span 2; } } @container layout (inline-size > 1000px) { .grid { grid-template-columns: repeat(4, 1fr); } .article-container:first-child { grid-column: span 4; } .article-container:nth-child(2), .article-container:nth-child(3) { grid-column: span 2; } } إذا غيرت حجم العنصر أو المكون، ستلاحظ كيف ستستجيب كل من تصميم الشبكة والمقالات المتداخلة للمساحة المتوفرة. وحدات الحاوية Container Units من خبرتك السابقة، قد تكون على دراية بالفعل بوحدات قياس إطار العرض viewport units المستخدمة لتحديد حجم العناصر بما يتناسب مع حجم نافذة العرض مثل vw التي تمثل نسبة مئوية من كامل عرض النافذة و vh التي تمثل نسبة مئوية من كامل ارتفاع النافذة، أما وحدات الحاوية container units فهي تمكنك من تحديد حجم العناصر بما يتناسب مع حجم الحاوية وهذه الوحدات مفيدة بشكل خاص في تنسيق المقالات في موقعنا الأخباري، حيث يمكننا استخدامها لتحديد الحواشي padding وحجم الخط font-size، مما يقلل الحاجة إلى ضبط النقاط الحديّة breakpoints المختلفة. تُستخدم وحدة cqi لتحديد النسبة المئوية من الحجم المضمن للحاوية inline-size كما في المثال التالي: article { padding: 4cqi; } يحدد هذا الكود قيمة الكود الحواشي padding للعنصر article لتكون 4% من العرض الداخلي لحاوية العنصر. ملاحظة: لا داعي لكتابة هذه التنسيقات داخل استعلام الحاوية، فمادمت قد عرّفتها في البداية فستكون جاهزة للاستخدام ضمن الحاوية. نستطيع أن نستخدم التابع ()clamp لمنع الحاشية padding من أن تكون كبيرة جدًا أو صغيرة جدًأ. وسيجد هذا التابع بدوره القيمة الوسطى middle value عن طريق تمرير قيمتين ثابتتين بالإضافة إلى القيمة الممررة cqi وبذلك نستطيع التأكد من أن القيمة padding لن تكون أصغر من 1rem ولا أكبر من 3rem. article { padding: clamp(1rem, 4cqi, 2rem); } ويمكن بنفس الطريقة لضبط حجم الخط بحيث يتكيف مع حجم الحاوية عن طريق التابع clamp ووحدات الحاوية container units. article { padding: clamp(1rem, 4cqi, 2rem); font-size: clamp(1rem, 2cqi, 3rem); } دعم المتصفحات والخيارات الاحتياطية أصبحت استعلامات الحاوية مدعومة في جميع المتصفحات الرئيسية مؤخرًا، ولكن لازال معظم المستخدمين يستخدمون متصفحات قديمة، ولتلبية احتياجاتهم ما علينا سوى استخدام الملف polyfill المقدم من فريق العمل في google chrome. حيث يوفر هذا الملف دعمًا لاستعلامات الحاوية في المتصفحات التي لا تدعمها بشكل أصلي عن طريق تحويل استعلامات الحاوية إلى استعلامات وسائط مع استخدام خصائص CSS مخصصة للحفاظ على نفس السلوك. هذا يمكن من استخدام ميزات استعلامات الحاوية الجديدة دون التخلي عن المستخدمين الذين لديهم متصفحات أقدم. استعلامات التنسيق Style queries لقد ناقشنا فيما سبق عملية بناء التنسيق باستخدام استعلامات الحاوية للاستعلام عن الحجم الداخلي للعنصر، ولكن يمكن أن تطبق استعلامات الحاوية على أمور أخرى مثل الاستعلام عن تنسيقات العنصر المطبقة على خصائص محددة. بالعودة إلى موقعنا الإخباري ربما نرغب في لفت الانتباه إلى المقالات المميزة والتي قد يكون لها تنسيقات مختلفة، يمكننا عمل ذلك من خلال وضع خاصية محددة ضمن الكود كما يمكننا تطبيق التنسيقات على العناصر ذات التي تحمل قيمة محددة لهذه الخاصية. <div class="grid-container"> <ul class="grid"> <li class="article-container">...</li> <li class="article-container">...</li> <li class="article-container" style="--featured: true">...</li> ... </ul> </div> @container style(--featured: true) { article { border-radius: 0.2rem; background: pink; border: 1px solid deeppink; padding: clamp(1rem, 5cqi, 3rem); } } لاحظ أننا لا نحتاج في هذا الكود إلى تعريف الحاوية container حيث أن كل عنصر يحتوي تلقائيًا على التنسيق الخاص به. القيود ودعم المتصفحات حتى وقت كتابة هذا المقال يدعم كل من متصفح Chrome ومتصفح Edge استعلامات تنسيق الحاوية بينما لا يدعم المتصفحان Firefox و Safari هذه التقنية، والسبب في ذلك هو عدم الإيفاء ببند موجود في مواصفات المستوى 3 لوحدة احتواء CSS للاستعلام عن الخاصية والقيمة معًا وليس الاستعلام عن خاصية محددة. وعلى كل حال لا توجد متصفحات تدعم هذه الخواص حتى الآن. هل ما زلنا بحاجة إلى استعلامات الوسائط من خلال استخدامنا لاستعلامات الحاوية أصبح بإمكاننا الاستعلام عن حجم إطار العرض وحجم العنصر، وهنا يتبادر إلى أذهاننا السؤال التالي: هل ما زلنا بحاجة إلى استخدام استعلامات الوسائط في الكود؟ الجواب القطعي هو نعم لأن استعلامات الوسائط تستخدم في أمور أكثر بكثير من موضوع الحجم.على سبيل المثال لا الحصر يمكننا من خلال استخدام استعلامات الوسائط اكتشاف تفضيلات المستخدمين من خلال التابع prefers-reduced-motion والتابع prefers-color-scheme. من المحتمل أن نحتاج في بعض الحالات إلى تصميم عنصر ضمن حاوية متداخلة وفقًا لحجم إطار العرض، في هذه الحالة ربما يكون استخدام استعلام الوسائط هو الطريقة الأبسط، ولكننا نعتقد أن استعلامات الحاوية ستحل مكانها في المستقبل غير البعيد حيث ستأخذ استعلامات الحاوية مكان استعلامات الوسائط عندما يتعلق الأمر بتخطيط التصميم أما استعلامات الوسائط فسوف تحافظ على استخداماتها التي ذكرنا فيما سبق من هذا المقال. الخلاصة تساعدنا استعلامات الحاوية في الوصول إلى تصاميم سريعة الاستجابة بالإضافة إلى مساعدتنا في كتابة كود CSS مختصر وقوي وقابل للصيانة. جرب البدء بالتعامل مع استعلامات الحاوية مع عناصر صفحتك، وإذا كان هناك عنصر ما تواجه صعوبة في إنشائه باستخدام استعلامات الحاوية، يمكن أن ننشئه باستخدام استعلامات الوسائط كالمعتاد. نتمنى لكم الاستفادة الجيدة من هذا المقال، وفي حال كان لديكم أي تساؤل يمكن تركه في قسم التعليقات أسفل المقال أو كتابته في قسم الأسئلة والأجوبة في الأكاديمية. ترجمة وبتصرف للمقال Getting started with CSS container queries لكاتبته Michelle Barker. اقرأ أيضًا تعرف على أساسيات لغة CSS الدليل السريع إلى لغة التنسيق CSS وحدات القياس والقيم في CSS تحجيم الأشياء في CSS
-
مع التطور الكبير لتقنيات الذكاء الاصطناعي وظهور روبوت الدردشة ChatGPT وغيره من النماذج اللغوية برزت أسئلة كبيرة حول مستقبل مطوري ووردبريس، وكيف يمكن للذكاء الاصطناعي أن يساعدك كمطور ووردبريس ولا يكون ضرراً عليك ويتسبب في خسارتك لوظيفتك. سنقدم لك في هذه المقالة بعض الطرق الحديثة التي يمكن للذكاء الاصطناعي وتحديدًا روبوت ChatGPT أن يساعدك بها مطوري ووردبريس. فبعد قراءتك لهذه المقالة وتجريب بعض التطبيقات المشروحة فيه ستدرك كيف يمكنك الاستفادة من الذكاء الاصطناعي في تسريع إنشاء تطبيقاتك ورفع إنتاجيتك. سنناقش في هذا المقال الأمور التالية: لمحة عامة عن بوت المحادثة الذكي شات جي بي تي ChatGPT. الطرق المختلفة التي يمكن فيها لشات جي بي تيChatGPT مساعدتك كمطور ووردبريس. ولنبدأ بمناقشة هذه النقاط بالتفصيل في الفقرات التالية: لمحة عامة عن ChatGPT إن مصطلح ChatGPT هو اختصار لعبارة Chat Generative Pretrained Transformer وهو عبارة عن أداة من أدوات الذكاء الاصطناعي طورتها شركة الأبحاث OpenAI لتكون أداة قوية جدًا تستخدم طريقة التعليم المعزز والتعليم الخاضع للإشراف للإجابة على الأسئلة المعقدة بفعالية كبيرة. يعد ChatGPT أحد تطبيقات الذكاء الاصطناعي التوليدي Generative AI وهذا يعني أنه عندما يتم تدريبه أو تعليمه فسيكون بإمكانه توليد مخرجات مشابهة للأمثلة التي تم تدريبه عليها، وبالتالي يمكنه مساعدتك في كثير من الأمور مثل كتابة الكود، وصياغة المقالات، وكتابة رسائل البريد الإلكتروني وغيرها من المهام. الطرق التي يساعدك ChatGPT فيها كمطور ووردبريس يقدم لك ChatGPT العديد من المميزات لمطوري ووردبريس ويساعدهم على تحسين بيئة العمل البرمجي، وفيما يلي نقدم لك أبرز هذه الميزات: توليد الكود البرمجي اختبار الكود التوثيق التقني تصحيح الأخطاء توليد التعليقات تبسيط الأكواد المعقدة معالجة البيانات هندسة المميزات توليد كود برمجي بديل تطوير عملية النمذجة إجراء عمليات البحث لنناقش كل ميزة من هذه الميزات بمزيد من التفصيل. 1. توليد الكود البرمجي يعد روبوت الدردشة ChatGPT أداة مفيدة جدًا لمطوري ووردبريس، وخاصة عندما يتعلق الأمر بكتابة الأكواد البرمجية، فهو يزودك بشيفرات تفهم احتياجاتك وتلبيها ويوفر عليك الكثير من الوقت والجهد في كتابتها. ولا يتوقف الأمر على كتابة أكواد بسيطة فحسب، بل يمتلك ChatGPT القدرة على كتابة تعليمات برمجية للمهام المعقدة مثل إنشاء أصناف برمجية classes بالكامل، فحتى إن لم تكن على معرفة كاملة بكتابة كود معين فإن ChatGPT سيتولى الأمر بنفسه ويشرح لك الكود الذي قام بكتابته مما يجعله بمثابة دورة مرشد خاص يعلمك أساسيات كتابة هذا الكود. لتوضيح قدرات ChatGPT في كتابة الشيفرات البرمجية، طلبنا منه كتابة كود يستطيع الزبائن من خلاله اختيار نوع الطعام المفضل لهم من بين أنواع مختلفة، والمفاجأة أن ChatGPT كتب الكود اللازم في غضون لحظات. كان السؤال أو المطالبة الموجهة له بالضبط على النحو التالي: اكتب الكود البرمجي اللازم الذي يُمكّن الزبائن من اختيار عناصر متنوعة من الطعام. وهذا هو الجواب الذي حصلنا عليه: هنا ستجد مثال لكود برمجي مكتوب بلغة HTML يستطيع الزبائن من خلاله اختيار عناصر متنوعة من الطعام وفي حال عدم رضاك عن النتيجة التي حصلت عليها، يمكنك إعادة المطالبة مرة أخرى لتوليد كود آخر لنفس الموضوع، إذ سيقدم لك نتيجة أخرى ويوضح لك ما الذي قام به بالتحديد كما هو موضح بالصورة التالية: ولن يكتفي بكتابة الكود كما ذكرنا بل سيشرح لك الخطوات التي قام بها على النحو التالي: من خلال هذا المثال يمكن أن نخلص للتالي: يمكنك كمطور ووردبريس أن توفر وقتك في كتابة الأكواد التي تستغرق وقتاً طويلاً وتكلف ChatGpt بهذه المهمة وتتفرغ لحل المشكلات الأخرى التي تواجهك في بناء التطبيق. 2. اختبار الكود البرمجي يعد أيضاً ChatGPT أداة مفيدة جدًا لمطوري ووردبريس فيما يخص اختبار الكود البرمجي، فهو يوفر عدة طرق للمساعدة في هذه العملية ويتمتع ChatGPT بقدرة عالية جدًا على فهم الكود الذي تدخله له وتحليله، وتوليد حالات اختبار تغطي الحالات المختلفة للكود مما يوفر وقتك وجهدك كمطور. كما يولد ChatGPT توثيقًا سهلًا وواضحًا لحالات الاختبار المختلفة، ويتضمن هذا التوثيق مدخلات الاختبار، والحالات المتوقعة للخرج، وشروط الاختبار، مما يؤدي إلى تبسيط عملية الاختبار إلى حد كبير. إن توفر هذه المعلومات القيّمة لك كمطور ووردبريس تساعدك على تحديث ومراجعة الكود بسهولة وسرعة أكبر، وتحديد المتغيرات المطلوبة بناءاً على نتائج هذا الاختبار، مما يؤدي إلى بناء تطبيق فعال وخالٍ من الأخطاء. 3. التوثيق التقني قد تكون عملية التوثيق مهمة شاقة ومتعبة للمطورين، ولحسن الحظ يستطيع ChatGPT مساعدتك بشكل رائع في هذا الأمر بفضل المهارات اللغوية التي يمتلكها. فكل ما عليك القيام به ببساطة هو تزويد ChatGPT ببعض التعليمات البرمجية ثم تطلب منه أن ينشئ توثيقًا لهذه التعليمات وستجد أنه سيؤدي هذه المهمة على أكمل وجه وينشئ لك توثيقًا واضحًا وسهلًا ويشرح من خلاله الكود والمدخلات والمخرجات وأي معلومات مهمة أخرى. على سبيل المثال إذا زودنا ChatGPT بجزء من الكود الذي قام هو بتوليده في الفقرة السابقة، ثم طلبنا من أن ينشئ لنا توثيقًا لهذا الكود ستكون النتيجة مشابهة للتالي: كما يزودك ChatGPT بمعلومات أكثر تفصيلاً حول معاملات أو وسطاء كل دالة والخرج النتائج ويوفر اقتراحات للتعامل مع الأخطاء والكثير من الأمور الأخرى، وقد يكون استخدام ChatGPT في عملية التوثيق غير مثالي حاليًا من ناحية دقة المعلومات، ومع ذلك فإنه يستطيع أن يقدم تصورًا جيدًا عن الطريقة المتبعة في عملية التوثيق ويسرع بناء توثيقات البرمجيات ليريحك كمطور ووردبريس من هذه المهمة الممللة ويمكنك من التفرغ لتطوير تطبيقاتك بفعالية أكبر. 4. معالجة أو تصحيح الأخطاء في حال كنت بحاجة إلى تعقب الأخطاء في برنامجك، وتريد تطبيق ممارسات عملية جيدة على الكود فسياعدك استخدام ChatGPT في العثور على الأخطاء البرمجية وفحص أجزاء معينة من الكود وتقديم اقتراحات مفيدة تساعدك في تشخصيص أي مشكلة وحلها بدلاً من أن تمضي ساعات طويلة من البحث المضني عن الأخطاء بنفسك. كما تستطيع أيضاً من خلال ChatGPT إعادة بناء الكود وذلك بفضل تقديم ChatGPT لاقتراحات تعديل لزيادة الوثوقية وقابلية صيانة الكود بحيث يكون هناك عدد أقل من الأخطاء في البرنامج، على سبيل المثال سنعطي ChatGPT كود جافاسكريبت التالي ونطلب منه اكتشاف الأخطاء الموجودة فيه: سيعطينا النتيجة التالية: وسيعطي اقتراحًا للكود المصحح أيضًا: إن عملية تصحيح الأخطاء باستخدام ChatGPT يمكن أن توفر الكثير من الوقت والجهد خاصة في التطبيقات الكبيرة والمعقدة. 5. توليد التعليقات يمكن أن يوفر ChatGPT ميزة كبيرة وقوية لمطوري ووردبريس وهي التعليق على الكود البرمجي، لتوفير فهم أكبر لما تقوم به التعليمات البرمجية من خلال وضع شرح واضح ومحدد لها. وستلاحظ أن ChatGPT يكتب تعليقات على الكود بصورة ذاتية، لكن يمكنك أن تطلب منه وضع تعليقات أوفى على تعليمات بعينها. ستساعد ميزة كتابة التعليقات هذه على فهم الكود الذي كتبته بشكل أكبر فالتعليقات تعد نوعًا من أنواع التوثيق المفيد الذي يوضح طبيعة الكود مما يجعل الأمر أكثر بساطة لك وللمطورين الآخرين لفهم وظائف الكود والعمل عليه بشكل جماعي. علاوة على ذلك يستطيع ChatGPT شرح وتوضيح جزء معين من الكود وهذه الميزة تكون مفيدة بشكل خاص عندما لا تحتاج أنت كمطور إلى كتابة توثيق كامل للمشروع بل لجزء محدد منه. على سبيل المثال عندما طلبنا من ChatGPT أن يضيف تعليقًا يشرح لنا الكود الموضح بالصورة: كان التعليق على النحو التالي: // الحصول على جميع العناصر في المستند التي تحتوي على الصنف "my-button" // وتخزينها في متغير يسمى "buttons". var buttons = document.getElementsByClassName('my-button'); 6. تبسيط الكود المعقد يسمح ChatGPT للمطورين أن ينشؤوا شروحات بسيطة للكود المعقد، مما يسهل على المطورين الآخرين فهم وصيانة هذا الكود، فيمكن لـGhatGPT إنشاء توثيق وتعليقات للكود كما رأينا ثم يقوم الذكاء الاصطناعي بتبسيط هذا الكود وتوضيحه مما يوفر الكثير من الوقت والجهد على المطور. بالإضافة إلى ذلك ومع القدرة على تبسيط الكود المعقد غالباً ما يكون الكود الذي يكتبه ChatGPT أكثر دقة من الكود الذي يكتبه المطور نفسه. لهذا السبب تعد عملية شرح وتبسيط الكود المعقد في مقاطع صغيرة مفيدة جدًا للمطورين من أجل فهم الكود الصعب وشرحه بوضوح وبساطة لأن ChatGPT يستطيع فهم وتحليل كل سطر من أسطر الكود على حدة ويشرح آلية عمله بالتفصيل. 7. معالجة البيانات إذا كنت بحاجة إلى مساعدة في عملية معالجة البيانات فيمكنك الاستعانة بـGhatGPT في توليد كود مهمته تنظيف وترتيب البيانات وتجهيزها من أجل نماذج الاختبار والتدريب، وهذا يتضمن إدارة المهام مثل تشفير المتغيرات والتعامل مع القيم المفقودة أو حسابها وذلك من خلال عدد من الاستراتيجيات. كما يمكن إنشاء ميزات جديدة من البيانات الموجودة مثل توليد كود يستخرج المعلومات من البيانات غير المهيكلة (مثل النصوص أو الصور) ثم يحول هذه المعلومات إلى بيانات مهيكلة تستخدم في عملية تعلم الآلة machine learning وهذا يجعل عملية المعالجة المسبقة أبسط وأكثر كفاءة ودقة. 8. هندسة المميزات Feature Engineering يساعد ChatGPT في تحسين ميزات البيانات المستخدمة في تعلم الآلة. وتحسين الميزات يعني تحسين جودة وكفاءة البيانات التي تستخدمها النماذج لتحقيق نتائج أفضل. فعندما تمتلك نموذج للتعلم الآلي مع بيانات مختلفة يستطيع ChatGPT مساعدتك لدرجة كبيرة وتحسين الأداء. لذا تساعد هندسة المميزات التي يقدمها ChatGPT في تقليل أخطاء مجموعات البيانات وتحسين أداء النموذج وزيادة دقته وموثوقيته. 9. توليد الكود البديل هذه الميزة مخصصة لمطوري ووردبريس الذين يرغبون في إنتاج كود بديل يعمل على تحسين الأداء إذ يمكن للمطور أن يأخذ أي جزء من الكود القديم ثم يطلب من ChatGPT اقتراح تحسينات على هذا الكود، وربما تكون أحد التحسينات المقترحة هي الحد من استخدام الذاكرة أو ربما تخفيض عدد العمليات الحسابية أو المنطقية في الكود. على سبيل المثال طلبنا من ChatGPT إنشاء كود بديل للكود الموضح بالصورة التالية: فكان جوابه أن قدم لنا الكود الموضح بالصورة التالية: ملاحظة: هذا جزء من الكود فقط ثم قدم الشرح التالي: 10. تطوير عملية النمذجة ونشرها يستطيع ChatGPT المساعدة في عملية إعداد البيانات النصية وتنظيفها، مما يؤدي إلى تحسين أداء النموذج وجعله ينشئ نصًا يشبه النص الذي ينتجه الإنسان، ويمكن استخدام هذا النص لمحاكاة عملية الاتصال بين الإنسان والحاسوب، وهذا مفيد جدًا عندما نريد تطوير أنظمة محادثة خاصة بالذكاء الاصطناعي مثل نظام رد آلي chatbot. كما يستطيع ChatGPT إنشاء شروحات باللغة الطبيعية لتنبؤات النموذج وهذا يساعد النموذج ليشرح الأمور بسهولة أكبر. ويمتلك ChatGPT قوة كبيرة لتوليد نص باللغة الطبيعية البشرية والتي تفيد في سياق العمل والتواصل مع الآخرين. 11. عمليات البحث ستحتاج كمطور ووردبريس لبعض الأسئلة خاصة أسئلة البرمجة المعقدة وسيكون ChatGPT خير صديق لك في هذا المجال من خلال تقديم الإجابات على جميع أسئلتك حيث يمكنك استخدام ChatGPT كمحرك بحث حيث بإمكانك أن تطرح عليه الأسئلة المعقدة وأن تحصل منه على إجابات دقيقة بسرعة وسهولة بدلاً من البحث في العديد من المصادر كما أنه يقدم لك أمثلة تحتوي على تعليمات برمجية مع إجاباته حتى تتمكن من معرفة فيما إذا كانت تلك الإجابات كافية أم لا. على سبيل المثال طلبنا من ChatGPT إنشاء مكتبة تضيف ميزة منبثقة pop-up لموقع الويب فكان جوابه كما يلي: ملاحظة: هذا جزء فقط من الإجابة التي قدمها ChatGPT لتوضيح الفكرة. وختامًا نذكرك أن الذكاء الاصطناعي ليس دقيقاً دائماً بنسبة 100% إلا أن بإمكانه في أغلب الأحيان توفير معلومات مفيدة ودقيقة فيما يتعلق بأسئلة البرمجة والتطوير، لذا من الضروي أن تتحقق دومًا من دقة وصحة الإجابات التي يوفرها لك، وهذا يقودنا إلى الأسئلة التالية. هل يمكننا الوثوق بأجوبة ChatGPT؟ يجب أن نعلم كمطورين ووردبريس أن ChatGPT أداة قوية ومفيدة ولكن يوجد فيها بعض العيوب التي يجب علينا فهمها والدراية بها وأولها أن ChatGPT ليس دقيقًا دائماً فهو يعتمد على الأسئلة الموجهة له فعندما تكون الأسئلة غير منطقية ستكون الإجابات غير منطقية كذلك! وتتكون بيانات التدريب الخاصة بالنموذج من مجموعة واسعة من المحتوى المكتوب من قبل البشر وبالتالي يمكن أن تكون أجوبة ChatGPT أحياناً غير دقيقة وغير موضوعية حسب المحتوى المكتوب وبمعنى آخر الدخل الخاطئ يؤدي إلى نتائج خاطئة. باختصار يجب توخي الحذر وعدم الوثوق بكل مخرجات أو أجوبة ChatGPT بشكل أعمى على أنها صحيحة والجيد في الأمر أنه يخضع لتحسينات مستمرة ليصل إلى مرحلة توفير إجابات دقيقة، ومع ذلك يمكنه حاليًا أن يقدم تصورات معينة تفيد المطورين ككل، ومطوري ووردبريس على وجه الخصوص. فلا يجب عليك كمطور الاعتماد على ChatGPT لكتابة الأكواد البرمجية بشكل كامل بالطبع، ولكن يمكنك الاستفادة منها في تسريع إنجاز بعض الأمور التي يتطلبها المشروع الذي تقوم به. لماذا نستخدم الذكاء الاصطناعي؟ مع استمرار تقدم تقنيات الذكاء الاصطناعي وأبرزها ChatGPT، بات من المهم جدًا البقاء على اطلاع كامل بالتغيرات المتسارعة التي تحصل في هذه التقنيات وهذا سيساعدك في فهم فوائد استخدام الذكاء الاصطناعي، واتخاذ القرارات المناسبة التي تخدم مسيرة عملك كمطور. ويمكن أن يكون ChatGPT أداة قيمة لمطوري ووردبريس (على الرغم من أنها ليست مثالية) من خلال الاستفادة من إمكانياتها المشروحة سابقًا لتوفير الوقت والتركيز على الجوانب الأخرى في بيئة تطوير ووردبريس الخاصة بك. في النهاية لا داعي للقلق بشأن استبدال الذكاء الاصطناعي لوظيفتك، على العكس يجب عليك اعتباره تقنية مفيدة لك تساعدك في عملك عند استخدامها بشكل جيد الأمر الذي يؤدي إلى زيادة إنتاجيتك، وخفض التكاليف، وتوفير الوقت، وضمان إنشاء تطبيقات ووردبريس عالية الجودة. نتمنى لكم الاستفادة الجيدة من هذا المقال، وفي حال كان لديك أي تساؤل حول ما ورد في المقال يمكن تركه في قسم التعليقات أسفل المقال أو كتابته في قسم الأسئلة والأجوبة في الأكاديمية. ترجمة وبتصرف للمقال 11Ways ChatGPT Can Help You As a WordPress Developer لكاتبيه N. Fakes & Martin Aranovitch اقرأ أيضًا دليل استخدام ChatGPT API لتحسين خدماتك عبر الإنترنت تطوير تطبيق 'اختبرني' باستخدام ChatGPT ولغة جافاسكربت مع Node.js بناء تطبيق بايثون يجيب على أسئلة ملف PDF باستخدام الذكاء الاصطناعي تدريب بوت المحادثة ChatGPT وتعليمه كيف يتحدث ويتعلم
-
يستخدم كل من ووردبريس بدون رأس Headless WordPress وواجهات برمجة التطبيقات REST API ضمن بيئة تطوير ووردبريس بشكل كبير مؤخرًا، ويحتاج المطورون لامتلاك مجموعة من الأدوات القياسية التي يرغبون في استخدامها عند العمل على هذه النوع من المشاريع. سسنشرح في مقال اليوم طريقة التعامل مع مفهوم ووردبريس مقطوع الرأس (بلا رأس) باستخدام مجموعة من الأدوات وعلى الرغم أننا لا نؤكد أن مجموعة الأدوات التي سنستخدمها ينبغي أن تكون قياسية إلا أننا نرى أن هذه الادوات ستكون مناسبة للاستخدام عند بناء تطبيقاتنا باستخدام REST API، وهذه الأدوات هي : MailHog: وهي أداة اختبار SMTP تعتمد على الويب وواجهة برمجة التطبيقات. Insomnia: هو عميل REST قوي متعدد الأنظمة يستخدم لاختبار واجهات برمجة التطبيقات. JWT Auth: هي أداة شائعة الاستخدام لتأمين واجهات برمجة التطبيقات. تطبيقات ووردبرس بدون رأس Headless WordPress باستخدام REST API لنتعرف في الفقرات التالية على أهم المتطلبات والخطوات التي تحتاجها لتطور تطبيقات ووردبريس بدون رأس مع توضيح حالات الاستخدام المفيدة لهذا النوع من التطبيقات. المتطلبات قبل النظر في استخدام هذه الأدوات في بناء مشروع فعلي يجب عليك تجهيز الحاسوب الذي سيستخدم هذه الأدوات. وعلى الرغم من أنك لن تقوم بذلك في مشاريع التطوير الفعلية لكن تجهيز بيئة تطوير محلية يفيدك في مرحلة التعلم واختبار التطبيق وإليك قائمة بأهم الأدوات التي ستحتاجها: خادم ويب يدعم لارافيل مثل Valet نظام إدارة قواعد البيانات مثل MariaDB لغة البرمجة PHP (أي إصدار أعلى من 7.4.33 حسب نوع الخادم الذي تشغل المشروع عليه) ولمزيد من المعلومات حول إعداد بيئة تطوير محلية يمكنك الاطلاع على مقال تثبيت وضبط تطبيق لارافيل مع خادم Nginx على حزمة LEMP من أوبنتو ومقال دليل إعداد خادم ويب محلي خطوة بخطوة سنستخدم أيضاً الأدوات التالية بالنسبة لبيئة التطوير الخاصة بنا: Visual Studio Code Insiders: لتحرير الأكواد. PHPDoc Comment: لتوثيق الأكواد. PHP Sniffer & Beautifier مع PSR12: لتحليل وتنسيق كود PHP والتأكد من مطابقته للمعايير والقواعد البرمجية. PHP Debug: لمراقبة تطبيقات PHP وتتبع أخطائها. Ray: أداة تسهل تصحيح الأخطاء البرمجية. عملياً إذا كنت على معرفة بكل الأدوات والتقنيات السابقة وكان لديك الإعدادات الخاصة بك أو كنت معتاد على بناء التطبيقات أو الحلول باستخدام ووردبرس فقد يكون كل ما سبق لا يعنيك. وإلا يجب عليك أن تأخذ بعض الوقت لتتعرف على كل ما ورد أعلاه. دراسة حالة بسيطة: بفرض طلب منك تطوير تطبيق ووردبرس بدون رأس Headless WordPress يوفر نقاط نهاية مخصصة endpoints ويمكن لتطبيق iOS الاتصال بها لتسجيل الدخول والقيام بالاستيثاق أو التأكد من صحة البيانات وإرسال واستقبال البيانات. سيقدم تطبيق ووردبريس الوظائف التالية بالتحديد: إنشاء حساب جديد للمستخدم. الحصول على معلومات المستخدم. طلب مفتاح التشفير المساعد token. التحقق من المفتاح المساعد token. ضبط بيانات المستخدم. إن هذه البنود ليست كاملة ولكنها كافية لتوضيح طريقة استخدام الأدوات المختلفة في بناء تطبيقات مماثلة.كما أن الوظائف المذكورة في التطبيق يمكنها أن توفر عبر نقاط الوصول endpoints التالية: /acme/v1/getUser /acme/v1/createUser /jwt-auth/v1/token /jwt-auth/v1/token/validate /acme/v1/setData ملاحظة: إن acme هنا عبارة عن قيمة مؤقتة placeholder تعبر عن اسم التطبيق. أما jwt-auth فهو اسم المكتبة الخاصة بعملية المصادقة (اسم المستخدم وكلمة المرور) التي نقوم باستخدامها وهذا ما سيتم مناقشته لاحقاً في هذه المقالة توثيق واجهة برمجة التطبيقات API: يعد توثيق واجهة برمجة التطبيقات API شرطاً أساسياً من شروط نجاحها، وبما أننا نعمل على إنجاز تطبيق بدون رأس Headless فمن المهم جداً تنفيذ واجهة جميلة موثقة جيدًا للتطبيق، وسننفذ واجهة تطبيقنا باستخدام REST API ليتفاعل معها المستخدمون. لكن واجهة برمجة التطبيق API لا ينبغي أن تكون كصندوق أسود نتفاعل معها دون الاهتمام بتوثيقها وتوضيح تفاصيلها الداخلية إذ يحتاج المطور لمعرفة رأي المستخدمين في التطبيق الذي أنشأه فربما يكتشف المستخدمون بعض الثغرات التي تجاهلها المطور عند بناء التطبيق. وهناك أسباب أخرى تدعونا إلى توثيق واجهة برمجة التطبيقات APIs حتى لو لم يكن التطبيق متداول على نطاق واسع. وهذه الأسباب هي: الاتصال الجيد بين نقاط الوصول والوسطاء والخرج المتوقع. سهولة تواصل المطورين الجدد مع الواجهة. سهولة العودة لتصحيح الأخطاء في حال ظهورها في التطبيق المقدم للعميل. وجود عدة إصدارات من واجهة برمجة التطبيق API يمنح للمطورين المرونة باختيار الإصدار المناسب لتطبيقاتهم ومعرفة المميزات الموجودة في كل إصدار. لذا من المفيد أن تأخذ هذه الأسباب بعين الاعتبار عندما تقوم بإنشاء وتنفيذ أي تطبيق في المستقبل. الأداة MailHog إذا قمت سابقاً بتثبيت برنامج ووردبريس فمن المؤكد أنك رأيت كمية رسائل البريد الإلكتروني المرسلة لك وذلك إما عبر واجهة التطبيق أو عبر واجهة سطر الأوامر Command Line Interface فعندما تعمل في بيئة التطوير الخاصة بك سيكون هناك اختلاف بسيط في التعامل وإدارة رسائل البريد الإلكتروني (سواء في طريقة استلامها أو تخصيصها) وهنا يمكنك استخدام تقنية MailHog وهي عبارة عن تطبيق ويب مفيد جداً عندما تصل إلى مراحل متقدمة في تطوير واختبار مشروعك حيث تقوم هذه التقنية بدور خادم SMTP وهمي وتقوم بشكل أساسي بدور خادم بريد إلكتروني وهمي لمساعدتك في اختبار وظائف البريد الإلكتروني وتصحيح الأخطاء. تقوم الأداة MailHog بشكل أساسي باعتراض وفحص رسائل البريد الإلكتروني الصادرة من تطبيقك بدلاً من إرسالها بشكل مباشر إلى المستلمين الحقيقيين ثم تقدم هذه الرسائل عبر واجهة التطبيق، وهذا يسهل قراءة وتحليل ومراجعة الرسائل الصادرة من ووردبريس. إذ تسمح لك هذه الأداة بإلقاء نظرة كاملة على رسائل البريد الإلكتروني وهذا يتضمن: الموضوع (عنوان الرسالة) المرسل المستقبل (مستلم الرسالة) الرسالة (نص الرسالة) كما تسهل هذه الأداة وظائف البريد الإلكتروني من خلال التأكد من تنسيق رسائل البريد الإلكتروني وتسليمها بشكل صحيح. وعلى الرغم من أن هذه الأداة تعد مفيدة نوعاً ما عند العمل ضمن برنامج ووردبريس إلا أنه يجب الأخذ بعين الاعتبار معرفة كيفية استجابة التطبيق عندما نستخدم بيئة ووردبريس بدون رأس Headless WordPress فكل الأوامر تشغل من خلال نقاط الوصول الخاصة بواجهة برمجة التطبيق API الأداة Insomnia الأداة Insomnia هي تطبيق عميل API مفتوح المصدر متوافق مع مختلف الأنظمة، لكن إذا كنت معتادًا على التعامل مع تطبيقات REST API مثل تطبيق Postman أو تطبيق Paw (المسمى حالياً RapidAPI) فإنك لن تكون مهتماً بالعمل على هذه التقنية بالرغم من أنها أبسط نوعاً ما من التطبيقات المذكورة أعلاه بالنسبة للمطورين الذين لا يعملون على البرامج المذكورة أعلاه فإن هدفهم الأول هو تبسيط عملية التفاعل مع واجهة برمجة التطبيق REST API واختبارها. تنشئ الأداة Insomnia طلبات بروتوكول HTTP وتحللها وترسلها إلى نقاط وصول واجهة برمجة التطبيق API باستخدام الأوامر GET و POST و PUT و DELETE القياسية وتجعل التعامل معها أكثر سهولة. كما أنها تسهل التعامل مع ملفات JSON, XML وبيانات النماذج أو الاستمارات forms. وإن كان هذا الأمر غير مهم عند إنشاء التطبيقات لأنه يمكنك التحكم بنوع ملفات الخرج التي سيتم استرجاعها ولكنه أساسي عندما تتعامل API آخر لأن تحتاج لتغيير تنسيق البيانات قبل إرسالها أو استقبالها. ومن أهم ميزات هذه الأداة هي قدرتها على تنظيم الطلبات requests في مجلدات ومساحات عمل workspaces وهذا يسمح للمطورين بتنظيم وإدارة مشاريع اختبار واجهة برمجة التطبيق API الخاصة بهم ويسهل عملية التنقل والتبديل بين نقاط الوصول المختلفة. بالإضافة إلى ذلك فإن الطريقة التي يسمح لنا التطبيق بها في هذه الأداة بتوثيق وتنظيم كل نقطة وصول تجعل عملية مشاركة التطبيق مع المطورين الآخرين أسهل وبالتالي يمكنهم اختبار التطبيق على تطبيق واجهة برمجية API آخر على سبيل المثال بمجرد إنشاء REST API لموقع مبني بواسطة ووردبريس يمكنك مشاركته بواسطة الأداة Insomnia مع مطوري تطبيقات iOS ليتمكنوا من معرفة ما يلي: بنية نقطة الوصول الوسطاء المتوقعة الخرج الذي سيعيده التطبيق توثيق كل نقطة وصول في حال حدوث خطأ غير متوقع أخيراً فإن هذا النوع من الأدوات يسهل بناء وتطوير التطبيقات عند العمل مع مطورين يعملون على تطبيقات أخرى الأداة JWT-Auth قبل البدء في معرفة أهمية الأداة JWT-Auth فإنه من المفيد جداً معرفة ما هي JWT ومدى فائدتها في تطوير واجهة برمجة التطبيقات REST API بشكل عام يمكن تعريف JWT على أنه طريقة آمنة لنقل المعلومات بين المطورين بصيغة JSON وبعبارة أبسط هي عبارة عن آلية مصادقة معتمدة على المفتاح المساعد token وتستخدم بشكل كبير في تطبيقات الويب ملاحظة: على الرغم من صحة ما سبق فإنك إن لم تكن على دراية بآلية عمل مصادقة REST API لن تستطيع بناء المصادقة التي تحتاج إليها. عند إجراء مقارنة بين طريقة تفاعلنا مع الووردبريس من خلال المتصفح، وطريقة تفاعلنا مع الوورد بريس من خلال JWT فإنه يجب الانتباه إلى النقاط التالية: أولا نقوم بإرسال بيانات الاستيثاق (اسم المستخدم وكلمة السر) إلى المتصفح ومن ثم يطابق التطبيق تلك البيانات ويرسلها إلى المتصفح وبعد ذلك نكون قادرين على العمل والتنقل ضمن الوورد بريس (إدارة المستخدمين - كتابة وتحرير المنشورات وما إلى ذلك وأحياناً يكون عبارة عن مراجعة للمنشورات فقط) وهذه الأمور تتم فقط من خلال مدير نظام ووردبريس. أما عندما تتم المصادقة عبر واجهة برمجة التطبيقات REST APIs فيجب أن يكون هناك طريقة أخرى للمصادقة يستطيع التطبيق من خلالها معرفة بيانات استيثاق المستخدم وهنا يأتي دور المفتاح المساعد token حيث ستقوم آلية المصادقة المعتمدة على JWT بإنشاء مفتاح مساعد token ثم تقوم بتضمين هذا المفتاح المساعد token مع كل طلب وفي انتهاء صلاحية المفتاح المساعد token ستقوم واجهة برمجة التطبيق API بإعلامك من أجل أن تقوم بتحديث المفتاح المساعد token من الواضح بأنك بحاجة إلى عمل كثير عند تطبيق JWT في ووردبريس ولكن عند استخدام إضافة JWT-Auth فإن ستصبح الأمور أسهل بكثير. وكما توضح الصورة أعلاه المأخوذة من توثيق الإضافة فإن استيثاق ووردبريس باستخدام JSON Web Token يسمح بإجراء مصادقة واجهة REST API باستخدام المفتاح المساعد token وهي عملية بسيطة وغير معقدة وسهلة الاستخدام وهذه الإضافة مناسبة جداًَ لعمل مصادقة JWT ضمن ووردبريس. وللتعامل معها عليك القيام بما يلي: ضع مفتاح سري في الملف wp-config.php حدد فيما إذا كنت تريد من الإضافة أن تدعم خاصية CROS (وهو موضوع مهم إذا كنت تريد تمكين الاتصال الآمن بين تطبيقات الويب) بعد الانتهاء من تثبيت الإضافة يمكنك ان تقوم بعمل طلب request إلى wp-json/jwt-auth/v1/token/ متضمنا اسم المستخدم وكلمة السر للحساب بتنسيق JSON كما في المثال التالي: { "username": "John@appleseed.com", "password": "kn6oLrcW0\/D1M" } وبعدها سوف تستقبل الرد التالي: { "success": true, "statusCode": 200, "code": "jwt_auth_valid_credential", "message": "Credential is valid", "data": { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3NjYXJpZi50ZXN0IiwiaWF0IjoxNjg0MzU1MzA5LCJuYmYiOjE2ODQzNTUzMDksImV4cCI6MTY4NDk2MDEwOSwiZGF0YSI6eyJ1c2VyIjp7ImlkIjoyNTksImRldmljZSI6IiIsInBhc3MiOiJiMWM3OTBhMmJhYzExNzNlYzI4ZTQxZGUxMDYyYzIwZiJ9fX0.q3-DRC_DQvezy1w-XV0CYNQYyKC3X8iSs7GKy86zjbg", "id": 259, "email": "John@appleseed.com", "nicename": "johnappleseed-com", "firstName": "John", "lastName": "Appleseed", "displayName": "John@appleseed.com" } وبعد ذلك ستقوم بتضمين المفتاح المساعد token مع كل طلب جديد، وإما أن يسمح لك الووردبريس مع إضافة JWT Auth بإرسال هذا الطلب request أو أن يمنعك من ذلك. هذا هو باختصار جوهر عمل الأداة JWT ضمن التطبيقات دون الرأس headless ولكن هذا ليس كل شيء إذ توفر هذه الأداة الكثير من المميزات الأخرى التي لن نتطرق لها حاليًا الخاتمة تعرفنا في مقال اليوم على أهم الأدوات التي سنحتاجها لبناء تطبيقات ووردبريس بدون رأس Headless WordPress باستخدام واجهة برمجة التطبيق REST API والتي يمكنك من خلالها الاستفادة من مرونة الووردبريس كنظام إدارة محتوى. نتمنى لكم الاستفادة الجيدة من هذا المقال وفي حال كان لديكم أي تساؤل حول ما ورد فيه فيمكن تركه في قسم التعليقات أسفل المقال أو كتابته في قسم الأسئلة والأجوبة في أكاديمية حسوب. ترجمة وبتصرف للمقال How To Build Headless WordPress Applications with a REST API لكاتبه Tom McFarlin اقرأ أيضًا مُقدّمة إلى برمجة إضافات Wordpress مدخل إلى برمجة قوالب ووردبريس أفضل طريقة للتّعديل على ووردبريس وإضافة نص برمجي إليه استقبال وحفظ خيارات الإضافة في ووردبريس من خلال التعامل مع Setting API وOptions API