سارة محمد2

الأعضاء
  • المساهمات

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

  • تاريخ آخر زيارة

  • Days Won

    2

السُّمعة بالموقع

11 جيدة
  1. يتضمن العمل مع أشكال SVG (الرسوميات المتجهية متغيرة الحجم) في سير عمل تصميم الويب المتجاوب مرحلة تصميم ومرحلة تطوير. يتعامل المصممون عادةً مع مرحلة التصميم وقد يعرفون أو لايعرفون كيفية كتابة الشيفرة. ونظرًا لطبيعة SVG كتنسيق صورة وتنسيق مستند معًا، فإنّ كل خطوة تتم في محرر الرسومات أثناء عملية إنشاء SVG تؤثر بشكلٍ مباشر على الشيفرة الناتجة وبالتالي على عمل المطور المسؤول عن تضمين SVG أو كتابته كنص برمجي أو تحريكه. عادةً، خلال عملي اليومي أنا المطور الذي يسلّم المصممون له أصول التصميم، وصور SVG هي جزء من هذه الأصول. احتاجت معظم الأصول التي تم تسليمها في مشاريعي السابقة إلى المزيد من العمل و/أو جولة ثانية من التحرير في محرر الرسومات قبل أن أتمكن من كتابتها كنصوص برمجية، لأنّ شيفرة SVG الناتجة لم تُحسَّن بما فيه الكفاية لنوع العمل - خاصةً الرسوم المتحركة - التي تم استئجاري للقيام بها. سبب ذلك هو أنّ العديد من المصممين الذين عملت معهم يعرفون القليل جدًا - إن كانوا يعرفون - عن شيفرة SVG. ينشؤون رسوميات متجهة وأصول واجهة المستخدم كل الوقت، لكن بالنسبة لهم فإنّ SVG ليس سوى تنسيق صورة ولا يعرفون الكثير عن الشيفرة المتولّدة عند تصدير أصولهم كمستندات SVG. هناك بعض الخطوات التي يمكن للمصممين اتخاذها أو تجنبها - مجموعة من "افعل ولا تفعل" - يمكن أن تساعد في جعل الشيفرة المتولدة أنظف. أودّ أن أشارك بعضها في هذه المقالة، إذا كنت تعرف أي شيء آخر يرجى مشاركته في التعليقات الموجودة في نهاية المقال. النصائح التي سنتحدث عنها قابلة للتطبيق في Adobe Illustrator -محرر الرسومات المفضل لي- وكذلك محررات الرسومات الأخرى. لكن بما أنني شخصيًا أستخدم AI فهذا ما سأركّز عليه خلال هذه المقالة. سنتحدث أيضًا عن خيارات تصدير SVG الحالية المتوفرة في AI وأيّ منها تختار ولماذا. لكن لاحظ أنّ هذه الخيارات ستتغير في المستقبل، وبعدها سيتم تحديث هذه المقالة لتعكس هذه التغييرات. تعتمد هذه المقالة على حديثي "SVG لمصممي ومطوري الويب" - حديث قمت به في CSSDevConf 2015 الشهر الماضي. لذا، لنبدأ. إذا كنت تستخدم برنامج Sketch لإنشاء SVG، هناك أيضًا بعض الأشياء التي يمكن أن تقوم بها لتصدير شيفرة أنظف. شارك Sean Kesterson بعض النصائح في هذه المقالة. 1- أنشئ الأشكال البسيطة باستخدام عناصر شكل بسيطة وليس عناصر <path> هناك سبب لوجود أشكال أساسية مختلفة في SVG لإنشاء أشكال أساسية بشكلٍ جيد. يمكن للشخص إنشاء أي شكل عمليًّا باستخدام عنصر <path>، أليس كذلك؟ عناصر الشكل البسيطة (<line>، <circle>، <rect>، <ellipse>، <polygon> و<polyline>) موجودة لعدة أسباب، وأحد هذه الأسباب هو أنّها سهلة القراءة وسهلة الصيانة وقابلية للتحرير باليد من بدائلها <path>. تأتي الأشكال الأساسية مع مجموعة من الخاصيات التي تسمح لك بالتحكم في ميزات الشكل، مثل الموضع (x، y، cx، cy) والأبعاد (width وheight)، بينما لا يكون لعناصر <path> مثل هذه الخاصيات. مثلًا، يعرض المقتطف التالي الاختلاف بين دائرة تم إنشاؤها وتصديرها كشكلٍ بسيط، مقابل دائرة تم إنشاؤها وتصديرها كمسار: <circle fill="#FFFFFF" stroke="#000" cx="28.1" cy="28.1" r="27.6"/> <!-- مقابل --> <path fill="#FFFFFF" stroke="#000" d="M55.7,28.1 c0,15.2-12.4,27.6-27.6,27.6 S0.5,43.3,0.5,28.1 S12.9,0.5,28.1,0.5 S55.7,12.9,55.7,28.1z"/> إذا أردت تحريك شكلك عن طريق تحريك موضع الدائرة أو تكبيرها، يمكنك القيام بذلك عن طريق تحريك موضع المركز عبر الإحداثيات x و y أي (cx وcy) ونصف قطر الدائرة (r). بينما إذا كنت تعمل مع دائرة متولدة كمسار، يجب عليك استخدام تحويلات CSS/SVG (التحريك وإعادة التحجيم) للقيام بذلك. ثمّ بفرض أردت تحريك ذلك المسار وتتطلب منك الحركة تطبيق المزيد من التحويلات، يمكن أن يصبح لديك ببساطة فوضى تحويلات. ميزة أخرى لاستخدام الأشكال البسيطة هي أنّه في معظم الحالات، الشيفرة المطلوبة لإنشاء شكل باستخدام عناصر شكل بسيطة هي أقلّ من المطلوبة لإنشاء نفس الشكل باستخدام عنصر <path> (شاهد المقتطف اعلاه للمقارنة)، لذا فإنّ استخدام أشكال بسيطة سيؤدي أيضًا إلى حجم ملف أصغر، وهذا أفضل دائمًا. 2 - حوّل النص إلى حدود خارجية..أو لا تحوّله لتحويل النص إلى حدود خارجية: اختر النص الذي تريد تحويله اختر النوع (Type) -> إنشاء حدود خارجية (Create Outlines) الإيجابيات النص المحوّل إلى حدود خارجية سيحافظ على نوع الخط المستخدم، بدون الحاجة إلى استخدام خط ويب لعرضه. هذا يعني أنك تحفظ القليل من طلبات HTTP الإضافية ولا تخاطر بعرض نصك بالخط الاحتياطي الذي لا يبدو جيدًا بشكلٍ كافٍ لاستبدال الخط الجميل الذي اخترته. يعدّ تحويل النص إلى حدود خارجية والحفاظ على نوع الخط المستخدم جيّدًا للحفاظ على هوية العلامة التجارية عندما تُعرّف بنوع خط مستخدم، مثلًا في شعار، أقوم دائمًا بتحويل نص الشعار إلى حدود خارجية وهذا جيد للحفاظ على الخط لنصوص برمجية معينة عندما تستخدم العناوين. السلبيات النص المحوّل إلى خطوط خارجية ليس نصًا حقيقيًا: إنّه مجموعة من المسارات التي تشكل الحدود الخارجية (الشكل) للنص. وبالتالي يصبح النص غير واقعي ولا يمكن الوصول إليه، وغير قابل للبحث وغير قابل للتحديد. إذا تمّ تحويل النص إلى حدود خارجية في عنوان نص برمجي أو حتى شعار، فإنّ استخدام نص alt (إذا تم تضمين الشعار كصورة) أو عناصر سهلة الوصول لـ SVG مثل (<title> و&>) هو فكرة جيدة لتوفير نص بديل لقارئات الشاشة. أوصي بشدّة بقراءة كل شيء عن جعل SVG سهل الوصول في هاتين المقالتين لـLéonie Watson: - نصائح لإنشاء SVG سهلة الوصول - استخدام ARIA لتحسين سهولة الوصول لـSVG يمكن أن يسبب تحويل النص إلى حدود خارجية زيادةً كبيرةً في حجم ملف SVG، حسب تعقيد الخط المستخدم. تُظهر الصورة أدناه الاختلاف في حجم (وسهولة القراءة) لـSVG مع النص المحوّل إلى حدود خارجية (يسار) والنص الذي يتم تصديره كـ SVG <text>‎ (يمين). لا يمكن التحكم بالمسارات أو تحريكها بسهولة مثلما يتم ذلك في عناصر <text> (المتضمنة عناصر <tspan>). إذ تحتوي الأخيرة على مجموعة من الخاصيات التي تمنحك المزيد من التحكم في رسومك المتحركة، بينما تبقى بيانات المسار محدودة بهذا الشأن. 3- تبسيط المسارات يعرَّف المسار بمجموعة من النقاط والتي بدورها يتم تحديد كلّ منها بزوج من الإحداثيات. كلّما قلّ عدد النقاط، قلّت بيانات المسار (خاصية d)، وبالتالي انخفض حجم ملف SVG الكلي. هذه الخطوة الجيدة يجب اتخاذها دائمًا لأنّ حجم الملف الأصغر أفضل للأداء. لتبسيط مسار: اختر المسار اذهب إلى كائن (Object) -> مسار (Path) -> تبسيط (Simplify) عدّل عدد النقاط. تأكّد من أن أنّك فحصت المعاينة حتى تتمكن من معرفة كيف يتغير المسار أثناء تغيّر عدد النقاط. عدّل العدد حتى تحصل على أقل عدد من النقاط أثناء المحافظة (أو التضحية) على أفضل مظهر مرئي للمسار الذي تحتاجه. يوجد فيديو تعليمي لشركة Adobe يشرح العملية، لذا إذا كنت أكثر انسجامًا مع مقاطع الفيديو، يمكنك الاطلاع عليه هنا. يمكنك أيضًا تبسيط المسارات باستخدام أداة Warp Tool. أنا لست مصممة ولكن أستخدم عادةً خوارزمية تبسيط Ai لتبسيط مساراتي، لذا إذا كنت مصممًا متمرّسًا، من المحتمل أنّك تعرف أكثر بكثير عن أداة Warp tool مني. يوجد مقال في مجلة Smashing حول هذه الأداة، يمكنك الاطلاع عليها إن أردت. 4- تجنب دمج المسارات إذا لم تكن بحاجة للتحكم في المسارات الفردية يميل العديد من المصممين إلى جمع المسارات أو دمجها كلما أمكن ذلك. لدمج المسارات: اختر الطرق التي تريد دمجها اذهب إلى نافذة (Pathfinder <- (Window اختر خيار الدمج (Merge) من بين قائمة الخيارات أسفل اللوحة (الأيقونة الثالثة من اليسار، المبينة في لقطة الشاشة أدناه). قد يكون هناك فوائدٌ لجمع المسارات، لكن تجنبها عندما تحتاج أنت أو المطور للتحكم و/أو تحريك المسارات بشكلٍ منفصل. تم تصميم بعض الرسوم المتحركة بحيث يتم تحريك عناصر متعددة بشكلٍ منفصلٍ، أو في بعض الأحيان قد تريد فقط تصميم المسارات باستخدام ألوان تعبئة مختلفة. إذا قمت بجمع المسارات، فلن يكون ذلك ممكنًا بعد الآن. تحتاج إلى التأكّد من معرفتك لما يحتاجه المطور (أو أنت إذا كنت ستتعامل مع مرحلة التطوير أيضًا) ولما يريد القيام به مع الأشكال التي تعمل عليها، واتخاذ قرار الدمج أو عدم الدمج وفقًا لذلك. هذا سيوفر لكما الكثير من الوقت والخلاف. 5- إنشاء مرشّحات باستخدم مرشّحات SVG، وليس تأثيرات Photoshop إذا كنت تستخدم المرشّحات في قسم تأثيرات Photoshop ضمن خيار تأثير، فسيقوم Illustrator بتصدير التأثيرات التي تنشؤها كصور نقطية. مثلًا، إذا قمت بإنشاء ظل إسقاط باستخدام تأثير الضبابية Blur في فوتوشوب، فإن الظلّ المسقط المتولّد سيكون صورة نقطية مضمنة داخل SVG إما مضمنة داخليًا أو خارجيًا، باستخدام عنصر <image>. أنت بالتأكيد لا تريد ذلك عند العمل مع SVG. تحتاج لتوليد مؤثراتك كشيفرة SVG إلى استخدام مرشّحات SVG المتاحة: اذهب إلى تأثير (Effect) -> مرشّحات (SVG Filters (SVG اختر واستخدم أحد المرشّحات المتاحة في اللائحة. 6- اجعل لوح الرسم (Artboard) مناسبًا لرسمتك هل قمت يومًا بتضمين SVG في صفحة، وإعطائه ارتفاعًا وعرضًا محددين ثمّ وجدت أنه كان معروضًا بحجم أصغر من الحجم الذي حددته؟ في معظم الحالات، يكون السبب في ذلك هو وجود مساحة بيضاء داخل منفذ العرض (viewport) الخاص بـ SVG. يتم عرض منفذ العرض بالحجم الذي تحدده في صفحة الأنماط (style sheet) الخاصة بك، لكن المساحة الإضافية داخلها - حول الرسم - تتسبب في "تقليص" صورتك، لأنّ هذه المساحة البيضاء تأخذ مساحةً داخل منفذ العرض. لتجنّب ذلك، تحتاج إلى التأكّد من أنَّ لوح الرسم الخاص بك كبير بما فيه الكفاية ليناسب الرسمة بداخله، ولكن ليس أكبر منها. أبعاد لوح الرسم هي أبعاد منفذ عرض SVG المصدَّر، وأي مساحة بيضاء في لوح الرسم سيتم إنشاؤها كمساحة بيضاء داخل منفذ العرض. ليتناسب لوح الرسم مع رسمتك: حدد كامل الرسمة (أنا أستخدم الاختصار cmd/ctrl + A) اذهب إلى كائن (Object) -> ألواح الرسم (Artboards) واختر الخيار تناسب مع حدود العمل الفني (Fit to Artwork Bounds option). 7- استخدم اصطلاحات تسمية، وتجميع وطبقات جيّدة أعلم أنّ هذا يبدو كأنّه غير عقلاني، لكن يجب التأكيد عليه لعدة أسباب: ستتم ترجمة معرّفات وأسماء الفئات التي تستخدمها في محرر الرسومات إلى معرّفات وأسماء الفئات في الشيفرة المتولّدة. كلّما كانت هذه الأسماء أكثر منطقيةً وأكثر وضوحًا في تصنيف العناصر الخاصة بكل منها، سيكون هناك تعارضًا أقل عندما يعمل المطوّر مع الشيفرة. أنا لا أقول الآن أنّه يجب عليك التفكير في الأسماء المثالية - أنا متأكّدة أنّه لكلّ منّا طرق مختلفة لتسمية الأشياء ويمكن أن تكون التسمية واحدة من أصعب المهام، لكن تسمية المجموعات بشكلٍ مناسب تقطع شوطًا طويلًا. مثلًا، إذا كنت ترسم سيارة، فإنّه من المناسب استخدام معرّف العجلة لتسمية الطبقة أو مجموعة تغليف الأشكال المكوّنة للعجلة. إذا كنت تقوم بتجميع كل العجلات في مجموعة واحدة، فقد تعطيها معرّف العجلات. تؤثّر الأسماء البسيطة في التعبير عن العناصر والمجموعات وتحفظ الكثير من الوقت، خاصةً إذا كان المطوّر سيحرر الشيفرة ويعالجها يدويًا. لا يقوم Illustrator بعمل أفضل في تسمية الأشياء، لذا فإنّ تحديد الأسماء يساعد في تقليل كمية المخلّفات التي ينتجها. وبالتأكيد، سيكون هناك بعض التحرير الإضافي المطلوب للتخلّص من الخطوط السفلية المزعجة التي يصرّ Ai على توليدها، لكن استخدام الأسماء الصحيحة يساعد في جعل هذه العملية أسهل قليلًا. كما ذُكر سابقًا، ستُظهر النسخة التالية من Illustrator تحسّنًا كبيرًا في طريقة توليد SVG، متضمنةً المعرّفات المتولّدة. استخدم الطبقات لتجميع العناصر ذات الصلة. تُترجم الطبقات إلى مجموعات في الشيفرة، لذا يجب تسميتها بشكلٍ جيّد أيضًا. أنشئ طبقات/مجموعات لتجميع العناصر ذات الصلة معًا، خاصةً تلك التي قد تكون متحركة بالكامل. إذا لم يتم ذلك بالفعل في مرحلة التصميم فمن الممكن قضاء الكثير من الوقت في إعادة ترتيب وتجميع العناصر يدويًا من قِبل المطوّر. تأكّد من التجميع بشكلٍ مناسب لتوفير الوقت. يُعدّ التحدّث مع المطور في مرحلة التصميم وتصميم كيفية ستُنفّذ الرسوم المتحركة معًا هما أكبر موفران للوقت. إذا كانت الصور التي تقوم بإنشائها ستُستخدم لإنشاء SVG sprite، فإنّ الأسماء التي تستخدمها يمكن أن تُستخدم وستُستخدم من قِبل معظم أدوات الأتمتة لتوليد ملفات جديدة. لذا فإنّ استخدام أسماء واضحة ومناسبة سيؤدي إلى أسماء ملفات أنظف. 8- اختر أفضل خيارات تصدير مناسبة للويب بدءًا من Illustrator CC 2015.2 الذي تمّ إصداره في تشرين الأول 2015، يوجد تدفق عمل جديد لتصدير SVG (ملف> تصدير> SVG) لتصدير ملفات SVG مُحسّنة للويب لتدفقات عمل تصميم الشاشة والويب لديك. يمكنك أيضًا أن تختار تصدير الأغراض الفردية أو كامل لوح الرسم. راجع هذه المقالة لمزيدٍ من التفاصيل. في وقت كتابة هذا المقال، يأتي Illustrator مع مجموعة من خيارات التصدير التي تسمح لك بتوليد شيفرة SVG أفضل بشكلٍ عام. لتصدير الـ SVG الخاص بك: اختر ملف (File) -> حفظ كـ (Save As) اختر SVG من القائمة المنسدلة اضغط حفظ (Save) بمجرد النقر على حفظ، سيظهر مربع حوار يحتوي على مجموعة من الخيارات التي يمكنك تخصيصها، والتي ستؤثّر على شيفرة SVG المتولّدة: الخيارات الموضّحة في الصورة أعلاه هي الخيارات المستحسنة لتوليد SVG للويب. يمكنك بالطبع اختيار تحويل النص إلى حدود خارجية إذا لم تكن ترغب باستخدام خط ويب، وكما ترى يوفر لك Illustrator خيارًا أيضًا للقيام بذلك عند التصدير. يحدد خيار موقع الصورة (Image Location) فيما إذا سيتم تضمين أي صور نقطية داخليًا في الـ SVG الخاص بك أو خارجيًا مع رابط يدلّ عليه في الـ SVG. ومجددًا، هذا يعتمد على ما تحتاجه. يمكن أن تزيد الصور المضمنة داخليًا في الـ SVG حجم الملف بشكلٍ كبيرٍ. آخر مرة أرسل مصممٌ لي SVG مع صورة مضمنة فيه، كان حجم الملف أكثر من 1 ميغابايت! بعد حذف تلك الصورة (وبسبب تأثيرات Photoshop المستخدمة المذكورة سابقًا)، انخفض حجم الملف إلى أكثر من 100 كيلو بايت! لذا، اختر بحكمة. يمنحك خيار خصائص CSS الحرية لاختيار كيف تريد إنشاء الأنماط داخل الـ SVG: استخدام خاصيات التقديم، الأنماط المضمنة داخليًا، أو داخل وسم <style>. هذا أيضًا مسألة تفضيل وتعتمد على الطريقة التي تنوي بها التعامل مع SVG بمجرد تضمينها. إذا لم تكن الشخص الذي سيفعل ذلك، فتأكد من التشاور مع المطور لتحديد الخيار الذي يناسب احتياجاته على أفضل وجه. كلّما قلّ عدد المنازل العشرية، قلّ حجم ملف SVG. يجب أن تكون منزلة عشرية واحدة كافية بشكلٍ عام، لذا سأتّبع ذلك. لاحظ أنّه إذا اخترت 3 أو 4 منازل عشرية، مثلًا، ثمّ استخدمت أداة تحسين لتحسين SVG وخفض هذا الرقم إلى 1، فقد ينتهي بك الأمر إلى SVG معطوب، لذا من الأفضل اختيار هذا الخيار في وقت مبكر. هناك المزيد في لائحة الخيارات التي قمت بتغطيتها. كتب Michaël Chaize من Adobe مقالةً ممتازةً حول لائحة التصدير توضّح ما يفعله كل خيار تمامًا. أوصي بشدّة بمراجعة مقالته: تصدير SVG للويب مع Illustrator CC الآن، في وقت كتابة هذا المقال، سيبقى Illustrator يولّد شيفرةً غير ضرورية مثل البيانات الوصفية للمحرر، والمجموعات الفارغة، من أمورٍ أخرى، لذا ستحتاج إلى تحسين الـ SVG بشكلٍ أكبر بعد تصديره، سواء كان ذلك باليد، أو باستخدام أداة تحسين SVG مستقلة. لكن قبل أن ننتقل إلى جزء التحسين، أريد أن أشير إلى أنّك قد ترغب أو لا ترغب في التحقق من خيارٍ إضافي أثناء حفظ SVG: خيار "استخدم ألواح الرسم" ، في لائحة الحفظ: يعدّ هذا الخيار مفيدًا عندما تعمل مع صور SVG متعددة (مثل الأيقونات) وتستخدم لوح رسم لكلّ أيقونة. سيؤدي تصدير ألواح رسم متعددة إلى توليد ملفات svg. متعددة، ملف لكل لوح رسم (واحد لكل أيقونة). سيتم تعطيل هذا الخيار بشكلٍ افتراضي إذا كنت تعمل مع لوح رسم واحد. يعتمد اختيار تصدير ملف SVG واحد أو عدة ملفات SVG على كيفية تضمين SVG. مثلًا، إذا كنت تريد إنشاء SVG sprite لنظام أيقونات SVG، فهناك عدة طرق يمكنك من خلالها إنشاء واستخدام الـ sprite، وكلّ منها تتطلب منهجًا مختلفًا: تتطلّب إحدى التقنيات فصل الأيقونات في البداية، بينما تتطلّب الأخرى أن تكون الأيقونات جزءًا من صورة واحدة. سأكتب منشورًا مفصّلًا حول SVG sprite وخيارات لوح الرسم، لكن حتى ذلك الحين، يمكنك أن تأخذ نظرة عامة حول التقنيات المختلفة لـ SVG sprite في المقالة التالية التي كتبتها لـ24Ways.org: نظرة عامة حول تقنيات إنشاء SVG sprite للتحسين أو لعدم التحسين يوصى عادةً بتحسين SVG بعد تصديره من محرر رسومات باستخدام أداة تحسين مستقلة. أداة التحسين الحالية الأكثر شيوعًا هي الأداة المعتمدة على NodeJS والتي تسمى SVGO. لكن قد لا يكون من الأفضل دائمًا تحسين SVG، خاصة إذا كنت تنوي تحريكه. إذا كنت تنوي كتابة SVG كنص برمجي و/أو تحريكه، فمن المحتمل أن تقوم بإعداد بنية وثيقة معينة - غلاف المجموعات، وأسماء المعرفات التي لا تستخدمها/تشير إليها داخل الـ SVG لكنك تنوي استخدامها في جافاسكربت أو غيرها. ستتغير البنية إذا قمت بتحسين الـ SVG باستخدام SVGO (أو أي أداة تحسين أخرى). عادةً ما تزيل أدوات التحسين أي مجموعات ومعرّفات غير مستخدمة، بالإضافة إلى أنّها تطبّق العديد من التغييرات على الـ SVG للتأكد من تحسينه بشكلٍ جيد. لقد قمت بتحسين ملف SVG مرةً بعد تطبيق رسم متحرك عليه باستخدام <animate>. كان ملف الـ SVG معطوبًا والحركة بداخله، لأنّه تم تغيير البنية بالكامل. وهذا شيء يجب تذكره قبل تحسين SVG. تجنّب التحسين مستخدمًا أداة تحسين، إذا قمت بتحرير و/أو توليد SVG يدويًا ببنية معينة تحتاجها، وحسّن يدويًا قدر الإمكان. يمكن إزالة بعض مخلفات التحرير في بداية ونهاية الـ SVG يدويًا بسهولة، والمخلفات الأخرى مثل البيانات الوصفية والفئات المتولّدة من قِبل برامج التحرير التي لا تحتوي على خيارات تحسين SVG - مثل Sketch - قد يكون من الصعب تحسينها يدويًا. عمومًا، أنا لا أستخدم Sketch لتوليد SVG معقدة. أستخدم Illustrator أو Inkscape؛ إذ يأتي الأخير مع لائحة تصدير افتراضية تعطيك الكثير من الخيارات لتحسين الـ SVG قبل تصديره (انظر الصورة أدناه). يولّد Inkscape أنظف شيفرة SVG في وقت كتابة هذا المقال. أي إذا اخترت خيار تحسين SVG، لكن ضبابية واجهة المستخدم على شاشة شبكة العين وكذلك اعتمادها على X11 على OS X يجعل من الصعب استخدامه، لذلك أنا متمسكة حاليًا بـ Illustrator. إذا كنت تحتاج/تريد تحسين الـ SVG الخاص بك فإنني أنصحك بأداة SVGO، إذ يأتي SVGO مع مجموعة من الإضافات التي يمكن دمجها مع أي نوع من تدفقات العمل تقريبًا. 9- تواصل. تواصل مبكرًا ربما كان أهم نصيحة يمكنني تقديمها هو التواصل والقيام بذلك مبكرًا في عملية التصميم. أفترض أنّك الآن - المصمم الذي ينشئ SVG - لست الشخص نفسه المسؤول عن تطوير SVG (كتابة نص برمجي، التحريك، التضمين، …إلخ). تتطلّب كل واحدة من النصائح السابقة تقريبًا معرفة مرحلة التطوير وما ينوي المطور القيام به مع الـ SVG - كيف ينوون تضمينه، وكتابته كنص برمجي، وتصميمه، وتحريكه. لذا إذا لم تكن أنت نفس الشخص الذي يصنع القرارات لكلا المرحلتين، وما لم تكن تريد تضييع الكثير من الوقت في تكرار وتحرير الـ SVG، تحتاج إلى التأكّد من معرفتك لما يحتاج لمطور أن يفعله مع الـ SVG وما المنهجية (أو المنهجيات) التي سيتبعها. إذا كنت تعمل في مشروع له موعد نهائي ضيق، فمن المحتمل أنك لا تستطيع أن تضيع وقتًا كبيرًا في إجراء تغييرات ومراجعات على أصول الصور، عندما يمكنك تجنب ذلك عن طريق التواصل مبكرًا. يمكن أن يكون المصممون والمطورون أفضل أصدقاء لبعضهم بعضًا. تتطلّب طبيعة SVG أن تكون مراحل التصميم والتطوير مفتوحة على بعضها بعضًا، وهذا بدوره يتطلب من المصمم (المصممين) والمطور (المطورين) التحدّث قبل بدء عملية التصميم وطوال العملية أيضًا . ترجمة -وبتصرف- للمقال Tips for Creating and Exporting Better SVGs for the Web لصاحبته Sara Soueidan
  2. كنت أعمل البارحة على إنشاء الشرائح والعروض التجريبية المرافقة لحديثي القادم عن الشيفرة في موقع web directions الأسبوع المقبل. أحد العروض التجريبية التي أنشئها هو دليل أساسي لمفهوم المفتاح البسيط الذي يُستخدم لتبديل مظهر واجهة المستخدم من مضيء إلى مظلم وبالعكس، أحببته واستلهمته من مبدّل المظهر في تطبيق Medium المبين أدناه. مُخصِّص مظهر تطبيق Medium هو لوحة منبثقة بسيطة تتضمن مفتاح بسيط للتبديل من وضع مضيء إلى مظلم وبالعكس. الاختلاف الوحيد الذي أريده هو أن يشير مفتاحي إلى حالة المظهر الفعالة حاليًا بشكلٍ واضح. لذا بدلًا من تفعيل وتعطيل المظهر المظلم فقط مثل مفتاح Medium، أريد أن يبدّل المستخدم بين خيارَي مضيء ومظلم بوضوح. لا يوجد سبب محدد لذلك سوى أنّه تفضيل شخصي. هناك طرق أخرى للقيام بذلك أيضًا، يبقى أمر كيفية تصميمه تفضيلًا شخصيًا، طالما أنّه يعمل وقابلًا للفهم بسهولة من قِبل المستخدمين. بدأت أفكر كالعادة في كيفية تمييز هذا العنصر البسيط، مع التأكّد من سهولة الوصول إليه مباشرةً من البداية. لذا بدأت القيام بواجبي وقراءة وتعلّم كل ما بوسعي بشأن هذا الموضوع. كان من المهم بالنسبة إليّ التأكد أنّ هذا العرض التجريبي سهل الوصول حتى لو كان مجرد دليل سريع للمفهوم لحديث. أولًا وقبل كل شيء، بما أنّ شيفرة العرض التجريبي ستكون متاحة للعامة، كان لدي مسؤولية أكبر للتأكّد من أنّها سهلة الوصول، لأنني لا أريد نشر أي شيفرة لا يمكن الوصول إليها، خاصةً إذا كان هناك فرصة ليستخدمها الناس في مكان آخر. وأردت أن تكون الشيفرة جيدةً لسببٍ آخر هو أنّني ربما أرغب في إعادة استخدامها لمكونات أخرى في ورشة عمل قادمة لي حول تطوير الواجهة الأمامية. البدء بالشيفرة كما ذكرت أعلاه، بدأت أفكر في كيفية ترميز هذا العنصر لضمان سهولة وصوله إلى قارئات الشاشة. عندها أدركت (وأحسست بالغباء نوعًا ما) أنّ الوظيفة والترميز يعتمدان على الطريقة التي أريد أن يكون سلوك التبديل بها وكيف أريده أن يظهر. كان الأمر واضحًا جدًا بالنسبة لي: سيسمح المفتاح للمستخدم بالاختيار بين المظهر المضيء والمظهر المظلم، وسيكون المظهر المضيء هو الافتراضي. وفي هذه اللحظة تبادر إلى ذهني أزرار الانتقاء (radio buttons): خياران يمكن تفعيل أحدهما فقط في وقت واحد، هذا يمكن أن يصنع حالة استخدام ممتازة لأزرار الانتقاء القديمة الجيدة. كنت أعلم أنّه يجب عليّ اختيار شيء ما مختلف في حال أردت أن تبدو واجهة المستخدم وتتصرف بشكلٍ مختلف. مثلًا، إذا أردت لواجهة المستخدم أن تعرض "تفعيل/تعطيل النمط المظلم"، عندها لن أحتاج إلى استخدام أزرار الانتقاء لأنّ لدي خيارًا واحدًا فقط للتعامل معه وهو التشغيل أو إيقاف التشغيل، سيكون ذلك حالة استخدام رائعة لمربع تأشير (checkbox) أو عنصر زر <button> تبديل قديم جيد. تحليلات التصميم والوظيفة مترابطان، مما يساعد على التفكير في هذين الأمرين في وقت واحد عند التصميم لسهولة الوصول (وفي الحقيقة تصميم أي شيء بشكلٍ عام). ابدأ دائمًا وأبدًا بالتفكير حول الترميز وسهولة الوصول عند إنشاء المكونات بغض النظر عن مدى صغرها وبساطتها. دراسة كنت بحاجة -كما هو الحال دائمًا- إلى دعم نظريتي وتطبيقي العملي بالبحث الجيد، لذا بدأت بالقراءة، مراجعي الأولى التي ألجأ إليها هي المكونات الشاملة ومشروع A11y لـ Heydon Pickering. كما اتّضح، لدى Heydon مقالة رائعة حول أزرار التبديل تعلّمت منها الكثير، ولدى صديقي Scott O’Hara زر تبديل ARIA مضمّن في قسم الأنماط من مشروع A11y. لذلك، بطبيعة الحال، فحصتُ شيفرة الزر وقرأتُ مقالة Heydon للتأكّد فيما إذا كنت على الطريق الصحيح. تجدر الإشارة قبل المتابعة إلى أنّ هذه ليست مقالة حول كيفية إنشاء مفاتيح تبديل سهلة الوصول. مقالة Heydon تقوم بعمل رائع يغطي ذلك. النقاط الرئيسية التي استخلصتها شخصيّا من البحث أعلاه هي: هناك أنواع مختلفة من المفاتيح التي تبدو وكأنّها تقوم بأشياء مماثلة ولكنها تختلف اختلافًا جوهريًا عندما يتعلق الأمر بالترميز وسهولة الوصول. ليس بالضرورة أن تكون متشابهة، لمجرد أنّها مصممة لتظهر بنفس الشكل. أحتاج إلى التفكير بالسلوك الذي ستسلكه واجهة المستخدم، الشكل والصوت عند التبديل. أولًا التصميم وتجربة المستخدم، ثمّ الشيفرة. يمكن استخدام مفتاح التبديل للتبديل بين خيارين منفصلين، أو يمكن استخدامه لتشغيل خيار واحد أو إيقاف تشغيله (أو مثل تفعيل/تعطيل خيار). هنا تبدأ اختلافات التطبيق بالظهور. إذا كنت تبدّل بين خيارين منفصلين، فإنّ استخدام أزرار الانتقاء القياسية يبدو منطقيًا. يتم استخدام أزرار الانتقاء عندما تريد من المستخدمين اختيار خيار واحد فقط من خيارين أو أكثر، لذا فإنّ هذه حالة استخدام مثالية لهم. لديهم أيضّا إمكانية وصول أساسية وسيتم وضع إشارة إلى جانب الخيار الذي تم تفعيله. تأكّد فقط أنّك لا تحدّ من إمكانية الوصول لأيٍّ منهما في HTML أو باستخدام CSS. إذا كان الهدف من التبديل هو تفعيل/تعطيل ميزة (أو تشغيلها/إيقافها)، هناك طرق أخرى لذلك: استخدام صندوق تأشير لتأشير/إلغاء تأشير (تفعيل/تعطيل) هذا الخيار. استخدام زر <button> يكون له حالتان: مضغوط وغير مضغوط. تتطلب هذه الطريقة استخدام ARIA والذي بدوره سيتطلب جافاسكربت لتعمل التقنيات المساعدة بشكل صحيح (يتم تحديث قيم خاصية ARIA عند الضغط باستخدام جافاسكربت). اقرأ مقال Heydon للمزيد من التفاصيل عن هذه الطريقة. تعني هذه الطريقة أن لديك خيارًا واحدًا فقط، وهذا يعني بدوره أنّ زر التبديل له تسمية واحدة مرتبطة فقط، وربما لن يبدو زر "التبديل المزدوج" بعد الآن، إلا إذا: حالات زر التبديل المزدوج (التي تشير إلى تشغيل/إيقاف) سيكون لها نصّا واضحًا يصف الحالة الحالية الفعالة، لذا ستبدو مثل هذا المثال من العرض التجريبي لـScott في مشروع A11y: بالنسبة لاستخدامي لمحوّل المظهر، فهو خيارٌ واضحٌ: بما أنّ المستخدم لديه خيارات (مظلم ومضيء)، أردت استخدام أزرار الانتقاء. لم أكن أريد أن أقول "تشغيل/إيقاف تشغيل الوضع المظلم" إنّما أردت أن أقول "تفعيل الوضع المضيء أو تفعيل الوضع المظلم". سيكون الحل هو HTML وCSS فقط، ولا يتطلب جافاسكربت، وستتم إتاحة إمكانية الوصول بشكلٍ افتراضي. تحليل دَع لديك فكرة واضحة حول كيفية تصميم المكون وماذا يُفترض أن يفعل أو لا يفعل، واجعل الترميز والتصميم يعتمدان على هذه الفكرة. فحص الشيفرة الآن، وبعد قراءة وفحص المصادر السابقة، قررت أن أرى كيف يقوم الناس بالتبديل. بعد كل ذلك، كنت أعلم أنني سأجد الكثير من الأمثلة لمفاتيح التبديل التي تبدو وتتصرف مثل هذا المثال الحي على codepen، من الرائع دومًا أن أرى كيف يفعل الآخرون ذلك - ربما أفتقد بعض التقنيات الرائعة التي يمكن أن أتعلمها للتصميم، أو ربما أفتقد بعض المعلومات المهمة عندما يتعلق الأمر بالترميز وقابلية الوصول. نظرًا لأنني كنت أعمل في codepen على نسختي الخاصة من هذا المفتاح (شاركت العرض التجريبي أدناه)، أظنُّ أنني فحصت مفتاح التبديل الخاص بـcodepen: المفتاح الذي تستعمله لاختيار فيما إذا إذا كنت تريد لتصميمك أن يكون عامًا أو خاصًا. تحليل سريع من المفيد والممتع أن تتعلم من عمل الآخرين بفحص شيفرتهم (سواء ذلك عن طريق أدوات المطور [devtools] أو على codepen). مفتاح الخصوصية في Codepen يعرض مفتاح التبديل في codepen بعض السلوكيات الغريبة التي لاحظتها سابقًا ولكن لم أشعر أبدًا بالفضول لأقوم بال"تنقيح". يُظهر الفيديو التالي هذا السلوك: Your browser does not support the video tag. تجدر الإشارة أولًا إلى أنّني كنت محتارة بشأن هذا المفتاح. دائمًا ما يعطي اللون الأحمر مقابل الأخضر حلقة ردود فعل غريبة. إذا جعلت التصميم خاصًا، سيتحول اللون للأخضر، وجعله عامًا سيجعل اللون أحمر. بالنسبة لي فإنَّ الأخضر يعني أكثر من ذلك "التصميم قابل للوصول من قِبل الأشخاص، إنّه متاح" واللون الأحمر أكثر من ذلك "هذا التصميم مغلق، لا يُسمح للأشخاص بالوصول". ولكن بالنسبة لـcodepen فإن هذه الألوان ترمز إلى أشياء أخرى. وبشكل أكثر تحديدًا يرمز الأحمر إلى "المنطقة العامة = المنطقة الخطرة" أو "احذر هذا التصميم متاح للعامة وهذا غير جيّد". (آسفة لأنّي درامية جدًا، ولكن هذا هادفًا). ثمّ هناك سلوك الأخطاء المبيّن في الفيديو أعلاه (إذ أنّ النقر على نفس التسمية يغيّر قيمة المفتاح إلى قيمة التسمية الأخرى). كنت فضولية مجددًا لمعرفة سبب ذلك، شغّلتُ أدوات المطور وفحصت الشيفرة. وجدت أنَّ مفتاح codepen هو صندوق تأشير واحد يبدو أنّه يحتوي تسميتين. لذلك عند الضغط على "عام" و"خاص" معًا عدة مرات يؤدي إلى تشغيل وإيقاف تشغيل المفتاح عدة مرات. بكلماتٍ أخرى، هذا هو السبب في أنَّ رد الفعل/ السلوك المرئي للمفتاح لا يُطابق هدف المفتاح أو ما يبدو أنّه يعمل. كتبت تغريدةً حول هذا الخطأ، وسببه المحتمل واقترحتُ حلًا بديلًا يُصلح الأمر. بدأت في الحصول على ردود وآراء من مطورين آخرين، مما أدّى إلى نقاش جيد ومفيد للغاية استمتعت به تمامًا (أحد الجوانب الإيجابية القليلة في تويتر). بالطبع، كان الإجماع العام هو أنّ المفتاح يحتاج إلى إصلاح. لكن كيفية إصلاحه تعتمد بشكلٍ كبير على ما يريد فريق codepen القيام به: إذا كان من المفترض أن يكون مفتاح التبديل هو زر تفعيل/تعطيل للخيار "خاص" عندها: هذا يعني أنّ نمط التبديل تشغيل/إيقاف المذكور في القسم السابق أعلاه سيكون الحل المثالي. هذا يعني أنّه سيتم تجاهل التسمية "عام" والاحتفاظ بالتسمية "خاص" فقط، مع إشارة واضحة إلى أنّ المفتاح المجاور لها يعمل على إيقاف الخيار أو تشغيله، وهذا يقودني إلى نقطة أخرى: يجب إمّا أن يتغير التمثيل البصري للمفتاح بالكامل أو يتم تعديله: إذا كان يجب أن يبقى زر المفتاح كما هو بصريًا (أي الحفاظ على سلوك المفتاح المزدوج "نقل هذا التبديل يسارًا ويمينًا")، أقترح إضافة تسميات "تشغيل/إيقاف" له بشكلٍ شبيه لما فعله Scott في عرضه التجريبي مشروع A11y، لأنّه وفقًا لإرشادات الوصول لمحتوى الويب (WCAG)، يجب ألا تستخدم اللون فقط لنقل المعلومات. تحتاج الألوان أيضًا أن يكون لديها تباين كافٍ إذا كنت تستخدمها. ومرةً أخرى، كنت أعيد النظر في ألوان الأحمر والأخضر لأنّها قد تربك شخصًا ما مثلما أربكتني لفترةٍ من الوقت. (اقترح عليّ أحدهم الرمادي والأخضر، لأنّ "إضافة اللون يمكن أن تدلّ بشكلٍ كبيرٍ على الحالة الفعّالة (بدون لون مقابل لون يواجه لونين منفصلين)"). يمكن أن تكون البنية الأساسية والترميز لهذه الحالة (تسمية واحدة فقط): صندوق تأشير، يمكن أن يكون مفعّلًا أو غير مفعّل. أودّ اقتراح استخدام تصاميم صندوق تأشير بسيطة (ربما رائعة) لهذا السلوك والتخلّي عن أسلوب التبديل المزدوج بالكامل. زر <button> مع خاصية aria-pressed (و aria-checked إذا احتجت)، إذ تشير حالة الضغط إلى أنّ التصميم خاص، وحالة عدم الضغط إلى أنّ التصميم غير خاص، أي أنّه عام. سيتغير تصميم الزر في هذه الحالة أيضًا، وقد يتطلّب سلوكه استخدام جافاسكربت لتعديل قيمة خواص ARIA عند الضغط. إذا كان من المفترض أن يعرض مفتاح التبديل خيارين بشكلٍ واضح ويمكّنهما: عام وخاص برأيي، عندها سيكون استخدام زوج من أزرار الانتقاء له معنىً كبير. يعلن زري الانتقاء، كلّ منهما بتسمية خاصة به، عن تقنيات جذابة كخيارين منفصلين يجب على المستخدم اختيار أحدهما، وسهلة الوصول تمامًا عبر لوحة المفاتيح، ولا تتطلب ARIA أو جافاسكربت لتعمل، واستخدم قليلًا من الـCSS لها لتخلق تصميم زر "التبديل المزدوج" إذا أردت (حقيقةً لا أعرف ماذا أسميه بشكلٍ آخر) وستكون قمت بكل العمل. قد يختلف قليلًا الترميز والتصميم لمثل هذا الحل بين المطورين، ولكن جوهر الشيفرة سيكون نفسه. وهذا سيقودني إلى التجربة الحية… مثال حيّ هذا تنفيذي لمفتاح تبديل بين خيارين. لقد قمت بإنشاءه ليس كحلّ لمفتاح codepen، لكن ببساطة كمفتاح لتبديل المظهر مضيء/مظلم الذي أستخدمه في حديثي: سيكون مفتاح التبديل في سياق مجموعة أكبر من عناصر النموذج، لهذا السبب ليس لدي أيّ مجموعة حقول (fieldset) أو وسيلة إيضاح (legend) في العرض التجريبي. وأعلم جيّدًا أنّك قد تعدّل الشيفرة لعدم الحاجة إلى عناصر span إضافية في الترميز وتستخدم عناصر زائفة بدلًا منها، ولكنني على أيّة حال قد اخترت هذه الطريقة، فقط لأنني أريدها. جرّب أن تلعب بالشيفرة وتزيل تعليقات بعض التصاميم (حاول إزالة عناصر span بشكلٍ خاص لتشاهد كيف يبدو مفتاحي بدونها) لتحلل الشيفرة وتمتلك فكرة أفضل عن خياراتي وما الذي كنت أحاول تحقيقه. أضفت أيضًا تصميمًا للتأكّد من أنّ مستخدمي لوحة المفاتيح يمكنهم رؤية مكان التركيز، إذ أنني غطيت أزرار الانتقاء الافتراضية بعناصر span، ولم يتم تسليط الضوء على التسميات بشكلٍ افتراضي. استخدمت :focus-visible فقط في البداية (مع polyfill) لكنه لم يعمل كما هو متوقع في متصفحي فايرفوكس وسفاري، لذا انتهيت بإضافة :focus مجددًا واستخدمته بدلًا من :focus-visible. وأعلم أيضًا أنّ تصميم التركيز ليس جميلًا جدًا، لكن هذا العرض التجريبي هو مجرد دليل للمفهوم لذا لا يحتاج أن يكون جميلًا بشكلٍ ملفت للنظر. إذا كنت ترغب في مشاهدة عرض تجريبي آخر يستخدم أيضًا أزرار انتقاء لكن ينفذّها بطريقةٍ مختلفةٍ، بدون عناصر spanواستخدام عناصر زائفة، راجع هذا التصميم على codepen لصديقي Scott O’hara: كلمات أخيرة دعني أبدأ بالقول أنني قد أكون مخطئة. أعلم أنّ مستخدمي codepen قد يريدون شيئًا آخر مختلف تمامًا، أو يفكرون في شيء ما مختلف تمامًا. لذلك لا تهدف هذه المقالة إلى إعادة تصميم زر التبديل في codepen، إنّما تقدم توثيقًا لبحثي وتدريبي على التفكير أثناء العمل على إنشاء زر التبديل الخاص بي لحديثي. سأحتاج أن أقوم بالمزيد من الأبحاث عندما يحين الوقت لمتابعة عملي على ورشة العمل الجديدة الخاصة بي، وقد تتغير الأمور، وأعلم أنني سأتعلّم وأعرف المزيد عندما أنشئ مفتاحي التالي. لكن، حتى ذلك الحين، أعلم أنني حصلت على منشور المدونة هذا للإشارة إلى بعض الأفكار والآراء التي دارت في ذهني عندما اتخذت الخطوة الأولى لذلك. أتمنى أن تجد شيئًا مفيدًا بقراءتك لهذا المقال. إذا حصل ذلك بالفعل، فشكرًا لقراءتك. شكرًا. ترجمة -وبتصرف- للمقال On Designing and Building Toggle Switches لصاحبته Sara Soueidan
  3. …إنّه أصعب مما ظننت اليوم هو أحد تلك الأيام التي بدأت مع بحث غوغل عن سؤال/مسألة أخرى تتعلق بسهولة الوصول (accessibility). أنا أعمل على مشروع جديد لعميلتي Provta وجزء من هذا المشروع هو بناء تلميح مساعدة بسيط وجميل يشرح للقارئ/المستخدم عن ماهيّة حاسبة فرامنغهام. يتم تشغيل التلميح بواسطة أيقونة مساعدة صغيرة مثل تلك التي تظهر في الزاوية اليمنى العليا من لقطة الشاشة هذه: كما هو الحال مع كل مشروع، فإنّ البدء بالتفكير ما هو عنصر HTML الذي سأستخدمه لترميز المكوّن هو أوّل شيء فعلته. لكن اتّضح أنّه لا يوجد عنصر HTML لترميز تلميح كهذا. وعندما لا يكون لدينا عناصر دلالية لترميز مكوناتنا، فإنّنا نواجه التحدي في التأكّد من أنّ التقنيات المساعدة (ATs)- التي عادةً ما تكون قادرة على فهم ونقل المعنى عن طريق الدلالات - قادرة على فهم ما هي عناصرنا بالفعل وماذا تفعل. هل تنقذنا خاصية ARIA؟ جعل العناصر سهلة القراءة من قِبل التقنيات المساعدة عندما لا يكون HTML كافيًا باستخدام خاصيّات ARIA. مثلًا، يمكنك الحصول على ذلك بإنشاء شريط تقدّم بدون استخدام عنصر <progess> من HTML غير المدعوم جيّدًا. يمكن جعل هذا الشريط يبدو ويتصرّف كأنّه شريطًا باستخدام CSS، لكن إذا كنت تستخدم عناصر div وspan لبنائه، فأنت بحاجة لإعطاء التقنيات المساعدة المزيد لصنع شيءٍ ما من تلك العناصر غير الدلالية. هذا هو المكان الذي تصبح فيه خاصيّات ARIA مثل role="progressbar"‎ ورفقائها المساعدين aria-valuenow، aria-valuemin وaria-valuemax (من بين أمور أخرى) مفيدةً. يمكنك تحديث القيمة داخل aria-valuenow باستخدام بعض الجافاسكربت كلّما تقدّم المستخدم في الخطوات قدّمتها له وهذا جيّد عمومًا. يمكنك قراءة المزيد حول هذا الموضوع على MDN إذا كنت مهتمًا أو غير معتاد على ذلك. عظيم! لدينا قيمة الخاصيّة ARIA لمساعدتنا في الإشارة إلى أن عنصرًا معينًا هو تلميح: tooltip. باستخدام role="tooltip"‎ فإنّ التقنيات المساعدة تعرف أنّ هذا العنصر هو بالفعل تلميح. لكن عادةً ما يتم تشغيل التلميحات بواسطة إجراء يتم تنفيذه على عنصر آخر. دعنا أولًا نعود إلى الأساسيات. حسب ويكيبيديا: التلميح أو infotip أو النص المُساعد هو عنصر واجهة مستخدم رسومي شائع، يُستخدم جنبًا إلى جنب مع مؤشر، والذي يكون عادةً مؤشر الفأرة. عندما يمرّر المستخدم مؤشر الفأرة على عنصر ما، بدون الضغط عليه، قد يظهر التلميح بشكل مربع صغير يتضمّن معلومات حول العنصر الذي تمّ تمرير المؤشر فوقه. لا تظهر التلميحات عادةً على أنظمة التشغيل في الأجهزة المحمولة، لأنّه لا يوجد مؤشر (على الرغم من أنه قد يتم عرض التلميحات عند استخدام الفأرة). المشكلة التي أواجهها هي هذا الجزء "لا تظهر التلميحات عادةً على أنظمة التشغيل في الأجهزة المحمولة، لأنّه لا يوجد مؤشر"، لأنّ مستخدمي الهواتف المحمولة لهم الحق أيضًا في الحصول على المعلومات على الصفحة مثل المستخدمين الذين لا يستخدمون الهواتف المحمولة. يجب أن تكون قادرًا على فهم نقاط فرامنغهام سواءً كنت تستخدم فأرة أو إصبعك، لذا فإنّ هذا التلميح يجب أن يكون سهل الوصول في جميع الأحوال، وهنا تبدأ المتعة. تشغيل فتح/إغلاق التلميح علمتُ أنني لا أستطيع الاعتماد على التمرير وحده (بالرغم من أن العميلة طلبت التمرير فقط) لأنّه إذا لم يكن لديك شاشة تعمل باللمس مع قلم رقمي فاخر يسمح بتشغيل التمرير، فلن تكون قادرًا على رؤية النص المساعد داخل التلميح، وهذا غير مقبول. بدأت في عرض التلميح عندما يتم التمرير والضغط على أيقونة المساعدة معًا. من الجدير بالذكر عند هذه النقطة أنّ التلميحات تعمل أحيانًا على شاشة اللمس والمؤشر بدون أن تضطر للقيام بأي عملٍ إضافي، مثل تلك التي تظهر كتسميات مساعدة في حقول الإدخال input، مثل هذا المثال لـ Heydon Pickering. في سيناريوهات مثل عندما يكون role="tooltip"‎ مع aria-describedby فإنّ هذا كافٍ لتعرف التقنيات المساعدة أنّ هذا الجزء من النص يحتاج إلى تلميح يصف محتوى أو وظيفة حقل الإدخال. من السهل جدًا وضع علامة على التلميح في هذه الحالة لأنّه واضح بالفعل بما فيه الكفاية. كل ما تبقى لك هو إظهار/إخفاء النص عندما يتم التركيز على حقل input، وهذا يمكن أن يتم باستخدام سطري CSS فقط. التجربة ممتازة بالنسبة لشاشات اللمس والفأرة، وسيجرّبها الجميع بنفس الطريقة. ومع ذلك فإنّ الأمور غير واضحة بشكلٍ جيّد عندما يكون لديك مثال كمثالي في المشروع الذي أعمل عليه. ويبدو أنني لست الشخص الوحيد الذي كان هنا وكان مرتبكًا تمامًا في كيفية القيام بهذا الأمر. إليك بعض الأفكار التي دارت بذهني عندما كنت أفكر في تنفيذ هذا: استخدام عنصر <button> لتشغيل فتح وإغلاق التلميح. سيكون التلميح مخفيًا بالحالة الافتراضية لذا فإنّ التقنيات المساعدة لن تقرأه بصوتٍ عالٍ أثناء تحرّك المستخدم إلى أسفل الصفحة. يقاطع التلميح تدفق المحتويات في حالة مشروعنا لذا يجب عرضه عند الطلب فقط. "عند الطلب" تعني عندما يمرر/يضغط/ينقر/يركّز للتشغيل سيُخفى التلميح في الحالة الافتراضية باستخدام display: none ويُعرض فقط عند الطلب. بما أنّ الدلالات و"كيف ستقرأ الآلات هذا" هي التي ستحدد كيف سأبدأ بكتابة شيفرة HTML، فكرت: إذا استخدمت <button> يجب أن أستخدم aria-controls للإشارة إلى عناصر التحكم للزر والتي ستُظهر/ستخفي بعض العناصر عندما تُضغط. الأمور جيّدة. عند الحديث عن الدلالات، يمكنني أيضًا استخدام رابط <a> يرتبط بقسم معين في الصفحة، وهو في حالتي العنصر الذي يحتوي على النص المساعد. عند استخدام رابط، سينتقل المستخدم إلى قسم الصفحة الذي يرتبط به هذا الرابط. أنا أكره القفز في الصفحات، أتجنّبه مثل الطاعون إلا إذا كان قرار القفز مقصودًا. (مثلًا، رابط "العودة إلى الأعلى" في كل صفحة طويلة). أحتاج جافاسكربت لتجنب القفز عند استخدام <a>، ولجعل <button> يفتح ويُغلق التلميح. أريد تجنّب جافاسكربت واستخدامه فقط عند الضرورة لسببٍ شاملٍ. ماذا لو كان المستخدم رجلًا كبيرًا ويستخدم جهازًا قديمًا تمّ تعطيل الجافاسكربت به؟ عادةً، أبحث عن آراء ثانية، عندما أحتار بين حلّين وعندما تبدأ الأفكار تدور في ذهني حول استخدام جافاسكربت وعدم استخدامها. مصدري الأول لرأيٍ ثانٍ هو غوغل. فكرت: "يجب أن يكون شخصًا ما قد بنى واحدًا من قبل وكذلك يجب أن يكون شخصًا غيره قد قام بذلك، لذا دعنا نرى كيف تعاملوا معه وحلّوا هذه المشكلة". عند تلك النقطة كنت مازلت أفكر "أريد القيام بذلك بدون استخدام جافاسكربت إذا كان ذلك ممكنًا"، لكن صدّق أو لا تصدّق أنا أحب جافاسكربت ومنفتحة تمامًا بشأن استخدامها إذا كان الحلّ المعتمد عليها أفضل من غيره من الحلول. قادني البحث في غوغل إلى سؤال كان بالضبط نفس سؤالي. وجدت الكثير من الأسئلة المتشابهة لكنهم كانوا جميعًا أشبه بمثال تلميح حقل الإدخال. وجدتُ مناقشةً قديمةً جدًا بدأتها Zoe Gillenwater التي كان عندها نفس السؤال الذي لدي الآن في عام 2011. أدرك أنّ بعض التفاصيل التقنية هناك ستكون أو قد تكون غير صالحة اليوم لكن المبادئ العامة تبقى صالحة. أنصح بشدّة بقراءة هذه المناقشة قبل متابعة قراءة هذا المقال لأنّ كل شيء آخر هنا يعتمد على بعض الأفكار التي حصلت عليها من تلك المناقشة. النقاط الرئيسية المذكورة في هذه المقالة: تجنّب استخدام العنصر <button> وتمسّك باستخدام العنصر <a> لأنّه يمكن استخدام الروابط لأي شيء تقريبًا بينما يجب ترك الأزرار للاستخدام في النماذج. يجدر بي القول أنّني لا أتفق مع هذه النقطة، إذا كنت تعرف سببًا جيّدًا لتجنّب استخدام <button> الرجاء إخباري. عند استخدام العنصر <a> يجب أن تأخذ بالحسبان/تتذكر ما يلي: إذا كنت تستخدم <a> وتمّ تضمين النص المساعد بداخله: ستبدو الشيفرة كالتالي: <a href="#" aria-describedby="#tip"> <!-- your icon here, img or svg --> <span id="tip"> Your hint text here </span> </a> سيكون النص قابلاً للقراءة مع التدفق (وهو غير مناسب في حالتي) لأن محتوى النص المساعد يقاطع تدفق المعلومات والبيانات المعروضة في المحتوى الرئيسي. يمكنك إخفاء وإظهار النص باستخدام CSS فقط مع display: none وdisplay: block على التوالي عندما يتم التمرير فوق الرابط. هذا جيد للمستخدمين المبصرين باستخدام الفأرة. لكن… إذا أخفيت النص باستخدام display: none داخل <a> وعرضته عند التركيز فإنّ التقنيات المساعِدة لن تكون قادرة على قراءة النص المعروض لأنّ محتويات الرابط تُعلن فقط عند focus الأولى ولن يتم إعادة الإعلان عنها عندما يُعرض النص داخل الرابط مع display: block. باختبار ذلك، لاحظتُ أنّه بما أنّ الرابط لا يرتبط فعليًا بأي مكان فإنّ المستخدم سينتقل إلى أعلى الصفحة عندما ينقر الرابط في شاشة تعمل باللمس. هذا أمر فظيع بشكلٍ خاص في حالتنا لأنّه لدينا صفحة طويلة والانتقال إلى الأعلى سيُربك المستخدمين. يمكن حلّ هذا باستخدام جافاسكربت preventDefault لمنع الحدث الإفتراضي عند النقر. (برأيي: أنا لا أحب حقيقة أنّ الرابط لا يرتبط بأيّ مكان. إنّه رابط ذاتي التضمين، برأيي، إذا كان هذا يصنع فرقًا فإنّه ينفي دور الرابط للبدء به. كنت أعلم أنني لا أريد استخدام هذا على الرغم من أنّني اختبرته بدافع الفضول.) إذا كنت تستخدم <a> وجعلته يرتبط بجزء نصي منفصل (ليس من فروع الرابط نفسه): ستكون الشيفرة كالتالي: <a href="#tip"><!-- icon here --></a><div id="tip"> <!-- tooltip text here --> </div> لا يزال الرابط يسبب انتقالًا في الصفحة ما لم يتم استخدام جافاسكربت. في شاشات اللمس، يؤدي النقر على الرابط إلى التركيز عليه وهذا بدوره يفتح التلميح، لكن تبقى صورة الرابط كما هي ولا توجد طريقة لإغلاق التلميح ما لم ينقر المستخدم أي مكان خارجه لإزالة التركيز عليه، وهذا غير بديهي لأنّ هذا سينطوي على الكثير من الجهد المعرفي والتخمينات والتساؤلات "كيف أغلق هذا" والإحباط، خاصةً أنّ التلميح يغطي المحتوى عندما يفتح. يأتي كل حلّ لا يستخدم جافاسكربت بجانب سيء للغاية يؤثّر سلبًا على تجربة المستخدم. جافاسكربت هي الطريقة التي تجعل هذا يعمل مع كل مستخدم بشكلٍ صحيح في جميع الأحوال. إنّها تحلّ كل واحدة من المشكلات المذكورة، باستثناء مشكلة "هذا الرابط لا يرتبط بأي مكان" والتي لا يمكن حلّها إلا بعدم استخدام رابط لا يشير إلى أيّ مكان. D: (قد لا يوافق البعض على أن الرابط الذي لا يرتبط بأيّ مكان ليس خاطئًا أو غير صالح عمليًا، لكن لا أظنّ ذلك شخصيًّا.)، انتهي بي الأمر باختيار حلّ يعمل باستخدام جافاسكربت ولديه آثار غير سيئة للغاية. يغطي هذا الأفكار والاعتبارات الرئيسية التي كانت لدي إلى حدٍّ كبيرٍ. خاتمة جافاسكربت أمر ضروري لبناء مكونات تفاعلية متاحة بالكامل. يمكنك بالتأكيد الابتعاد عن استخدامها في بعض الحالات، لكن الكثير من قابلية الوصول تتطلّب جافاسكربت. بعض أدوار وخاصيات ARIA ضرورية للغاية لجعل المكونات سهلة للوصول، والكثير منها لن تسلك السلوك الذي تحتاجه ما لم تجعلها تعمل مع جافاسكربت. بما أنّ عميلي هو شركة صحيّة، فمن الممكن أن يكون لدى المستخدمين شكل من أشكال الإعاقة أو غيره لذا يجب أن تكون الجافاسكربت معطّلة لديهم، لذا من الآمن التفكير بعدم استخدام جافاسكربت بدلًا من تعطيلها. إذا كنت مهتمًا بمعرفة المزيد حول ما تتطلّبه الخاصيّات، أنصح بشدّة بفحص مقاييس دور ARIA من WhatSock. فهي توفر نظرة عامة سهلة القراءة. أيضًا لدى Paul J Adam عرض تجريبي يظهر الطرق المختلفة لعرض تلميحات عند التمرير. لا تزال أمثلته تستخدم جافاسكربت لبناء مكونات أكثر قابلية للوصول بتشغيل خاصيات ARIA عندما تفتح/تغلق التلميحات. هذا العرض يستحق تفحّصه بالتأكيد. ترجمة -وبتصرف- للمقال Building a fully-accessible help tooltip لصاحبته Sara Soueidan
  4. يمكن أن تقوم SVG بأكثر بكثير من مجرد عرض الصور الثابتة، إذ تعد إمكانياتها الحركيّة واحدة من أكثر مميزاتها قوةً، مما يعطيها مزايا فريدة عن صيغ الصور الأخرى، وهي أحد الأسباب العديدة التي تجعل SVG أفضل من الصور النقطية، بما فيها GIF. ولكن هذا، بالطبع، ينطبق فقط على الصور التي تعد مرشحةً جيدةً لتكون SVG، مثل: table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } الشعارات، الصور غير المعقدة، المعتمدة على الأشعة، أدوات التحكم بواجهة المستخدم، الرسوم البيانية، والأيقونات. بالطبع، إذا كان لديك صورة أكثر ملاءمةً للصيغة النقطية – مثل الصور الفوتوغرافية أو الرسومات المعقدة جدًا المعتمدة على الأشعة (والتي من الطبيعي أن تكون كبيرة الحجم جدًا مثل صور SVG)، فعليك استخدام صيغة الصور النقطية بدلاً منها. لا يجب أن تكون الصورة مرشّحاً جيداً لـ SVG فقط، ولكن يجب أن يكون SVG مرشّحاً جيداً للصورة. فمثلًا، إذا كان حجم الصورة صغير جداً كصور PNG، يجب استخدام PNG، وتقديم إصدارات/معاملات دقة مختلفة للصورة باستخدام srcset أو <picture>، بالاعتماد على ما تعمل عليه وتحاول تحقيقه. عمومًا، الصور المذكورة أعلاه هي عادةً مرشّحات مثالية لـ SVG. وإذا كنت ستحرّك أيًا منها، فإنّ الطريقة المعقولة للقيام بذلك هي إنشاء صورك المتحركة عن طريق تحريك شيفرة SVG. ومع ذلك، في الأسبوع الماضي، ظهر رابطًا منبثقًا في يوميات حسابي على التويتر يشير إلى مجموعة من الأيقونات المتحركة كـ GIF. إنّ أول ما مرّ في ذهني عندما رأيتهم بأنّهم كانوا مرشحين مثاليين لـ SVG ويجب أن يتم إنشاؤهم كصور SVG، وليس صور GIF. في العديد من الحالات يمكنك استبدال صور GIF بصور SVG بالفعل، تمامًا كما يمكن أن تستبدلها بتنسيقات الصور النقطية الأخرى لمرشّحين مثل تلك المذكورة أعلاه. إنّ القدرة على تحريك صور SVG هي ما يمنحها هذه الميزة والقدرة. وهذا ينطبق على عناصر أكثر من الأيقونات المتحركة. لذا، لهذا السبب أعتقد أنّه يجب عليك استخدام SVG بدلًا من صور GIF كلما استطعت. جودة الصورة الميزة الأولى لاستخدام SVG بدلًا من GIF - أو أية صيغة صورة لهذه المسألة - هي - كما هو متوقع - ميزة SVG الأولى: استقلالية الدقة. ستبدو صورة SVG فائقة الوضوح عند أي دقة للشاشة، بغض النظر عن حجمها. في حين أن صور GIF - صيغة الصورة النقطية - لا تظهر كذلك. حاول تكبير الصفحة التي تحتوي على صورة GIF وشاهد كيف ستبدو صورة الـ GIF منقطة ومحتوياتها غير واضحة. مثلّا، يبدو تسجيل GIF التالي لحركة صورة SVG جيدًا بهذا الحجم الصغير: يؤدي تكبير الصفحة عدّة مرات إلى جعل الصورة غيرَ واضحةٍ وتصبح حواف ومنحنيات العناصر الداخلية مسننةً، كما تشاهد في الصورة أدناه: بينما إذا قمت بالتحقق من عرض SVG التجريبي وتكبير الصفحة، فإن محتوى SVG سيبقى واضحًا وصافيًا بغض النظر عن مقدار التكبير. لتوفر صورًا واضحةً للشاشات عالية الدقة عند استخدام صيغة صورة نقطية مثل GIF، تحتاج إلى استخدام <picture> أو srcset وتشغيل الصور بسياقات مختلفة. بالطبع، كلما زادت دقة الصورة، زاد حجم الملف. مع صور GIF، سيصل حجم الملف لحجمٍ كبير بشكلٍ هائل. لكننا سنصل إلى ذلك في غضون دقيقة، كذلك، فإنّ استخدام صور GIF عالي الدقة وعرضها بحجمٍ أصغر للهواتف المحمولة يعد أمرًا سيئًا بالنسبة للأداء، لذا لا تفعل ذلك. عندما تنشئ أيقونات أو صور GIF متحركة، أبعادها ثابتةٌ. غيّر الأبعاد أو كبّر وصغّر الصفحة، وستحصل على صورة غير واضحة. أمّا مع SVG، ستتخلص من مشكلة الحجم، وستحصل على وضوحٍ دائم. يمكنك إنشاء صور SVG صغيرة وتغيير أبعادها وفق احتياجاتك دون التضحية بوضوح الصورة. نتيجة صورة GIF صورة SVG متحركة GIF مثل تنسيقات الصور الأخرى تمامًا، غير مستقلة الدقة، ولذا ستبدو غير واضحة عند تغيير حجمها أو عرضها بدقة أعلى صورة SVG قابلة لتغيير الحجم ومستقلة الدقة، وستبدو واضحةً بأي دقة شاشة الألوان والشفافية ربما يكون السبب الأول لتبتعد عن التعامل مع صور GIF هو الطريقة التي يتعامل بها مع الشفافية، خاصةً عندما يتم عرض الصورة على خلفية غير الخلفية البيضاء. هذه مشكلة تظهر غالبًا عند استخدام أيقونات GIF (سواءً كانت متحركة أم لا)، عندما يتم إنشاء الأيقونات عادةً بخلفيات شفافة. مثلًا، فكر مليّا بالدائرة التالية المحاطة بحدّ، أُنشئت كصورة SVG (يسار) وGIF بخلفية شفافة (يمين). المشكلة واضحة بمجرد النظر إلى الصورتين: تحتوي دائرة GIF على حواف رمادية حول حدّها. إذا كنت لا تعاينها في المتصفح، فقد لا يكون التأثير مرئيًا لك لأنّه من الممكن ألا يتم تطبيق أنماط الشكل. فيما يلي لقطة توضح المشكلة (على اليمين): يحدث هذا لأن الشفافية في صور GIF ثنائية. هذا يعني أن كل بكسل إما يعمل أو لا يعمل؛ البكسل سيظهر إما شفافًا أو ممتلئًا بشكلٍ كامل. وهذا بدوره يعني أنّ الانتقال بين اللون الأمامي ولون الخلفية ليس سلسًا، والتشوهات الناتجة تظهر بسبب تردد العينات غير المناسب، والمعروفة باسم التعرج. عندما لا يكون الخط مستقيمًا تمامًا، فإنّه يتسبب بأن تكون بعض البكسلات (حول الحواف) شفافة جزئيًا وممتلئة جزئيًا ، لذلك يحتاج البرنامج إلى معرفة اللون الذي يجب استخدامه لهذه البكسلات. تأثير الهالة "ناتج عن كل البكسلات التي كانت ممتلئة أكثر من 50٪ امتلاءً كاملاً وتحمل لون الخلفية بدلاً من اللون الذي تم تنقيطه" كريس ليلي. لذلك يكون هذا التأثير عادةً ناتجًا عن تلوث البكسل من لون الخلفية التي تتكون الصورة على أساسه مقارنةً بما هي عليه عند إنشائها/حفظها في محرر الرسومات. عادةً ما يحاط التعرج بمكافح تعرج، ولكن هذا ليس بالأمر البسيط عندما تكون الشفافية ثنائية: حلّ هذه المشكلة هو الشفافية المتغيرة، والمعروفة باسم قناة ألفا، والتي تسمح بدرجات متفاوتة من الشفافية وبالتالي انتقالًا أكثرَ سلاسةٍ بين اللون الأمامي ولون الخلفية، وهذا غير متوفر في GIF؛ وبالتالي، هذا يسبب مشكلة تأثير الهالة. تظهر الصور ذات تأثير الهالة أفضل عادةً عند استخدامها مع خلفيات بيضاء؛ أي لون خلفية آخر عالي التباين سيجعلها تبدو مشوهةً. لست متأكدةً تمامًا مما إذا كانت هناك طريقة لحل هذه المشكلة، لكنني لم أجد بعد صورة GIF ذات خلفية شفافة وحواف منحنية لم تكن بها هذه المشكلة. لقد رأيت الأشكال المستطيلة تعاني منها أيضًا. إذا كنت ترغب في استخدام صورتك/أيقونتك على خلفية غير بيضاء - مثلًا، على خلفية تذييل داكنة، قد يكون هذا لوحده يبعدك عن التعامل مع GIF، ولكن هناك أسباب أخرى تجعل استخدام SVG أفضل من GIF أيضًا، والتي سنغطيها في الأقسام التالية. ملاحظة: إذا كنت تقرأ هذه المقالة باستخدام المتصفح ولكنك لا ترى الحواف في الصورة الأولى على شاشة أصغر، فحاول تكبير الصفحة لترى التأثير. لماذا من الممكن ألا تكون قادرًا على رؤية الحواف على أحجام أصغر؟ الإجابة هي: ينعّم المتصفح الحواف كجزءٍ من عملية تغيير حجم الصورة. هل هذا يعني أنّه يمكنك استخدام هذا للتخلص من الحواف والاستمرار باستخدام GIF؟ نعم تستطيع. ولكن للقيام بذلك، يجب عليك استخدام صور GIF بحجم أكبر بكثير من الحجم الذي تريد أن تكون الصورة به، ثم تغيير حجمها. هذا يعني أيضًا أنك ستعرض صورًا للمستخدمين أكبر بكثير مما يحتاجون إليه، وبالتالي استهلاك المزيد من النطاق الترددي على الهاتف المحمول، بالإضافة إلى الإضرار بالحجم العام للصفحة وأدائها. رجاءً لا تفعل ذلك. نتيجة صورة GIF صورة SVG متحركة صور GIF قادرة على الشفافية الثنائية فقط. يسبب هذا التشوهات، المعروفة باسم تأثير الهالة التي تظهر كلما تمّ استخدام الصورة أو الأيقونة مع خلفية غير بيضاء كلما كان تباين لون الخلفية مع الصورة أعلى، كان تأثير الهالة أكثر وضوحًا مما يجعل الأيقونات غير قابلة للاستخدام عمليًا. تأتي صور SVG مع قناة ألفا ولا تعاني من أيّة مشاكل عند استخدامها مع ألوان خلفية مختلفة. تقنيات الصور المتحركة وأداؤها يمكنك تحريك صور SVG باستخدام CSS أو جافاسكربت أو SMIL، وتمنحك كلٌّ منها مستوىً مختلفًا من التحكم يمكنك الاستفادة منه لإنشاء جميع أنواع الصور المتحركة على عناصر SVG. لا توجد "تقنيات" لتحريك صور GIF. يتم تحريكها من خلال عرض سلسلة من الصور - صورة لكل إطار - بشكلٍ تسلسلي، بأسلوبٍ ثابتٍ وسرعةٍ ثابتةٍ. تعلم أنّ هذه الطريقة التي تعمل بها صور GIF فقط. ومن المؤكد أنّه يمكنك الإبداع مع أيقوناتك قبل تحويلها إلى صور GIF ثم "تسجيل" الحركات وتحويلها إلى GIF، ولكن ما مدى جودة مظهرها؟ وما مدى السيطرة على وقت الحركة التي ستحصل عليها بعد ذلك؟ لا شيء. لن تبدو الحركة سلسةً ما لم تتأكد من أن لديك 60 إطارًا على الأقل - أي 60 صورة - في الثانية لإنشاء صورتك الـ GIF. بينما مع SVG، فإنّ الحصول على صورٍ متحركةٍ سلسةٍ أسهل وأبسط بكثير من خلال الاستفادة من تحسينات المتصفح. حجم صور GIF أكبر من حجم صور PNG أو JPEG، وكلما طالت مدة حركة صورتك، زاد حجمها. ماذا لو أردت الآن تشغيل صورتك المتحركة لمدة 5 إلى 6 دقائق على الأقل؟ ماذا لو أردت تشغيلها لفترة أطول؟ ستحصل على الصورة. لنلقِ نظرةً أكثر تحديدًا على مثالٍ بسيط. يوجد أدناه صورتان: صورة SVG متحركة على اليسار وصورة GIF متحركة على اليمين. يتغير لون المستطيل في كلتا الصورتين بفترة ست ثوانٍ. يوجد العديد من الأشياء لملاحظتها هنا: تبدو صور GIF المتحركة سلسةً، لكن إذا ألقيت نظرةً قريبةً ستلاحظ أنّ مستطيل SVG يمر بمجموعة أكبر من الألوان عندما ينتقل من اللون الأولي إلى اللون النهائي. عدد الألوان التي يمر بها GIF محدودًا بعدد الإطارات. في الصورة أعلاه، تمر صورة الـ GIF بـ 60 إطارًا، أي 60 لونًا، بينما يمر SVG عبر الطيف بأكمله بين ظل اللون الوردي المستخدم واللون الأخضر النهائي. بالنسبة لتكرار الصور المتحركة مثل هذه، من الأفضل عمومًا تجنب قفزة اللون الظاهرة في الصورة المتحركة أعلاه، وإنشاء الصورة المتحركة بحيث تنعكس ما إن تصل إلى اللون الأخضر؛ بهذه الطريقة، ستنتقل بسلاسة إلى اللون الوردي ثم تبدأ الجولة الثانية من الصورة المتحركة من هناك أيضًا، مع تجنب قفزة الألوان القبيحة. مع CSS، يمكنك عكس حركة الصورة باستخدام قيمة اتجاه الحركة alternate. ولكن مع GIF، ستحتاج إلى العمل على عدد إطاراتك وربما ينتهي بك الأمر إلى مضاعفتهم لتحقيق ذلك؛ سيؤدي هذا بالطبع إلى زيادة حجم الصورة أيضًا. أحجام الصورتين الظاهرتين في الأعلى: حجم صورة GIF هو 21.23 كيلوبايت حجم صورة SVG هو 0.355 كيلوبايت هذا ليس فرقًا بسيطًا. ولكننا نعلم جميعًا أنه يمكننا تحسين صورنا. لذلك دعونا نفعل ذلك. يؤدي تحسين SVG باستخدام واجهة المستخدم الرسومية SVGO بالسحب والإفلات إلى تقليل حجم ملف الـ SVG إلى 0.249 كيلوبايت. لتحسين صورة الـ GIF، يمكنك استخدام واحدة من العديد من أدوات تحسين GIF عبر الإنترنت. لقد استخدمت ezgif.com لتحسين الصورة أعلاه. (توجد أدوات أخرى أيضًا؛ مثل gifsicle.) انخفض حجم الملف إلى 19.91 كيلوبايت. هناك العديد من الخيارات التي يمكنك الاختيار من بينها عند تحسين صور GIF. قمت بتحسين الصورة أعلاه بحيث يبقى عدد الإطارات نفسه، باستخدام ضغط Lossy GIF، الذي "يمكن أن يقلل من حجم ملف GIF المتحرك بنسبة 30 ٪ - 50 ٪ بتكلفة بعض التدرج / التشويش". يمكنك أيضًا تحسينها عن طريق إزالة كل إطار ذو الرقم n؛ مما يمكن أن يقلل من حجم الملف إلى أبعد حد، ولكن على حساب حركة الصورة التي لم تعد سلسةً بعد الآن. وفي حالة الصور المتحركة مثل الحالة المدروسة الآن، فإن إزالة الإطارات سيجعل التغيير في اللون "سريعًا" وملاحظًا. تتوفر أيضًا خيارات أخرى للتحسين مثل تقليل الألوان (الذي لن يكون مناسبًا للصور المتحركة المعتمدة على الألوان هنا) وخفض الشفافية. يمكنك معرفة المزيد حول هذه الخيارات في صفحة التحسين في موقع ezgif.com. للتلخيص: إذا كنت أردت أن تكون صورك الـ GIF المتحركة سلسةً، ستحتاج إلى المزيد من الإطارات في الثانية، وسيؤدي ذلك بالتالي إلى زيادة حجم الملف كثيرًا. بينما مع SVG، من المحتمل أن تحتفظ بحجم ملف أصغر بكثير. المثال أعلاه صغيرٌ، وأنا متأكد من وجود أمثلة أفضل، ولكني أردت أن يوضح المثال البسيط الفرق بين الصيغتين. حتى إذا كنت تحرّك المستطيل أعلاه باستخدام جافاسكربت أو حتى إطار عمل جافاسكربت — نظرًا لأن الصور المتحركة SVG لا تعمل في متصفح IE، مثلًا، من المحتمل أن يكون حجم ملف إطار العمل المقترن بحجم SVG أصغر أو يساوي على الأقل حجم صورة GIF. مثلًا، باستخدام GreenSock’s TweenLite، سيكون حجم SVG مع المكتبة المدمجة أقل من 13 كيلو بايت (والذي لا يزال أقل من حجم GIF)، إذ انّ حجم TweenLite المصغر هو 12 كيلو بايت. إذا انتهى بك الأمر إلى حجمٍ مساوٍ لحجم GIF، فإن الفوائد الأخرى لـ SVG ستزيد من الحجم وستحصل على المزيد منه. توجد بعض مكتبات جافاسكربت الأخرى التي تركز على مهام حركة معينة في وقت واحد، وتأتي بأحجام ملفات صغيرة بشكلٍ ممتاز (أصغر من 5 كيلوبايت)، مثل Segment التي تستخدم لتحريك مسارات SVG لإنشاء تأثيرات رسم خطية. حجم Segment المصغر هو 2.72 كيلو بايت. هذا ليس سيئًا للغاية، أليس كذلك؟ يمكن أن يكون هناك استثناءات، لذلك يجب عليك دائمًا الاختبار. ولكن بالنظر إلى طبيعة صور GIF وكيفية عملها، فمن المحتمل أن تجد أن SVG يُعدُّ خيارًا أفضل في معظم الحالات. ملاحظة: أداء SVG ليس في أفضل حالاته اليوم، ولكن نأمل أن يتغير هذا في المستقبل. توفر متصفحات IE/MS Edge أفضل أداء لعرض SVG بين جميع المتصفحات اليوم. على الرغم من ذلك، ستظل صور SVG المتحركة تبدو أفضل من صور GIF المتحركة، خاصةً عند التعامل مع الصور ذات الحركة الطويلة، لأنّ حجم ملف GIF - بافتراض تسجيلها بمعدل 60 إطارًا في الثانية - سيكون له تأثيرًا سلبيًا على الأداء العام للصفحة. تقدم المكتبات مثل GreenSock أداءً رائعًا أيضًا. نتيجة صورة GIF صورة SVG متحركة - صور GIF أكبر حجمًا عمومًا من صور SVG. حركة الصورة الأكثر تعقيدًا والأطول مدةً، تتطلّب عدد إطارات أكبر لإنشائها ولهذا يكون حجم الملف أكبر ويؤثر سلبًا على الأداء - ما لم تعمل حركة الصورة بمعدل 60 إطارًا في الثانية، فإنّ الحركة ستصبح مسننة وغير سلسة. أيضًا زيادة عدد الإطارات في الثانية سيؤدي إلى زيادة حجم الملف خاصةً للحركات الطويلة نتيجة: سيكون هناك حل بسيط يجب القيام به. إمّا أن تكون حركة صورة GIF ناعمة وسيتأثر الأداء وحجم الملف الكلي وحجم الصفحة بشكلٍ سلبي، أو ستعاني الصورة المتحركة GIF من إطارات أقل. سيتم المخاطرة بأحد أشكال الأداء في كلا الحالتين تستفيد صور SVG من تحسينات المتصفح عند تحريك العناصر. على الرغم من أنّ أداء المتصفح على عناصر SVG لا تزال في أفضل حالاتها، إلا أنّ الصور المتحركة ستبقى تعمل بشكلٍ جيد بدون الحاجة لتقديم تنازلات في أداء الصفحة لا يزال حجم ملف SVG معقولًا جدًا، إن لم يكن صغيرًا جدًا، مقارنةً بـ GIF، حتى عندما قد نحتاج إلى مكتبات معينة للصور المتحركة لإنشاء صور متحركة عابرة للمتصفحات إصلاح وتعديل الصور المتحركة إنّه من المحزن أن تستخدم صور GIF. ستحتاج إلى استخدام محرر رسومات مثل Photoshop أو Illustrator أو After Effects، على سبيل المثال لا الحصر. وإذا كنت مثلي، فلن يكون محرر الرسومات المكان الذي تصقل فيه مهاراتك، وستشعر أكثر أنّك في مكانك الصحيح أكثر عند إجراء تعديلات في الشيفرة، وليس في برامج تحرير الرسومات. ما الذي يحدث إذا كنت ترغب في تغيير توقيت صورتك المتحركة؟ أو إذا كنت تريد تغيير وظائف التوقيت لعنصرٍ واحدٍ أو عدة عناصر داخل صورتك؟ أو إذا كنت ترغب في تغيير الاتجاه الذي يتحرك فيه عنصرٍ ما؟ ماذا لو كنت تريد تغيير التأثير بالكامل وجعل العناصر في صورتك تفعل شيئًا مختلفًا تمامًا؟ ستحتاج إلى إعادة إنشاء الصورة أو الأيقونة مرةً أخرى. يتطلب منك أي تغيير الانتقال إلى محرر الرسومات والعمل مع الإطارات وواجهة المستخدم المعتمدة على الإطار. سيكون ذلك بمثابة تعذيب للمطورين، ومهمةً مستحيلةً لأولئك الذين لا يعرفون طريقتنا بالتعامل مع هذه المحررات بشكلٍ كافٍ لإجراء هذه التغييرات. مع SVG، فإن إجراء أي نوع من التغيير على الصورة المتحركة (الصور المتحركة) لا يُعدُّ سوى بضعة أسطر من الشيفرة. نتيجة (من وجهة نظر المطور) صورة GIF صورة SVG متحركة إصلاح وتعديل الصور المتحركة GIF يتطلّب إعادة إنشاء الصورة أو اللجوء إلى واجهة المستخدم في محرر الرسومات المعتمد على الإطارات للقيام بذلك، ويعدّ هذا مشكلة للمطورين في مواجهة التصميم عادةً، يمكن أن تتغير صور SVG ويتم التحكم بها بشكلٍ صحيح داخل شيفرة SVG - أو في أي مكان تم تعريف الصور المتحركة به، باستخدام عدة سطور من الشيفرة. حجم الملف، ووقت تحميل الصفحة والأداء في القسم السابق، ركزنا على أداء الصور المتحركة نفسها. في هذا المثال، أريد إلقاء بعض الضوء على أداء الصفحة ككل وكيفية تأثرها باختيار صيغة الصورة الذي تقوم به. في الحقيقة: كلما زاد حجم الملف، زاد التأثير السلبي على وقت تحميل الصفحة والأداء. بأخذ ذلك بالحسبان، لنرى كيف يمكن أن يساعد استخدام SVG بدلاً من GIF في تحسين إجمالي وقت تحميل الصفحة من خلال النظر إلى مثالٍ أكثر واقعيةٍ وعمليةٍ. في حديثي الأول عن SVG، منذ 18 شهرًا، ذكرت كيف يمكن استخدام SVG لاستبدال صور GIF المتحركة وكيف تؤدي إلى تحسين أداء الصفحة بشكلٍ عام. في ذلك الحديث، قدمت مثالًا واقعيًا لصفحة ويب واقعية استفادت مما توفره SVG وحصلت على الفوائد: الصفحة الرئيسية لـ Sprout. تحتوي صفحة الرئيسية لـ Sprout على صورتين متحركتين تم إنشاؤهما في البداية وعرضهما بشكل صور GIF. قبل عامين، كتب Mike Fortress مقالًا في مدونة Oak، يوضح فيه كيف قاموا بإعادة إنشاء صور GIF متحركة، خاصةً مخطط GIF (انظر الصورة أدناه) كصورة SVG متحركة. يشارك Mike في مقالته بعض الأفكار المثيرة للاهتمام حول أدائهم الجديد للصفحة كنتيجةٍ للتغيير إلى SVG: إنّه بالفعل فرقٌ كبيرً. مخطط Sprout هو المرشّح المثالي لـ SVG. لا يوجد سبب لتحريكها عن طريق تحويل الصور المتحركة إلى تسجيل GIF، عندما يمكن لـ SVG أن تحقق الكثير من الفوائد. يدرك Jake Archibald قوة صور SVG المتحركة أيضًا ، ويستخدمها لإنشاء الرسوم التوضيحية التفاعلية وتحريكها لاستكمال مقالاته. مقاله كتاب الطبخ دون اتصال يعتبر مثالًا ممتازًا (وبالمناسبة مقالًا ممتازًا). هل كان بإمكانه استخدام صور GIF للقيام بذلك؟ بالتأكيد. ولكن بالنظر إلى عدد الصور التي استخدمها، كان من الممكن أن تزيد صور GIF بسهولة الحجم الكلي للصفحة إلى 2 ميغابايت أو أكثر، بحيث يكون حجم كل GIF على الأقل مئات الكيلوبايت؛ في حين أن صفحة الويب بأكملها تبلغ حاليًا 128 كيلو بايت فقط مع جميع صور SVG المضمنة داخليًا، لأنّه يمكنك إعادة استخدام العناصر في SVG، لذا فإن أيّة عناصر متكررة لن تتسبب فقط في أن الصفحة بأكملها ستستعمل برنامج gzip كثيرًا، ولكن لكل صفحة سيصبح الحجم الكلي لصور SVG أصغر. الآن هذا رائع! سأبقي حالتي حول تحميل الصفحة والأداء هنا. ولكن لا يزال من المهم الإشارة إلى أنّه يمكن أن يكون هناك استثناءات. مرةً أخرى، في معظم الحالات، من المحتمل أن تجد أن SVG أفضل من GIF، لكنك ستحتاج دائمًا إلى الاختبار على أي حال. نتيجة صورة GIF صورة SVG متحركة صور GIF أكبر حجمًا بشكلٍ عامٍ من صور SVG مع الحركات المضافة لهم. وهذا يؤثر سلبًا على حجم الصفحة الكلّي ووقت التحميل والأداء. يمكن أن تُستعمل صور SVG وأن يُعاد استخدامها، مما يجعل حجم الملف أصغر عمومًا من GIF وهذا يحسّن من وقت تحميل الصفحة والأداء دعم المتصفح ربما تكون الميزة الوحيدة المتفوقة لصور GIF على صور SVG هي دعم المتصفح. تعمل صور GIF إلى حدٍ كبير في كل مكان، في حين أن دعم SVG أقل انتشارًا. على الرغم من أن لدينا العديد من الطرق لتوفير النسخ الاحتياطية للمتصفحات غير الداعمة - ويجب ألا يعوق دعم المتصفح الحالي أي شخص عن استخدام SVG، إنّ الصور الاحتياطية، إذا تم توفيرها كـ PNG أو JPG، ستكون ثابتة وبدون حركة. بالطبع ، يمكنك دائمًا تقديم GIF كخطوة احتياطية لـ SVG، ولكن يجب مراعاة الاعتبارات والعيوب المذكورة سابقًا. نتيجة صورة GIF صورة SVG متحركة تعمل صور GIF تقريبًا في كل مكان صور SVG يدعمها عدد أقل من المتصفحات، لكنها تأتي مع طرق كثيرة لتوفير صور احتياطية للمتصفحات التي لا تدعمها مخاوف سهولة الوصول (a11y#) انقل شيئًا ما إلى صفحة، أو أيّ مكان، لهذه القضية، لقد أضفت للتو تسليةً - شيءٌ ما من المؤكد أنّه سيجذب انتباه المستخدم بمجرد أن يبدأ التحرك. هكذا يعمل الدماغ البشري ببساطة. يعدُّ هذا أيضًا أحد الأسباب التي تركز عليها لافتات الإعلانات - وهي مبنية على - تركيزٍ قوي على الصور المتحركة. وهذا هو السبب في أنّ لافتات الإعلانات المتحركة مزعجةٌ للغاية. إنها تشتت الانتباه، خاصةً عندما تحاول أداء مهمة على صفحة تتطلب اهتمامك بالكامل، مثل قراءة مقال. تخيّل الآن صفحة بها مجموعة من الأيقونات المتحركة (أو الصور) التي لن تتوقف عن الحركة بغض النظر عما تفعله. لم نعد نتحدث عن صورة أو صورتين على الصفحة الرئيسية أو داخل مقالة هنا؛ نحن نتحدث عن متحكمات وعناصر واجهة المستخدم، والأيقونات الأصغر التي من المحتمل أن تكون موجودة في أماكن متعددة على الصفحة، وعلى صفحات متعددة. ما لم يكن من المفترض أن تكون الأيقونة عبارة عن رسوم متحركة بشكلٍ غير محدود - مثلًا، إذا كانت عبارة عن دوران متحرك أثناء مرحلة انتظار غير نشطة للمستخدم، فمن المحتمل أن تسبب مشكلة، وتصبح مصدر إزعاجٍ أكثر من كونها "شيئًا جميلًا". في الواقع، يمكن أن يصبح الأمر أكثر إزعاجًا لبعض الناس، لأن الحركة المستمرة يمكن أن تجعل بعض الناس حرفيًا يشعرون بالمرض. تناقش المصممة ومستشارة صور الويب المتحركة Val Head في مقالتها "تصميم رسوم متحركة للويب أكثر أمانًا من أجل حساسية الحركة" تأثيرات الصور المتحركة المفرطة الاستخدام في الويب على الأشخاص الذين يعانون من اضطرابات الدهليزية المتسببة بصريًا (منجم التركيز): تخيّل الآن ما إذا كانت الصور المتحركة لا تنتهي … مضاعفات مزدوجة. تشرح مقالة Val المشكلة بمزيدٍ من التفصيل، حيث تجمع ردود فعل من شخصين يواجهان هذه المشاكل بالفعل ويشاركان تجربتهما مع الصور المتحركة بأمثلةٍ مختلفةٍ. أحد الحلول التي يمكن أن تساعد في تجنب هذه المشاكل هو تزويد المستخدم بالقدرة على التحكم في الصور المتحركة حتى يتمكنوا من إيقافها عندما تصبح مزعجةً. يمكنك القيام بذلك باستخدام SVG. يمكنك التحكم بشكلٍ كاملٍ في الصور المتحركة وتشغيلها مرةً واحدة أو مرتين عند تحميل الصفحة - إذا كنت تحتاج حقًا إلى تشغيلها بمجرد قيام المستخدم "بالدخول" إلى صفحتك، ثم إظهارها عند تمرير مؤشر الفأرة لمرات متتالية، باستخدام سطورٍ قليلةٍ من الـ CSS أو جافاسكربت. لا تحتاج إلى المئات أو الآلاف من أسطر CSS أو جافاسكربت لإنشاء صورةٍ متحركةٍ للأيقونة، إلا إذا كانت أيقونتك مشهدًا معقدًا بالفعل فيها الكثير من المكونات المتحركة. لكنني أعتقد أنّه في هذه الحالة، لم تعد تمثل "أيقونةً" بعد الآن، وأنّها أصبحت عبارةً عن صورةٍ عاديةٍ أكثر. يمكنك الوصول إلى أقصى قدر من التحكم في إعادة التشغيل، والسرعة لكل تشغيلٍ لاحقٍ، وأكثر من ذلك بكثير، بفرض، بالطبع، أنّك تستخدم جافاسكربت للحصول على هذا المستوى من التحكم. أو يمكنك إضافة تبديل لمنح المستخدم القدرة على إيقاف تشغيل الصور المتحركة بلا حدود. لا يمكنك فعل ذلك باستخدام GIF … إلا إذا اخترت استبدال GIF بصورة ثابتة عند إجراء تبديلٍ معينٍ. قد يجادل البعض بأنّه يمكنك عرض نسخة ثابتة من الصورة – مثل صورة PNG مثلًا، ثم توفير إصدار GIF عند تمرير مؤشر الفأرة. ولكن هذا يسبب بعض المشاكل: إذا كانت الصورَ مضمنةٌ، فستحتاج إلى استبدال هذه الصور باستخدام جافاسكربت. هذا الإجراء لا يتطلب أي جافا سكربت إذا كنت تستخدم SVG. إذا كانت الصور عبارة عن صور أمامية (مضمنة في HTML باستخدام <img>)، وتحتاج إلى استبدال هذه الصور، فسينتهي بك الأمر إلى مضاعفة كمية طلبات HTTP لكلّ صورة. وإذا كانت صور الخلفية مضمنة في صفحة الأنماط (هذا غير مستحسن)، فإنّ الصور (خاصةً صور GIF) ستزيد حجم صفحة الأنماط وبالتالي تزيد الوقت الإجمالي لحظر عرض الصفحة. إذا كنت تبدّل/ عندما تبدّل مصادر الصورة عند تمرير مؤشر الفأرة، فهناك وميض ملحوظ بين الصورة الأولى والثانية في الاتصالات البطيئة. اتصالي بطيء؛ أحيانًا اتصال 3G بطيء، ولم أتذكر بعد وقتًا تم فيه استبدال صورة بأخرى عند تمرير مؤشر الفأرة، أو تم تغيير حجم منفذ العرض، أو أي شيء آخر، ولم أشاهد هذا الوميض. يزداد هذا الموقف سوءًا عندما تكون الصورة الثانية (GIF التي تحمّل عند تمرير مؤشر الفأرة) كبيرة الحجم إلى حدٍ ما — سيكون هناك وميض، تتبعه صورةً متحركةٌ بطيئةٌ غير متقنةٍ لـ GIF أثناء تحميلها بالكامل. هذا ليس جذابًا أبدًا. لذا، نعم، يمكنك تبديل مصادر الصورة للتحكم إذا أو عندما تشتغل الصور المتحركة GIF أو متى يتم ذلك، لكنك تفقد التحكم الدقيق في GIF وتؤثر على تجربة المستخدم مع واجهة المستخدم. يمكنك أيضًا التحكم في عدد المرات التي يتم فيها تشغيل الصور المتحركة في GIF - وهو أمرٌ رائعٌ، لكن هذا يعني أن الصور المتحركة ستعمل فقط عددًا محددًا من المرات. ثم لإعادة تشغيل الصور المتحركة عند تفاعل المستخدم، ستحتاج إلى اللجوء إلى الأسلوب أعلاه مع صور متعددة. يمكن تحقيق الصور المتعددة التي تحتاج إلى إصلاح، وطلبات HTTP المتعددة، والحل العام المُستهلَك غير الأمثل بصورة SVG واحدة. ضمّن صورة SVG واحدة على الصفحة. أنشئ الصورة المتحركة بالطريقة التي تريدها/تحتاجها. (أو أنشئ الحركة قبل تضمين الصورة.) شغّل وأوقف وتحكّم بالصورة المتحركة. وأمنح المستخدم القدرة على التحكم فيها أيضًا. لا توجد طلبات HTTP إضافية لكل صورة، ولا صيانة معقدة للخط الزمني للصورة المتحركة في محرر الرسومات، ولا توجد أي مخاوف تتعلق بسهولة الوصول أو مشاكل لا يمكن تجنبها ببضعة سطور من التعليمات البرمجية. نتيجة صورة GIF صورة SVG متحركة صور GIF لا يمكن إيقافها من قبل المستخدم بدون طلب صور إضافية وطلبات HTTP إضافية. لا تتم السيطرة بشكلٍ كاملٍ حتى يتم ذلك يمكن تخصيص صور SVG المتحركة بشكلٍ كامل لذا يمكن تفعيلهم وتعطيلهم والتحكم بهم من قبل المستخدم بدون الحاجة إلى طرق مستهلَكة سهولة الوصول إلى المحتوى صورة GIF صورة SVG متحركة صور GIF سهلة الوصول مثل صور PNG وJPEG باستخدام قيمة الخاصية `alt` لوصفهم. لا يمكن تمييز المحتوى الموجود داخل الصورة أو إتاحته مباشرةً لقارئات الشاشة بما يتجاوز الوصف العام للصورة يمكن الوصول إلى صور SVG ودلالتها. يمكن أيضًا وصف المحتوى المتحرك داخل الصورة وجعله قابلًا للقراءة من قِبل قارئات الشاشة باستخدام عناصر سهولة الوصول المدمجة في SVG، كما يمكن تحسينها باستخدام أدوار ARIA وخاصيّاتها أيضًا. (يمكنك أن تقرأ [هنا](https://www.sitepoint.com/tips-accessible-svg/) كل شيء عن جعل SVG قابلة للوصول) التفاعل لا يوجد الكثير لإضافته هنا، ولكن الحقيقة أنّه يمكنك التفاعل مع العناصر الفردية داخل SVG، أثناء أو قبل أو بعد تحريك الصورة، لكن هذا غير ممكن مع GIF. لذا، إذا كنت تستخدم GIF، فستفقد القدرة على فعل أي شيء يتجاوز تشغيل الصور المتحركة أو إيقافها، وحتى هذا لم يتم تنفيذه فعلاً باستخدام الـ SVG، كما رأينا، ولكن يتم تحقيق ذلك عن طريق مبادلة GIF بالخارج ببديلٍ ثابت. حتى تغيير ألوان العناصر داخل GIF سيتطلب صورًا إضافية للقيام بذلك. هذه ميزة أخرى لاستخدام SVG بدلاً من GIF. نتيجة صورة GIF صورة SVG متحركة لا يمكن أن تكون الحركات المحددة في صور GIF تفاعلية، لا يمكنك التفاعل مع العناصر الفردية داخل عنصر GIF، ولا يمكنك إنشاء روابط خارج العناصر الفردية أيضًا. محتوى SVG تفاعلي بالكامل. يمكنك إنشاء التفاعلات مثل تمرير ماوس الفأرة والضغط (والمزيد) التي تستجيب لها العناصر الفردية داخل صورة SVG. استجابة وتكيّف الصور المتحركة إنّ القدرة على تحريك صورة SVG مباشرةً من الشيفرة، بالإضافة إلى معالجة العديد من خاصياتها، تعطي وتضيف ميزةً أخرى على الصور المتحركة المعتمدة على GIF: القدرة على إنشاء صور متحركة جيدة الأداء، سريعة الاستجابة وقابلة للتكيّف، دون إضافة أي طلبات HTTP إضافية باستخدام بضعة أسطر من الشيفرة وبأحجام ملفات أصغر. كتبت سارة دراسنر مقالةً في مجلة Smashing Magazine تُظهر طرقًا مختلفة لتحريك SVG sprites. تتمثل إحدى هذه الطرق في وجود "مشاهد" متعددة داخل الـ SVG، وتحريكها باستخدام CSS، ثم تغيير "عرض" الـ SVG - عن طريق تغيير قيمة خاصية الـ viewBox - لإظهار مشهدٍ واحدٍ في وقتٍ واحدٍ، اعتمادًا على حجم منفذ العرض الحالي ومساحة الشاشة المتاحة. إذا كنت ترغب في إنشاء نفس الحركات باستخدام صور GIF، فستفقد إمكانيات التحكم في الصور المتحركة، كما ستحتاج إلى صور متعددة ربما تكون أكبر (في حجم الملف) من صورة الـ SVG الواحدة. ولكن إذا كنت لا ترغب في استخدام شيفرة SVG المتحركة، فيمكنك دائمًا إنشاء SVG sprite وتحريكه بالطريقة التي يمكنك بها تحريك أي صيغة صورة آخرى - باستخدام ()steps وعدة أسطر CSS. تتحدث سارة أيضًا عن هذه التقنية في مقالتها. لا يحتاج تحريك صور SVG أن يكون معقدًا، وهو يقدم أداءً أفضل بشكلٍ عام. نتيجة صورة GIF صورة SVG متحركة بما أنّه لا يمكن التحكم بالمحتوى داخل صورة GIF بالشيفرة، فلا يمكن جعل الصور المتحركة تتكيف أو تستجيب لتغيّرات منفذ العرض أو السياق بدون اللجوء إلى فصل الصور. بما أنّه يمكن تحريك محتوى SVG بشكلٍ مباشر باستخدام الشيفرة، فإنّه يمكن تعديل المحتوى والحركات بحيث يستجيب و/أو يتكيّف مع سياقات وأحجام منافذ عرض مختلفة، بدون الحاجة إلى اللجوء إلى أي أصول إضافية. كلمات أخيرة تتمتع صور GIF بدعم جيد جدًا للمتصفح، نعم، ولكن مزايا صور SVG تفوق مزاياها في جميع الجوانب تقريبًا. قد تكون هناك استثناءات، وفي هذه الحالات، تستخدم صور GIF بالتأكيد أو أي صيغة صور آخرى تقدم عملًا أفضل من SVG. قد تستخدم حتى فيديو أو HTML5 Canvas أو أيًا كان. يمكن أن توفر SVG الكثير من مزايا الأداء إلى القائمة عند موازنتها مع صيغ الصور الأخرى، وخاصةً GIF. وبالتالي، وبالنظر إلى كل ما سبق، أوصي بأنه في أي مكان يمكن استخدام SVG للصور المتحركة، يجب تجنّب استخدام صور GIF. أنت حر بالطبع في تجاهل توصيتي لكنك ستتخلى عن المزايا الكثيرة التي توفرها صور SVG المتحركة. ما لم توفر صور GIF الكثير من المزايا على صور SVG التي لا تحقق دعم المتصفح IE8 وأقل منه، فإنني أعتقد أن صور SVG ينبغي أن تكون هي السبيل للانطلاق. بعض المصادر لمساعدتك على البدء مع صور SVG المتحركة: عالم الصور المتحركة SVG. عدة طرق مختلفة لاستخدام SVG sprites في الصور المتحركة. إنشاء الصور المتحركة مع SVG. لدى GreenSock مجموعة من المقالات المفيدة حول تحريك SVG. Snap.svg ويُعرف أيضًا بأنّه "مكتبة jQuery للـ SVG". الصور المتحركة SVG باستخدام CSS وSnap.SVG. تصميم وتحريك صور SVG مع CSS. رسم خط متحرك في SVG. أرجو أن تكون قد وجدت هذه المقالة مفيدةً. شكرًا للقراءة. ترجمة -وبتصرف- للمقال Animated SVG vs GIF [CAGEMATCH]‎ لصاحبته Sara Soueidan
  5. يعدُّ التحضير لأسئلة المقابلة في مجال هندسة البرمجيات عملًا بدوام كامل تقريبًا، إذ هناك مصادر لا حصر لها على الإنترنت وغالبًا ما تكون هائلة عندما تبدأ العملية، ومن الطبيعي أن تكون متوترًا وخائفًا عند التقدم لمنصبٍ ما، لأنك تواجه خطر الحكم والرفض وهذا يكفي لإثارة قلق أي شخص. ومع ذلك فإن أفضل طريقة لتهدئة أعصابك هي الاستعداد الكامل حتى تعرف أنك تعطي نفسك أفضل فرصة للحصول على وظيفتك المثالية، ستقلل الممارسة والتحضير من الإحساس بالغموض وتساعد على تحضير نفسك للنجاح. إذا كنت مهندس برمجيات متخرج وتستعدّ لمقابلات العمل الأولى لك، فإليك المجالات المختلفة التي يجب التركيز عليها. أسئلة حول المعلومات الأساسية الخاصة بك يجب أن تكون مستعدًا للإجابة على الأسئلة المتعلقة بمعلوماتك الأساسية، خبرتك، مشاريعك السابقة ومعرفتك. تكون هذه الأسئلة عادةً مفتوحة وتمنحك فرصةً جيدةً لاختيار ما الذي تريد التحدث عنه، بعض الأمثلة على هذه الأسئلة يمكن أن تكون: أخبرني عن مشروع عملت عليه، وما الذي جعلك تستمتع أو لا تستمتع؟ أخبرني ماذا تعلّمت من العمل على هذا المشروع هل يمكنك التحدّث عن بعض الصعوبات التي واجهتها؟ كيف نسّقت المهام بين أعضاء الفريق؟ إذا قمت بإعادة هذا المشروع ثانيةً، هل ستفعل شيء ما بشكلٍ مختلفٍ؟ يمكنك التحدّث عن المشاريع التي عملت عليها خلال فترات التدريب أو خلال توظيفك في خبرة عملية أو خلال العمل في مقرر جامعي اعتمادًا على خبرتك، أيّ تجربة مررت بها لها قيمتها فلا تستخف بالمعرفة التي اكتسبتها من خلالها، عند الحديث عن تلك المشاريع السابقة ستكون الإجابة رائعة عندما تتضمن وصف وأهداف المشروع وأصحاب المصلحة فيه وتأثيره أو إجابة على الأسئلة "ماذا"، "مَن"، "كيف" و"لماذا". سيساعدك هذا في إثبات أنَّ لديك فهمًا جيدًا للعملية بأكملها وليس فقط للجانب التقني للأمور. ومن الجيّد أن تُدرج مقاييس النجاح إذا كانت ذات صلة، مثلًا: "هذا ساعد الناس على القيام بالمهمة x بشكلٍ أسهل"، "قمت بأتمتة هذه العملية مما وفر مقدارًا من الوقت للفريق" وغير ذلك. هذا يُظهر مدى اهتمامك بتأثير عملك. حتى لو كان هناك شيئًا يبدو عديم الأهمية بالنسبة لك، تذكر أن الشخص الذي يقابلك لا يعرف عنك شيئًا، إنّهم يبدؤون بصفحة فارغة بالكامل ولديهم فترة زمنية قصيرة ليفهموا كيف تتفاعل مع الأشخاص، ما الذي يثيرك في عملك وكيف تهدف لتحقيق النجاح. اغتنم الفرصة للتفكير في خبراتك وتحديد مجالات النمو للحديث عنها. يدرك المقابِلون أنّ لا أحد مثالي لكنهم يقدرون عقلية النمو. هل حددت بعض العمليات التي يمكن ضبطها؟ ربما كان تواصلك كان يمكن أن يكون أفضل في حالة معينة؟ أسئلة حول الشركة سيسألك المقابِلون سؤالًا اعتياديًّا عن العوامل التي جذبتك إلى الشركة وإلى المنصب الذي تتقدم إليه، من الضروري القيام ببعض الأبحاث حول الشركة مسبقًا وتحضير إجابات للأسئلة مثل: ما الذي يثير اهتمامك في صناعتهم؟ ما الذي جعلك مهتمًا بالشركة؟ ما الذي تتوقع تحقيقه من خلال العمل هناك؟ كيف تتناسب معرفتك مع ما يقومون به؟ ما الذي جعلك تعتقد أنك ستكون مناسبًا لثقافتهم؟ المعرفة المسبقة حول المنتجات التي يبنونها يمكن أن تأخذك شوطًا طويلًا، يتضمن ذلك فهم فضاء المنتج والاطّلاع قليلًا على مجموعة التقنيات. إذ أستفيد عادةً من الاستعدادات للمقابلة كفرصة لتعلّم شيء جديد وفهم ما إذا كنت مهتمًا بالمساهمة في ذلك. بعدها أكون قادرًا على طرح المزيد من الأسئلة ذات الصلة في المقابلة، بالإضافة إلى ذلك أجد أنّه من الأسهل بكثير أن أكون ناجحًا في مجال يثير اهتمامي. لا تقلق بشأن عدم معرفة جميع التقنيات التي يستخدموها. الانفتاح حول ما لا تعرفه (حتى الآن) يبدو جيدًا لمعظم المقابِلين. إظهار الاهتمام بتعلّم مجالهم حتى قبل توفير الوظيفة يبدو أفضل. بالنسبة للأسئلة الثقافية، يتوقع المقابِل أن يرى كيف تتفاعل مع الأشخاص الآخرين. إذا كانت لديك تجربة تدريب، فقد يسألونك عن كيفية تعاونك مع زملائك. مثلًا الوصول إلى المساعدة عندما تحتاجها، قضاء بعض الوقت في مساعدة الآخرين، التغلب على التحديات وتلقي أو تقديم التغذية الراجعة هي بعض مجالات المواضيع المحتملة. أسئلة المقابلة العامة بالنسبة للجانب التقني للأشياء فإنَّ إدراك مبادئ تطوير البرمجيات مثل البرمجة كائنية التوجه والتطوير الموجه بالاختبار والتركيب المتواصل وأنماط التصميم والتحكم بالنسخة هو أمرٌ أساسيٌّ. قد تكون محتاج أيضًا لتعلّم أساسيّات الشبكات أو قواعد البيانات أو الأنظمة حسب طبيعة المنصب الذي تتقدم له. اقرأ وصف الوظيفة بدقة، وحضّر نفسك لتكون قادرًا على إعطاء التعريفات والتحدّث حول المزايا الأساسية. قد لا تملك خبرة عمليّة، ولكن من الجيد أن تكون قادرًا على شرح لماذا هذه التقنيات مهمة ومناقشة بعض اتجاهات الصناعة. ليس عليك أن تعرف كل المتطلبات الموجودة في وصف العمل. أخبرني مرشدي الأول أنّني يجب أن أتقدّم لمنصب ما بالاعتماد على ما الذي أريد أن أكون عليه، بدلًا من الذي أنا حاليًا عليه. إذا كان لديك القليل من وقت الفراغ، فإن محاولة بناء بعض المشاريع الجانبية الصغيرة لتحسين معرفتك بتقنية جيدة هي فكرة رائعة أيضًا. حل المشاكل يأتي هنا الجزء الذي يقلق معظم الأشخاص بسببه. جلسات "حل المشاكل"، التي تختبر فهمك لبنى المعطيات والخوارزميات والتعقيد. خلال مرحلة التحضير هناك مواد غير محدودة على الإنترنت تساعدك على التحضير. الكتاب التقليدي الذي يعطّل العملية ويوفر المشاكل والحلول هو كتاب Cracking the Coding Interview لـ Gayle Laakmann McDowell. المصادر على الإنترنت مثل LeetCode وHackerRank هي بدائل جيّدة. يجب عليك معرفة ما هي الأعمال الأفضل لك بينما أنت تدرس. شخصيًّا أفضّل حل المشاكل والتحديات الصغيرة، مثل code katas أو advent of code. ومع ذلك يمكن أن يكون مضيعة للوقت. لذا عندما أريد أن أتعلّم أسرع، أجد الحلول على الإنترنت وأطبّقها بنفسي لفهم الخوارزمية. هناك الكثير من مقاطع الفيديو على اليوتيوب تشرح كيفية عمل خوارزمية معينة يمكنك أن تستخدمها. عندما تطبّق حلّ ما، خذ بعض الوقت لتقوم بدفعه (push) إلى حسابك على Github. لن يكون مفيدًا فقط إذا احتجت إليه مرة أخرى لاحقًا، لكن سيساعدك أيضًا في بناء معرض أعمالك. خلال المقابلة نفسها تأكد من أنّك فهمت المشكلة بشكلٍ كامل. اطلب من الشخص الذي يقابلك توضيح الأسئلة للتأكد من أنّك تعرف المطلوب منك. لا تنتقل إلى كتابة الشيفرة بدون أن يكون لديك معلومات كافية. مثلًا، يجب أن تتأكد من أنّك تعرف من أين تقرأ الدخل، ما هو تنسيق الدخل وحجم البيانات. ابدأ مع حل "القوة العنيفة" (brute force) ولا تفكر في الأداء بعد. اتركه بسيطًا. اشرح للمقابِل طريقة تفكيرك واذكر أنّك ستجرب حل القوة العنيفة أولًا. لا تقلل من صعوبة المهمة. فحتى الاختبارات التي تبدو بسيطة مثل اختبار FizzBuzz) يمكن أن تعطي انطباعًا سريعًا عن كيفية تحليل المشكلة وفهمها. يبدأ عادةً الاختبار يبدأ بسيطًا ويتقدم تدريجيًا. اذكر أيّة افتراضات تقوم بها. إذا كنت تستخدم توابع مكتبة للغةٍ ما من اختيارك، اسأل إذا كان يجب عليك تنفيذها. مثلًا الترتيب متوفر في معظم لغات البرمجة. بينما تتابع حل المشكلة، اشرح كيف تنقل بياناتك وأين تخزّنها وكيف تعالجها. يوضح هذا أنّك تفهم جيّدًا البيانات التي تتعامل معها ولا تحاول فقط تجربة الأشياء التي رأيتها حولك. يمكنك تقسيم مشكلتك إلى دوال أصغر بأسماء واضحة ثم المضي قدمًا والبدء بتنفيذ كلّ منها. غالبًا، يتوقع منك المقابِلون تنفيذ الحل البسيط أولًا، ثم سيسألونك عن التعقيد. حاول تحديد الحلقات المضمنة، والعمليات لحساب التعقيد. أبقِ تعقيد المساحة والزمن في عقلك. ما مقدار الذاكرة التي تحتاجها لتخزين الأشياء مقارنةً بدخلك وكم عدد العمليات التي تحتاج أن تقوم بها لتحصل على النتيجة. ذكّر نفسك بدرجة تعقيد الخوارزميات القياسية مثل خوارزمية الترتيب والبحث وغيرها ثم حاول معرفة أين يمكن تحسين الأشياء. ربما يمكنك استخدام بنية معطيات مختلفة؟ المصفوفات المترابطة (HashMaps) والمجموعات والقوائم المرتبطة هي بُنى نموذجية تستخدم لتحسين التعقيد اعتمادًا على المشكلة. من الجيد أن تكون هذه البنى بين يديك لتستخدمها في المشكلة الصحيحة. إذا كنت تعرف المشكلة والحل الأكثر فعاليةً مسبقًا، تابع العمل ولكن خذ بعض الوقت لشرح سبب كونه الحل الأمثل. إذا أبدى المقابِل أي رأي فكر بها جيّدًا. في معظم الأحيان، يكون المقابل يعرف الحل مسبقًا ويريد أن يساعدك لذا فإنّ اقتراحاته تستحق التفكير. ماذا لو لم تحلّ المشكلة؟ هل يعني هذا أنّك فشلت في المقابلة؟ ليس بالضرورة. المقابِل مهتم أكثر بطريقة تفكيرك. من تجربتي يركز المقابِلون على: ماذا تفعل عندما تكون عالقًا؟ هل أنت قادر على توضيح لماذا أنت عالقٌ، هل سيثقون بك عند إعطائك مهمة ويعرفون أنّك ستطلب المساعدة عندما تحتاجها؟ هل أنت قادر على فهم عمل بنى المعطيات أو هل تستخدمهم كمربع أسود؟ مثلًا استخدام المصفوفات المترابطة بدون سبب معيّن لذلك أو بدون توضيح لماذا يحسّن استخدامها من الأداء؟ هل أنت تتواصل جيّدًا بشكلٍ عامٍ؟ هل تتقبّل التغذية الراجعة أو تتحمس لحلك فقط؟ هل أحرزت أيّ تقدّم خلال المقابلة؟ هل يمكنك أن تعمل تحت الضغط أو تتوقف نهائيًا؟ أسئلة حول المقابِلين من الجيّد أن تحضّر بعض الأسئلة للمقابِلين أيضًا. تذكّر أنّه من المهم معرفة ما إذا كانت الشركة مناسبة لك أم لا. كلّما ازدادت المعلومات التي تعرفها، كان من الأسهل عليك اتخاذ القرار. عادةً أحضّر بعض الأسئلة للمقابلة مسبقًا، مثل: كيف تُنظَّم فرق الهندسة؟ كيف توزَّع المهام على الفريق؟ كيف يمكنك اختبار منتجاتك؟ ما الفريق الذي يحتمل أن أعمل معه؟ كيف تدعمون الأشخاص أثناء التدريب؟ كم يستغرق عادةً الشخص الجديد للتدريب؟ ما الذي يمكن أن يكون مثالًا لمهمة أعمل عليها؟ ما هي التقنيات الرئيسية التي تستخدمونها لهذا المشروع؟ ما الفرص المتاحة للعمل على مشاريع مختلفة؟ كيف يمكنك إجراء مراجعات الأداء؟ كيف يبدو التقدم الوظيفي لهذا المنصب تحديدًا؟ فكّر في يوم عملك المثالي - ما الذي تريد أن تعرفه عنه؟ يقيّم الأشخاص المختلفون أشياء مختلفة في العمل. مثلًا، قد تفضّل بيئة متغيرة ديناميكية توفر مزيدًا من الحرية، أو قد تشعر بالإنتاجية في بيئة أكثر تنظيمًا. قد ترغب في العمل بطريقةٍ استقلاليةٍ أو تفضّل أن تكون جزءًا من فريق. إنّها فكرةٌ جيّدة أن تعرف البيئة التي تساعدك على أداء الأفضل وتبحث عن الشركات التي تمنحك هذه الفرصة. إذا شعرت بأن المقابِل قد غطّى معظم أسئلتك فلست مضطرًا لأن تسأل لمجرد النقاش فقط. طريقة التفكير مهمة بالطبع، تعد مهاراتك التقنية مهمة جدًا لمقابلات هندسة البرمجيات، ولكن لا تقلل من أهمية مهاراتك الشخصية. سيعمل معك الأشخاص يوميًا لذا يجب أن يشعروا بأنّك شخص لطيف للعمل معه. هناك جزء كبير من العمل لا يتعلق بكتابتك للشيفرة البرمجية، إنّما بالتعاون مع المبادئ والفرق الأخرى. جمع وفهم المتطلبات، والقدرة على التعبير عن المشكلة بطرق تقنية وطرق غير تقنية، إبلاغ أصحاب المصلحة حول التقدم والمشاكل ليست سوى جزء منها. إذا كنت تعتقد أنّ لديك مصدر قوة في شخصيتك فحاول أن تظهر ذلك خلال العملية. اهدف للتحسين المستمر لا تنسَ أن إجراء المقابلات، مثله مثل أي شيء آخر، هو مهارة تتحسن مع الخبرة. لا تقلق بشأن الفشل، ركّز على ما تعلمته والقيام بأداء أفضل في المرة القادمة. عندما تكتسب بعض الخبرة ستصبح أقل توترًا وبالتالي سيكون من الأسهل أن تنجح. إنّها عملية من التطوير المستمر، يجب أن تتحسن في كل مرة تجري فيها مقابلة، لذا استمر في التحضير وفي تحسين مهارات التواصل لديك ومهاراتك التقنية. البحث الصحيح عن المعلومات هو المفتاح للتحضير الجيّد، ومراجع مثل موقع Quora أو حتى غوغل فقط هي مصادر جيّدة لأسئلة المقابلة الشائعة للشركات. قد تكون كل هذه المعلومات هائلة، لكنك لن تُسأل كل هذه الأسئلة في كل مقابلة. حاولت أن أجمع الأشياء المختلفة التي لاحظتها حتى الآن لأعطيك بعض المجالات لتستكشفها. القليل من التحضير المركّز يقطع شوطًا طويلًا، لكن لا تبالغ فيه. من السهل أن تفكر كثيرًا في مقابلة وتربط نفسك بعقد. حظًا سعيدًا، اذهب إلى هناك وكن رائعًا! هل لديك أيّة خطوات أخرى تستخدمها أثناء التحضير؟ شاركها معنا في التعليقات. ترجمة -وبتصرف- للمقال How to prepare for software engineering interview questions‎ لصاحبته Sofia Tzima
  6. لنستخدم CSS لرسم شخصية بيماكس (Baymax) من فيلم Big Hero 6. سنحرّك في هذا المنشور صورة خلفية، بالإضافة إلى تأثير حركة بتوقيت دقيق، ونقوم بإجراء عرض تجريبي CSS من عنصر HTML واحد. تجربة حية. عنصر واحد سنكون قادرين باستخدام العنصر الزائف على إنشاء أجزاء الوجه بعنصر HTML واحد. <div class="baymax"></div> التصميم لضبط المرحلة سنضيف تدرّجًا دقيقًا للشاشة لجعلها تبدو كرأس أبيض منحنٍ. سنستخدم لهذا تدرجًا شعاعيًا على عنصر body. body { background: radial-gradient(circle at center, #fff, #fff 50%, #aaa); background-size: 100% 100%; background-repeat: no-repeat; height: 100vh; } بعد ذلك نضع الوجه في مركز الصفحة. الفم خط أسود بسيط، وسنقوم بذلك باستخدام الخاصية border. .baymax { border-bottom: 1.5em solid #000; position: absolute; top: 50%; left: 50%; width: 50%; transform: translate(-50%, -40%); } تقوم الخاصية الأولى بتعيين إطار سفلي أسود بعرض 1.5em. ثمَّ يتموضع الخط باستخدام الموضع المطلق، فيندفع إلى الأسفل 50% ويتحرك من اليسار 50%. قيم الـ 50% هي بالنسبة إلى حجم العنصر الحاوي (body). المشكلة التي نواجهها الآن هي أنَّ العنصر يبدأ من منتصف طول وعرض الشاشة. إنَّه خارج المركز. لموازنة ذلك، نستخدم تحويلًا لسحب العنصر 50% يسارًا من عرضه، وإلى الأعلى 40% من عرضه. عند هذه المرحلة يصبح الفم في المركز على هذا النحو: إضافة العينين سنستخدم العناصر الزائفة before وafter لإضافة العينين إلى الوجه. لن يحتاج هذا إلى أي عنصر HTML إضافي وتتم معالجته بالكامل باستخدام CSS. .baymax::before { background: #000; border-radius: 50%; content: ""; position: absolute; width: 12em; height: 12em; left: -9em; top: -6em; transform: skewX(-4deg); } .baymax::after { background: #000; border-radius: 50%; content: ""; position: absolute; width: 12em; height: 12em; right: -9em; top: -6em; transform: skewX(4deg); } كل من هذه العناصر الزائفة لها خلفية سوداء، وحواف دائرية -border-radius: 50%; لجعلها دائرية. كلّ منها متموضع في نهاية الفم، وأخيرًا لها تحوّل skew لجعلها تبدو كأنَّها مائلة قليلًا للخلف. ستظهر النتيجة كما يلي: البطارية منخفضة هناك مشهدٌ مضحك في الفيلم حيث تنخفض بطارية بيماكس. فيترنَّح وتتدلَّى جفونه. يمكننا استخدام مزيج من الحركات والتدرّجات في الخلفية لإحداث التأثير. نريد أولًا أن نعطي الخلفية لونين. أسود لجزء العين المفتوح، وأبيض للجفن. يحتاج الجزء الأبيض أن يتموضع خارج العينين في البداية، ثمَّ سنحركه لجعل الجفون تتدلَّى. .baymax::before { background: linear-gradient(to bottom, #efefef, #efefef 50%, #000 50%, #000); background-position: 0 -100%; background-size: 200% 200%; ... } .baymax::after { background: linear-gradient(to bottom, #efefef, #efefef 50%, #000 50%, #000); background-position: 0 -100%; background-size: 200% 200%; ... } نحن الآن نضيف تدرّجًا خطيًّا في الخلفية، فنجعله ضعف ارتفاع الحاوية، ثم نحدد موضعه لذا يصبح النصف العلوي خارج الحاوية. مع وجود تدرّجات للخلفية في مكانين، يمكننا أن نضيف حركة keyframes للتحكم في حركة الجفون. @keyframes blink { 0%, 50% { background-position: 0 100%; } 85%, 95% { background-position: 0 75%; } 100% { background-position: 0 100%; } } قاعدة keyframes في الحركات هي طريقة لوصف سلسلة من الإطارات، باستخدام النسبة المئوية. تعود النسبة المئوية لمدى استمرار الحركة، لذا فإنَّ النسبة المئوية 50% منتصف الطريق عبر الحركة. بهذه الطريقة يمكننا ضبط الخلفية للبقاء مستيقظين حتى منتصف الحركة، ثمَّ بين الإطار 50% والإطار85%، تتحرك للأسفل، وثمَّ تستقر في نهاية الحركة. الخطوة التالية هي إخبار العناصر الزائفة أن تستخدم إطارات الحركة هذه. أضف خاصية animation للأنماط الحالية. .baymax::before { animation: blink 6s infinite; ... } .baymax::after { animation: blink 6s 0.1s infinite; ... } أخبرنا المتصفح هنا أن يستخدم حركة blink على كل عنصر. وضبطنا مدة الحركة إلى 6 ثواني وستستمر بشكلٍ لا نهائي. خاصية واحدة إضافية في المثال الثاني: تخبر المدة 0.1s بعد 6s المتصفح أن تتأخر الحركة لـ0.1 ثانية. يخلق هذا تأثير إغلاق العين الثانية بتأخيرٍ عن الأولى. وذلك يضيف التأثير المتدلي والمتعب ويجعلها تبدو أكثر واقعيةً. يجب أن تظهر النتيجة النهائية كما في الصورة: يمكنك مشاهدة تجربة حية. المتصفحات حذفت في أمثلة الشفرة أيّ بادئات معتادة webkit- وmoz-. لكن خاصيّات transform وanimation يجب أن تكون مسبوقة، وأنصحك باستخدام شيء ما مثل Autoprefixer لهذا. نسخة gif قابلة للمشاركة إليك نسخة gif متحركة يمكنك مشاركتها على الإنترنت بسهولة: ترجمة -وبتصرف- للمقال Baymax in CSS
  7. كلنا نعلم أنّ الاستعانة بفريق خارجي لتطوير البرمجيات هو كارثة على وشك الحدوث. أولًا، تجد شركة تعدك بكل ما ترغب به للمنتج من تسليم في الوقت المحدد، وكلفة ضمن الميزانية، وجودة عالية، وواجهة مستخدم جميلة، وتقنيات متطورة، ودعم طويل الأمد خالٍ من المتاعب، لذا ترسل الدفعة الأولى وتبدأ رحلتك. بالكاد يفهم الفريق احتياجاتك، والجودة سيئة، وتُنتهك كل توقعاتك حول الزمن والميزانية بشدّة، ويرتفع مستوى الإحباط. والجزء "الأفضل" هو أنّه لا يمكنك الابتعاد وإلا ستخسر كل الأموال التي أنفقتها وستضطر أن تبدأ من الصفر. يجب أن تبقى "متزوجًا" من هذا الفريق لأنّه لا يمكنك تحمّل "الطلاق". هل هناك طريقة للاستعانة بفريق خارجي لتطوير البرمجيات بشكلٍ صحيح؟ نعم، من الممكن القيام بذلك بشكلٍ صحيح وخالٍ من المتاعب، ولكن يجب أن تكون مستعدًا لتغيّر فلسفة إدارتك. المبدأ الأساسي هنا هو: يجب عليك أن تناقش مخاوفك مع الفريق الخارجي الذي تستعين به بشكلٍ علني ومتكرر، و يجب أن يناقشوا معك المشاكل والمخاطر بشكلٍ علني ومتكرر. هذان عاملان أساسيان للنجاح في الاستعانة بفريق خارجي لتطوير البرمجيات يتم إهمالهما في كثير من الأحيان. لقد تعلّمت هذا المبدأ من Wei Liao Zi. قال في كتاب كلاسيكيات الإستراتيجية العسكرية للصين القديمة الصفحة 239: دعني أوضّح بعض الأمثلة العملية لكوارث الاستعانة بفريق خارجي لتطوير البرمجيات وشرح كيف يمكن تجنبها باتّباع مبدأ عمره 2500 سنة. يستغرق الأمر إلى الأبد وأنا تجاوزت الميزانية دائمًا يكون المنتج جاهزًا بنسبة 95%، ولديك دائمًا شيء ما غير منفَّذ أو معطوب. لقد أنجزوا الكثير من العمل، ودفعت الكثير من المال، ولكن المنتج الجاهز للسوق لم يصل بعد. يستغرق أسبوعًا بعد أسبوع وشهرًا بعد شهر؛ دائمًا هناك أعمال متأخرة، وأنت لا يمكنك إنهاء ذلك ببساطة. لقد بدأت برؤية هذا المشروع في كوابيسك، والفريق لم يعد يساعد بعد ذلك. كيف يبدو هذا؟ مألوفًا؟ أرجو أن تدرك أنّه بغض النظر عن نوع العقد الذي وقّعت عليه مع الفريق الخارجي لتطوير البرمجيات، وعدد الجداول الزمنية التي حددتها، وعدد الوعود التي قطعت لك، يريدون الاحتفاظ بك كعميل إلى الأبد. بالطبع طالما لديك شيء في حسابك المصرفي. تريد أن ينجح عملك ويزدهر، صحيح؟ إنّهم يريدون نفس ذلك لعملهم. نجاحك يعني منتجًا تم إنهاؤه وإطلاقه للمستخدمين النهائيين. نجاحهم يعني عمليةً لا نهائية من كتابة البرامج لك. هناك القليل جدًا من القواسم المشتركة بين هذين الهدفين. أودّ حتى أن أقول أنّهم متناقضين مع بعضهم بعضًا - عندما تنجح، يفشلون. سيخبرونك بالطبع أّنهم يريدون إنهاء هذا المنتج لك والحصول على عقود جديدة في المستقبل. سيقولون أنّ الدافع الأساسي هو جعلك سعيدًا والحصول على توصية جيّدة. سيؤكدون لك أنّ رضا العميل أهم من المال، لكن أقترح عليك أن تكون قويّا بما يكفي لتواجه الواقع، كل هذه أكاذيب. أغلب المشاريع المستعان بها بفريق خارجي لتطوير البرمجيات تفشل. الغالبية العظمى (انظر تقرير شاوس الأخير). يدرك مطورو البرمجيات هذا أفضل منك، لأنّهم في معظم الأحيان يشاهدون كيف يحصل هذا كل يوم. ومشروعك ليس استثناءً. لذلك، دعنا ننسى هذه الوعود الجميلة ونركز على الواقع القبيح - اعتمد على نفسك. بأخذ المبدأ الذي ذكرته في الأعلى بالحسبان، إليك نصيحتي: تأكّد من أنّ الفريق يتفهّم وقتك الفعلي، وميزانيتك، ومجالك، وقيود مجالك عواقب انتهاكها. يتعلّق هذا بالجزء الأول من المبدأ - يجب عليك أن تناقش مخاوفك بشكلٍ علني ومتكرر -. الذي يحدث عادةً أنّ فريق الخارجي المستعان به يبقى غير مدركًا لوضع العمل الحقيقي ويسمع فقط عبارة "أحتاج إلى هذا في أسرع وقت ممكن" مرّةً كل يومين. "أسرع وقت ممكن" هو ليس موعدًا نهائيًا. علاوةً على ذلك، إنّه فقط بديل مثبّط للغاية لمرحلة رئيسية حقيقية. عندما لا يعرف الفريق متى تحتاج المنتج بالضبط، ما الذي يجب أن يكون جاهزًا بحلول ذلك التاريخ، ولماذا، فيبدأ العمل ضدك. التركيز هنا على "لماذا". بالنسبة لمعظم أصحاب الأعمال، من الصعب الإجابة على هذا السؤال. لماذا تحتاج أن يكون المنتج جاهزًا بحلول الأول من حزيران؟ لأنّك سئمت من الانتظار فقط؟ هذه ليست إجابة معقولة. أنت سئمت من ذلك ولكن لا يزال لديك أموالًا في حسابك المصرفي. سيستمرون في إصدار الفواتير لك، ولن يحترمونك. لن يعاملوك كرجل أعمال قوي وموجّه نحو تحقيق الأهداف. إمّا أنّك لست ذكيًا بما يكفي لتحديد القيود الزمنية أو أنّك تخفيها عن الفريق. في كلتا الحالتين لن يقدّروا هذا السلوك. فيما يلي تحديد قيود الوقت والتكاليف بشكلٍ صحيح: يجب أن تكون الميزات A و B و D جاهزة قبل الأول من حزيران، لأنّ حملتنا التسويقية تبدأ في الخامس من حزيران. إذا لم يكونوا جاهزين، سأخسر 25000$ من تكاليف التسويق. إذا حدث هذا سأضطر إلى تخفيض ميزانية التطوير الشهرية إلى النصف. عندما تسمع شركة الاستعانة بفريق خارجي لتطوير البرمجيات، شريكتك، هذا التعريف للموعد النهائي، تصبح شريكًا حقيقيًا لك الآن. الآن أهدافها تتماشى مع أهدافك. إذا تمّ تأخير المرحلة الرئيسية، ستعاني وسيفهمون لماذا بالضبط. إلى جانب ذلك، يرون كيف ستُنقل معاناتك إليهم أيضًا. توقف عن مطالبتهم بإنهاء كل شيء بأسرع وقت ممكن. توقف عن الاتصال بهم مرتين في اليوم والصراخ لساعة بسبب أدائهم الضعيف. توقف عن استخدام اللغة في رسائل البريد الإلكتروني للعمل. توقف عن القيام بكل هذا الضجيج. إنّه لا يساعدك على أيّ حال. علاوةً على ذلك، لا يؤدي هذا إلا إلى جعل الوضع يزداد سوءًا، لأنّك تفقد احترامك وقد بدأوا في معاملتك كبقرةٍ حلوب - بل بالأحرى شخص غبي وعاطفي. بدلًا من ذلك، قم بأداء واجبك وحدد مراحلك الرئيسية الواقعية. فكّر بوقتك الحقيقي، ومجالك، وحدود ميزانيتك. دوّنهم بجمل قصيرة ومختصرة جدًا. تأكّد من أنّ قيودك واقعية ووصفها يجيب على السؤال الرئيسي - لماذا -. لماذا تحتاج هذا بحلول الأول من حزيران؟ لماذا تريد أن تنفق أقل من 50000$؟ لماذا تحتاج أن تكون كل الميزات الخمسة موجودة في النسخة 1.0؟ لماذا تريد لتطبيق الويب الخاص بك أن يكون جاهزًا لمعالجة 1000 جلسة متزامنة؟ لماذا تحتاج إلى تطبيق للهاتف في الإصدار الأول؟ أجب نفسك وتأكّد من أنّ أجوبتك مفهومة من قبل شركة الاستعانة بفريق خارجي. لا تخفي هذه المعلومات. المنتج سيء جدًا تريد أن يبدو تطبيق الويب الخاص بك مثل Pinterest، يتفاعل سريعًا، ويكون سهل الاستخدام، ويجعلك فخورًا عندما تعرضه على أصدقاءك. لكن المنتج الذي بنوه لك سيء، وبطيء، ولأكون صريحًا، قبيح. تطلب منهم أن يفعلوا شيئًا ما بشأنه، ويستمروا في إعطائك الوعود. يستمر المشروع في استهلاك أموالك وتنمو ميزانيته، لكن المظهر والإحساس لا يتحسّن. إنّه بعيد جدًا عن Pinterest. ينمو الإحباط ولا تشاهد أي طريقة معقولة للخروج من هذا. النصيحة الوحيدة التي تتلقاها من أصدقائك هي أن تعيد تنفيذ كل المشروع من الصفر مع فريق تطوير ويب جديد. كيف يبدو هذا؟ أراهن أنّه مألوفًا. أعتقد أنّ السبب الرئيسي للطريق المسدود في هذا الموقف هو الخوف من الصراع. تحاول في المراحل الأولى من المشروع أن تفعل كل ما تستطيع للحفاظ على علاقة جيدة مع شركة الاستعانة بفريق خارجي وعدم الإساءة إلى أيّ شخص. لا تريد أن تتحكم في عمل أي شخص لأنّهم قد يعدونها إهانة. لا تريد أن تعبّر عن مخاوفك بشأن الجودة لأنّها قد تثبّط همّة الفريق. أنت فقط تأمل أنّهم سيحسّنون المنتج في المستقبل، لكن عندما يأتي المستقبل، يكون متأخرًا جدًا. مجددًا، مع أخذ المبدأ القديم بعين الاعتبار، أنصحك بأن تقوم بإجراء روتيني من اليوم الأول للمشروع للتحقق من نتائجهم والتعبير عن مخاوفك. في مشاريعنا في Zerocracy نطلب من عملاءنا أن يكونوا موجودين في GitHub، ويراجعوا إصداراتنا بشكلٍ متكرر، ويبلغونا عن أيّة تناقضات موجودة كمشكلات (issues) في GitHub. نشجّع رعاة المشروع أن يكونوا متشائمين وسلبيين بشأن جودتنا منذ بداية المشروع. نحن ندرك أن هذه الطريقة يمكننا بها تقليل خطر "الإحباط المتراكم". حاول أن تفعل الشيء ذاته في مشروعك الذي تمّ به الاستعانة بفريق خارجي. لا تخف من الإساءة إليهم. النقد التكراري والتدريجي منهجية أكثر صحةً من السلام الخالي من ردود الفعل والذي ينتهي بالحرب. ابحث عن طريقة ليبقى الفريق المستعان على درايةٍ برأيك حول نتائجه بشكلٍ منتظم. لا تحاول أن تكون لطيفًا لتحافظ على مشروعك.أنت تقدّم لنفسك معروفًا سيئًا. بدلًا من ذلك، كن منفتحًا بشأن مخاوفك. تذكر الجزء الأول من المبدأ أعلاه - يجب عليك أن تناقش مخاوفك بشكلٍ علني ومتكرر. هذه هي الطريقة التي سيستقر بها المشروع وتقلّ بها المخاطر. أيضًا، هناك ممارسة جيّدة جدًا، من وقت لآخر، وهي دعوة المراجعين التقنيين ليعطوا آراءً مستقلة حول المنتج قيد التطوير. اقرأ مشاركتي الأخرى حول هذا الموضوع: هل تحتاج لمراجعات تقنية مستقلة؟. لا يمكنني الاعتماد على وعودهم تتصل بهم، وتضعون الخطط، وتوضّحون المراحل الرئيسية، وتعرّفون الميزات، وتحددون الأولويات، وتتفقون على الجودة، ثمّ تنهي الاتصال. خلال أيّام قليلة، تدرك أنّه كان مضيعة للوقت. إنّهم لا يحافظون على وعودهم لأنّ هناك دائمًا شيء ما جديد يحدث. شخصٌ ما مريض، خادمٌ معطل، بعض أجزاء البرنامج غير صالحة للعمل، بعض الشيفرة لم تعد تعمل، وما إلى ذلك. تتصل مجددًا، تعبّر عن إحباطك، وتوجّه اتهامات قوية، وتعيد هيكلة المراحل، وتعيد تعريف الميزات، وتعيد تحديد الأولويات، وخلال عدّة أيّام تبدأ من جديد. كنت هناك، وقمت بذلك؟ هل يبدو هذا مألوفًا؟ من خلال تجربتي، فإنّ سبب عدم القدرة على التنبؤ وعدم موثوقية الفريق الخارجي في معظم الحالات هو راعي المشروع. يحدث هذا عندما لا تستمع إليهم أو يخشون أن يقولوا لك الحقيقة، والذي هو عادةً نفس الشيء. يطلق البعض على هذا "التطوير المُقاد بالخوف". يخاف الفريق منك، ولكي يحتفظ الفريق بك كعميل يدفع له المال، يضطر أن يكذب عليك. في الأساس، يخبرونك بما تريد أن تسمعه - أنّ نهاية المشروع قريبة، والأخطاء الموجودة حاليًا سهلة الإصلاح، وأنّ مشاكل الأداء بسيطة، وأنّ جودة المعمارية رائعة، وأنّ الفريق متحمس جدًا للعمل معك. عندما تسمع أي مما سبق، اسأل نفسك - هل تشجعهم على قول الحقيقة؟ هل تكافؤهم على إعطائك أخبارًا سيئة لكن صادقة؟ بالإشارة إلى المبدأ الأساسي المذكور أعلاه، مجددًا، أنصحك بالتأكد من أنّ منطقك في المكافآت والعقوبات شفاف لشريكك، الفريق الخارجي، ويستند إلى أهداف المشروع، وفقًا للمبدأ الأساسي وليس لمشاعرك الشخصية. في إحدى منشوراتي السابقة، كتبت أنّ العميل السعيد هو هدف خاطئ لفريق تطوير البرمجيات. العميل الذي يدعم هذا الهدف هو عميل رهيب محكوم عليه بفشل المشروع. إذا كافأت فريقك عندما يُشعرك بالسعادة بالأخبار الجيّدة، فأنت تدرّبهم على الكذب عليك. إذا كنت تتوقع منهم أخبارًا جيّدة، فأنت لا تشجعهم على إخبارك بالحقيقة وعلى القيام بما هو جيّد للمشروع، ليس لك شخصيًّا. أنت لا تشجعهم على الجدال معك. بكلماتٍ أخرى، أنت تخنق قناة المعلومات التي من المفترض أن تأتي إليك من الأشخاص الذين يعملون معك. أنت تعزل نفسك، والفريق بدأ بالعمل ضدك، ليس معك. إليك نصيحةً عمليّةً. أولًا، أعلن عن أهدافك وقيودك المعقولة بانتظام، كما شرحت أعلاه. تأكّد من فهم الفريق لخطط عملك و"لماذا" الأسباب الكامنة وراءها. ثانيًا، اسأل أعضاء الفريق بانتظام عن المخاطر والمشاكل. اسألهم لماذا يعتقدون أن أهداف المشروع قد تتعرض للخطر. ومن الأفضل أن تدعهم يوثّقوا المخاطر بانتظام ويبلغوك بها. كافئهم على صدقهم في قائمة المخاطر هذه. جرّب ذلك وستُفاجأ بعدد الأشياء المثيرة للاهتمام التي ستتضمنها قائمة المخاطر. ترجمة -وبتصرف- للمقال How to Avoid a Software Outsourcing Disaster لصاحبه Yegor Bugayenko
  8. ASP.NET Core هي إطار عمل مفتوح المصدر وعالي الأداء لإنشاء تطبيقات ويب حديثة، ومن المفترض أن يكون النسخة الأفضل من إطار العمل ASP.NET لمايكروسوفت. تمَّ إصداره عام 2016، ويمكن تشغيله على العديد من أنظمة التشغيل مثل لينكس وmacOS، مما يتيح للمطورين استهداف نظام تشغيل معين للتطوير اعتمادًا على متطلبات التصميم. يستطيع المطور باستخدام ASP.NET Core بناء أي نوع من تطبيقات الويب أو الخدمات بغض النظر عن التعقيد والحجم. يمكن للمطورين أيضًا استخدام صفحات Razor لإنشاء تصميم يركز على الصفحة يعمل أعلى النمط التقليدي MVC (اختصارًا للعبارة Model-View-Controller). يوفر ASP.NET Core المرونة للتكامل مع أي إطار عمل للواجهة الأمامية للتعامل مع العمليات المنطقية من طرف العميل أو استخدام خدمة ويب. مثلًا، بإمكانك بناء واجهة برمجية RESTful مع ASP.NET Core واستخدامها ببساطة مع أطر عمل جافاسكربت مثل Angular، React و Vue.js. ستعدّ في هذا الدرس تطبيق ASP.NET Core وتنشره ليكون جاهزًا للإطلاق مع خادم MySQL على أوبنتو 18.04 باستخدام Nginx. ستنشر تطبيق ASP.NET Core تجريبي مماثل للتطبيق من توثيق مايكروسوفت والمُستضاف على GitHub. بمجرد أن يتم النشر، سيسمح لك التطبيق التجريبي بإنشاء قائمة أفلام وتخزينها في قاعدة البيانات. ستكون قادرًا على إنشاء، وقراءة، وتحديث، وحذف سجلات من قاعدة البيانات. يمكنك بدلًا من ذلك استخدام هذا الدرس لتنشر تطبيق ASP.NET Core خاص بك؛ من الممكن أن تحتاج إلى تنفيذ خطواتٍ إضافيةٍ تتضمن إنشاء ملف تهجير (migration) جديد إلى قاعدة البيانات الخاصة بك. المتطلبات الأساسية ستحتاج لما يلي لاتباع هذا الدرس: خادم أوبنتو 18.04 تم ضبطه باستخدام دليل التهيئة الأولية لخادم أوبنتو 18.04، متضمنًا مستخدمًا عاديًا مع وصول sudo وجدار حماية. خادم Nginx مثبَّت باتباع الخطوات كيفية تثبيت Nginx على أوبنتو 18.04. خادم ويب Nginx آمن. بإمكانك اتباع درس كيف تؤمّن خادم Nginx بشهادة Let's Encrypt في أوبنتو للقيام بذلك. ضبط سجلَّي الـ DNS لخادمك. يمكنك اتّباع هذه المقدمة إلى DigitalOcean DNS للتفاصيل حول كيفية إضافتها. سجل A مع إشارة اسم النطاق الخاص بك your-domain إلى عنوان IP العام للخادم. سجل A مع إشارة اسم النطاق الخاص بك مسبوقًا بـ www www.your-domain. إلى عنوان IP العام للخادم. نظام إدارة قواعد البيانات MySQL مثبَّت باتباع خطوات كيف تثبّت الإصدار الأخير من MySQL على أوبنتو 18.0. الخطوة 1: تثبيت مُشِّغل NET Core. الآني مُشِّغل NET Core. الآني (أي NET Core runtime.) مطلوب لتشغيل تطبيق NET Core.، لذا يجب البدء بتثبيته على جهازك. تحتاج أولًا لتسجيل مفتاح مايكروسوفت ومستودع المنتج. بعد ذلك ستثبّت الاعتماديات (dependencies) المطلوبة. أولًا، سجّل الدخول كمستخدم مُنشأ حديثًا، وتأكّد من أنَّك في المجلد الجذر: $ cd ~ ثمّ نفّذ الأمر التالي لتسجيل مفتاح مايكروسوفت ومستودع المنتج: $ wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb استخدم dpkg مع الراية i- لتثبيت الملف المحدد: $ sudo dpkg -i packages-microsoft-prod.deb لتسهيل تثبيت الحزم الأخرى الضرورية لتطبيقك، ستثبّت مستودع universe باستخدام الأمر التالي: $ sudo add-apt-repository universe ثمّ ثبّت حزمة apt-transport لتسمح باستخدام المستودعات التي يتم الوصول إليها عبر بروتوكول HTTP الآمن: $ sudo apt install apt-transport-https نفِّذ الأمر التالي لتنزيل قائمة الحزم من المستودعات وتحديثها للحصول على معلومات حول أحدث إصدارات الحزم واعتمادياتها: $ sudo apt update أخيرًا، يمكنك تثبيت SDK لمشغّل NET. الآني عبر الأمر: $ sudo apt install dotnet-sdk-2.2 ستُعرض تفاصيل ما سيجري تنزيله ويُطلب تأكيد ذلك عبر الضغط على Y أي yes للمتابعة. لقد انتهيت الآن من تثبيت SDK لمشغّل NET Core. الآني على الخادم، وأنت جاهز تقريبًا لتنزيل التطبيق التجريبي من Github لضبط إعدادات النشر. لكن أولًا يجب إنشاء قاعدة بيانات التطبيق. الخطوة 2: إنشاء مستخدم MySQL وقاعدة بيانات ستنشئ في هذا الجزء مستخدم على خادم MySQL، وقاعدة بيانات للتطبيق، وتمنح جميع الصلاحيات الضرورية للمستخدم الجديد ليتصل بقاعدة البيانات من تطبيقك. تحتاج لتبدأ إلى الوصول لـMySQL العميل باستخدام حساب MySQL الجذر كما هو موضح هنا: $ mysql -u root -p سيُطلب منك إدخال كلمة سر الحساب الجذر الذي تمّ إعداده ضمن درس المتطلبات الأساسية. ثم أنشئ قاعدة بيانات MySQL للتطبيق باستخدام الأمر: mysql> CREATE DATABASE MovieAppDb; ستشاهد الخرج التالي في سطر الأوامر: Output Query OK, 1 row affected (0.03 sec) لقد أنشأت الآن قاعدة البيانات بنجاح، بعد ذلك ستُنشئ مستخدم MySQL جديد، وتربطه بقاعدة البيانات التي أُنشئت حديثًا، وتمنحه جميع الصلاحيات. نفّذ الأمر التالي لتُنشئ مستخدم MySQL وكلمة مرور. تذكّر أن تغيّر اسم المستخدم وكلمة المرور ليصبحوا أكثر أمانًا: mysql> CREATE USER 'movie-admin'@'localhost' IDENTIFIED BY 'password'; ستشاهد الخرج التالي: Output Query OK, 0 rows affected (0.02 sec) يحتاج مستخدم MySQL إلى الإذن المناسب للوصول إلى قاعدة بيانات أو تنفيذ إجراء محدد عليها، حاليًا لا يملك المستخدم movie-admin الإذن المناسب على قاعدة بيانات التطبيق. سنغيّر ذلك بتنفيذ الأمر التالي لمنح حق الوصول للمستخدم movie-admin إلى قاعدة البيانات MovieAppDb: mysql> GRANT ALL PRIVILEGES ON MovieAppDb.* TO 'movie-admin'@'localhost'; ستشاهد الخرج التالي: Output Query OK, 0 rows affected (0.01 sec) يمكنك الآن إعادة تحميل جداول الصلاحيات بتشغيل الأمر التالي لتطبيق التغييرات التي قمت بها باستخدام عبارة التدفق: mysql> FLUSH PRIVILEGES; ستشاهد الخرج التالي: Output Query OK, 0 rows affected (0.00 sec) لقد أنشأت مستخدم جديد ومنحه الصلاحيات. لتختبر فيما إذا تمّ الأمر بشكلٍ صحيحٍ، أغلق MySQL العميل: mysql> quit; ثمّ سجّل الدخول مجددًا باستخدام بيانات الاعتماد لمستخدم MySQL الذي أنشأته وأدخل كلمة المرور المناسبة عندما تُطلب منك: $ mysql -u movie-admin -p للتأكد من أن المستخدم movie-admin يمكنه الوصول إلى قاعدة البيانات المُنشأة، تحقق باستخدام الأمر: mysql> SHOW DATABASES; الآن ستشاهد جدول MovieAppDb ضمن قائمة الخرج: Output +--------------------+ | Database | +--------------------+ | MovieAppDb | | information_schema | +--------------------+ 2 rows in set (0.01 sec) الآن، أغلق MySQL العميل: mysql> quit; لقد أنشأت قاعدة بيانات، ومستخدم MySQL جديد للتطبيق التجريبي، ومنحت المستخدم الجديد الصلاحيات الصحيحة للوصول إلى قاعدة البيانات. في الجزء التالي، ستبدأ بضبط التطبيق التجريبي. الخطوة 3: ضبط التطبيق التجريبي وبيانات اعتماد قاعدة البيانات كما ذُكر سابقًا، ستنشر تطبيق ASP.NET Core موجود. تم بناء هذا التطبيق لإنشاء قائمة أفلام باستخدام نمط التصميم (Model-View-Controller) لضمان وجود الهيكل المناسب والفصل بين الاهتمامات. لإنشاء أو إضافة فيلم جديد إلى القائمة، سيملأ المستخدم حقول النموذج بالتفاصيل المناسبة ويضغط على زر إنشاء لإرسال التفاصيل إلى المتحكم (controller). عند ذلك يستقبل المتحكم طلب POST HTTP مع التفاصيل المرسلة وتبقى البيانات في قاعدة البيانات من خلال النموذج (model). ستستخدم Git لسحب (pull) الشيفرة المصدرية لهذا التطبيق التجريبي من GitHub وحفظه في مجلد جديد. يمكنك أيضًا تحميل تطبيق بديل إذا كنت ستنشر تطبيقًا مختلفًا. للبدء، أنشئ مجلدًا جديدًا باسم movie-app من الطرفية (terminal) باستخدام الأمر التالي: $ sudo mkdir -p /var/www/movie-app سيكون هذا المجلد بمثابة المجلد الجذر لتطبيقك. بعد ذلك، عدّل مالك ومجموعة المجلد للسماح لحساب المستخدم العادي بالعمل على ملفات المشروع: $ sudo chown sammy:sammy /var/www/movie-app استبدل sammy باسم المستخدم العادي الذي يملك صلاحيات sudo. يمكنك الآن الانتقال إلى المجلد الأب ونسخ (clone) التطبيق من Github: $ cd /var/www $ git clone https://github.com/do-community/movie-app-list.git movie-app ستشاهد الخرج التالي: Output Cloning into 'movie-app'… remote: Enumerating objects: 91, done. remote: Counting objects: 100% (91/91), done. remote: Compressing objects: 100% (73/73), done. remote: Total 91 (delta 13), reused 91 (delta 13), pack-reused 0 Unpacking objects: 100% (91/91), done. لقد نسخت التطبيق التجريبي بنجاح من GitHub، لذا ستكون الخطوة التالية هي إنشاء اتصال ناجح بقاعدة بيانات التطبيق. ستقوم بذلك عن طريق تعديل خاصية ConnectionStrings ضمن ملف appsettings.json وإضافة تفاصيل قاعدة البيانات. غيّر المجلد ضمن التطبيق: $ cd movie-app ثم افتح الملف للتعديل: $ sudo nano appsettings.json أضف بيانات اعتماد قاعدة البيانات في ملف appsettings.json: { "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "MovieContext": "Server=localhost;User Id=movie-admin;Password=password;Database=MovieAppDb" } } بوضع هذه البيانات بطريقةٍ صحيحة فقد أنشأت اتصالًا ناجحًا بقاعدة البيانات. الآن اضغط CTRL+X لحفظ تغييرات الملف واضغط Y للتأكيد. ثم اضغط ENTER لإغلاق الصفحة. تستخدم تطبيقات ASP.NET Core مكتبة NET. قياسية تسمّى Entity Framework (EF) Core لإدارة التفاعل مع قاعدة البيانات. Entity Framework Core هي إصدار خفيف عابر للمنصات (cross-platform) من التقنية الشائعة للوصول إلى بيانات Entity Framework. إنّها تقنية ربط الكائنات بالعلاقات (ORM) التي تتيح لمطوري NET. العمل مع قاعدة البيانات باستخدام أي مزود قاعدة بيانات، مثل MySQL. يمكنك الآن تحديث قاعدة بياناتك مع الجداول من التطبيق التجريبي المنسوخ. نفّذ الأمر التالي لهذا الغرض: $ dotnet ef database update سيحدّث هذا الأمر قاعدة البيانات ويُنشئ المخططات المناسبة. الآن، لتبني المشروع وكلّ اعتمادياته، نفّذ الأمر التالي: $ dotnet build ستشاهد خرجًا مشابهًا للتالي: Output Microsoft (R) Build Engine version 16.1.76+g14b0a930a7 for .NET Core Copyright (C) Microsoft Corporation. All rights reserved. Restore completed in 95.09 ms for /var/www/movie-app/MvcMovie.csproj. MvcMovie -> /var/www/movie-app/bin/Debug/netcoreapp2.2/MvcMovie.dll MvcMovie -> /var/www/movie-app/bin/Debug/netcoreapp2.2/MvcMovie.Views.dll Build succeeded. 0 Warning(s) 0 Error(s) Time Elapsed 00:00:01.91 سيؤدي هذا إلى بناء المشروع وتثبيت أيّة اعتماديات خارجية (third-party dependencies) مذكورة في ملف project.assets.json، ولكن لن يكون التطبيق جاهزًا للنشر بعد. نفّذ الأمر التالي ليصبح التطبيق جاهزًا للنشر: $ dotnet publish ستشاهد التالي: Output Microsoft (R) Build Engine version 16.1.76+g14b0a930a7 for .NET Core Copyright (C) Microsoft Corporation. All rights reserved. Restore completed in 89.62 ms for /var/www/movie-app/MvcMovie.csproj. MvcMovie -> /var/www/movie-app/bin/Debug/netcoreapp2.2/MvcMovie.dll MvcMovie -> /var/www/movie-app/bin/Debug/netcoreapp2.2/MvcMovie.Views.dll MvcMovie -> /var/www/movie-app/bin/Debug/netcoreapp2.2/publish/ هذا سيُحزّم التطبيق ويترجمه، ويقرأ اعتمادياته، وينشر مجموعة الملفات الناتجة في مجلد للنشر، ويُنتج ملف dll. عابر للمنصات يستخدم مشغّل NET Core. الآني المثبّت لتشغيل التطبيق. بتثبيت الاعتماديات، وإنشاء اتصال بقاعدة البيانات، وتحديث قاعدة البيانات بالجداول الضرورية، ونشرها للإنتاج، تكون قد أكملت ضبط التطبيق التجريبي. في الخطوة التالية ستضبط إعدادات خادم الويب لجعل التطبيق متاحًا وآمنًا على نطاقك. الخطوة 4: ضبط إعدادات خادم الويب الآن وبعد اتّباع درس كيف تؤمّن خادم Nginx بشهادة Let's Encrypt ستحصل على كتلة الخادم لنطاقك في ‎/etc/nginx/sites-available/your_domain مع توجيه server_name الذي تمّ تعيينه بالفعل بشكلٍ مناسب. في هذه الخطوة ستعدّل كتلة الخادم هذه لتقوم بضبط Nginx كوكيل (proxy) عكسي لتطبيقك. الوكيل العكسي هو خادم يقع أمام خوادم الويب ويعيد توجيه كل طلب من مستعرض الويب إلى خوادم الويب هذه. إنّه يستقبل كل الطلبات من الشبكة ويعيد توجيهها إلى خادم ويب مختلف. في حالة تطبيق ASP.NET Core، يعدّ خادم الويب Kestrel هو الخادم المفضّل المضمّن افتراضيًا، وهذا ممتاز لتقديم المحتوى الديناميكي من تطبيق ASP.NET Core لكونه يوفر أداءً أفضل لمعالجة الطلبات وقد صُمم لجعل ASP.NET أسرع قدر الإمكان. ومع ذلك لا يعد Kestrel خادم ويب كامل المواصفات لأنه لا يمكنه إدارة الأمان وتقديم الملفات الثابتة، ولهذا يُستحسن دائمًا تشغيله خلف خادم ويب. للبدء، تأكد من أنّك داخل المجلد الجذر للخادم: $ cd ~ افتح كتلة الخادم للتعديل بهذا الأمر: $ sudo nano /etc/nginx/sites-available/your_domain كما هو مفصّل في الخطوة 4 من درس كيف تؤمّن خادم Nginx بشهادة Let's Encrypt، إذا اخترت الخيار 2، فإنَّ برنامج Certbot سيضبط تلقائيًا كتلة الخادم هذه لإعادة توجيه حركة مرور HTTP إلى HTTPS مع بعض التعديلات فقط. تابع الإعدادات بضبط أول كتلتين في الملف لتصل إلى ما يلي في ملف ‎/etc/nginx/sites-available/your-domain: server { server_name your-domain www.your-domain; location / { proxy_pass http://localhost:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } listen [::]:443 ssl ipv6only=on; # managed by Certbot listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/your-domain/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/your-domain/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } … هذه الإعدادات في كتلة الخادم ستطلب من خادم Nginx التنصّت على المنفذ 443، الذي هو المنفذ القياسي للمواقع التي تستخدم SSL. بالإضافة إلى ذلك سيقبل Nginx حركة المرور العامة على المنفذ 443 ويعيد توجيه كل طلب مطابق إلى خادم Kestrel المدمج http://localhost:5000. أخيرًا، بعد كتلة الخادم التي عدلتها في الملف، تأكّد من أنّ كتلة الخادم الثانية في الملف ‎/etc/nginx/sites-available/your-domain تبدو كالتالي: … server { if ($host = www.your-domain) { return 301 https://$host$request_uri; } # managed by Certbot if ($host = your-domain) { return 301 https://$host$request_uri; } # managed by Certbot listen 80; listen [::]:80; server_name your-domain www.your-domain; return 404; # managed by Certbot } ستعيد كتلة الخادم توجيه كل الطلبات إلى https://your-domain وhttps://www.your-domain لوصول HTTPS آمن. بعد ذلك، دع Nginx يعكس التغييرات التي قمت بها على كتلة الخادم بتنفيذ الأمر: $ sudo nginx -s reload بعد إكمال إعداد خادم Nginx، تمّ ضبط الخادم بالكامل لإعادة توجيه جميع طلبات HTTPS الموجهة إلى https://your-domain على تطبيق ASP.NET Core الذي يعمل على الخادم Kestrel إلى http://localhost:5000. ولكن لم يتم ضبط Nginx ليدير إجراء الخادم Kestrel. ستستخدم وظائف systemd لمعالجة هذا والتأكّد من أنّ عملية Kestrel تعمل في الخلفية. ستسمح لك ملفات Systemd بأن تدير إجراءًا من خلال توفير وظائف البدء، والإيقاف، وإعادة التشغيل، والدخول، حالما تنشئ إجراء عمل يدعى وحدة. انتقل ضمن مجلد systemd: $ cd /etc/systemd/systems أنشئ ملفًا جديدًا للتعديل: $ sudo nano movie.service وأضف المحتوى التالي له: [Unit] Description=Movie app [Service] WorkingDirectory=/var/www/movie-app ExecStart=/usr/bin/dotnet /var/www/movie-app/bin/Debug/netcoreapp2.2/publish/MvcMovie.dll Restart=always RestartSec=10 SyslogIdentifier=movie User=sammy Environment=ASPNETCORE_ENVIRONMENT=Production Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false [Install] WantedBy=multi-user.target يحدد ملف الإعداد موقع مجلد المشروع مع WorkingDirectory وأمر التنفيذ في بداية العملية في ExecStart. بالإضافة إلى ذلك، لقد اعتدت على استخدام توجيه RestartSec لوصف متى سيتم إعادة تشغيل خدمة systemd إذا تعطلت خدمة مشغّل NET. الآني. الآن، احفظ الملف وفعّل خدمة movie المنشأة حديثًا مع الأمر: $ sudo systemctl enable movie.service بعد ذلك، تابع لتشغيل الخدمة والتحقق من أنّها تعمل عند التشغيل: $ sudo systemctl start movie.service ثمّ تفحّص حالتها: $ sudo systemctl status movie.service ستشاهد الخرج التالي: Output movie.service - Movie app Loaded: loaded (/etc/systemd/system/movie.service; enabled; vendor preset: enabled) Active: active (running) since Sun 2019-06-23 04:51:28 UTC; 11s ago Main PID: 6038 (dotnet) Tasks: 16 (limit: 1152) CGroup: /system.slice/movie.service └─6038 /usr/bin/dotnet /var/www/movie-app/bin/Debug/netcoreapp2.2/publish/MvcMovie.dll يعطيك هذا الخرج نظرةً عامةً عن الحالة الراهنة لخدمة movie.service المنشأة لتحافظ على التطبيق شغّالًا. تشير إلى أنّ الخدمة شغّالة وفعّالة حاليًا. انتقل إلى https://your-domain من متصفحك لتشغيل التطبيق واختباره. ستشاهد الصفحة الرئيسية للتطبيق التجريبي- تطبيق قائمة الأفلام. مع الخادم الوكيل العكسي وKestrel الذي تتم إدارته باستخدام systemd، تم ضبط تطبيق الويب بالكامل ويمكن الوصول إليه من المتصفح. خاتمة نشرت في هذا الدرس تطبيق ASP.NET Core على خادم أوبنتو. وقمت بتثبيت خادم MySQL واستخدمته لاستمرار البيانات وإدارتها، واستخدمت خادم الويب Nginx كوكيل عكسي لخدمة تطبيقك. بعد هذا الدرس، إذا كنت مهتمًا ببناء تطبيق ويب تفاعلي باستخدام #C بدلًا من جافاسكربت، يمكنك تجربة إطار العمل Blazor لواجهة المستخدم للويب من مايكروسوفت. إنّه واجهة المستخدم للويب تعتمد على المكونات ومقادة بالأحداث لتحقيق المنطق من جهة العميل في تطبيق ASP.NET Core. إذا كنت ترغب بنشر تطبيقك، ستحتاج إلى أخذ الإجراءات الأخرى بالحسبان لنشر تطبيقك. الشيفرة المصدرية الكاملة لهذا التطبيق التجريبي تجدها هنا على GitHub. ترجمة -وبتصرف- للمقال How To Deploy an ASP.NET Core Application with MySQL Server Using Nginx on Ubuntu 18.04 لصاحبه Oluyemi Olususi
  9. تعدّ الصيانة لميزة الأكثر قيمةً لتطوير البرمجيات الحديثة. يمكن تعريف قابلية الصيانة بشكلٍ أساسيّ بأنّها وقت العمل اللازم لمطور جديد حتى يتعلم البرنامج قبل أن يتمكن من البدء في إجراء تغييرات مهمة فيه. كلما طال الوقت، انخفضت الصيانة. يقترب هذا الشرط الزمني في بعض المشاريع من اللانهاية، مما يعني أنه غير قابل للصيانة حرفيًا. أعتقد أن هناك سبعة أخطاء أساسية وقاتلة تجعل برنامجنا غير قابلٍ للإصلاح وإليك هذه الأخطاء. الأنماط المضادة لسوء الحظ، لغات البرمجة التي نستخدمها مرنة جدًا. إنها تسمح بالكثير وتمنع القليل جدًا. مثلًا، لا تعارض لغة جافا وضع التطبيق بالكامل في "صنف" واحد مع عدة آلاف تابع. تقنيًا، سيتم ترجمة التطبيق وتشغيله. ولكن هذا نمط مضاد معروف يدعى God object. وبالتالي، فإن النمط المضاد هو طريقة مقبولة تقنيًا لتصميم الأشياء التي يُتفق عليها عمومًا أنها خاطئة. هناك العديد من الأنماط المضادة (anti-pattern) في كل لغة. وجودها في منتجك يشبه وجود الورم في الكائن الحي. بمجرد أن يبدأ في النمو، يصبح من الصعب جدًا إيقافه. وفي النهاية، يموت الجسم بأكمله. في النهاية، يصبح البرنامج بأكمله غير قابلِ للإصلاح ويجب إعادة كتابته. بمجرد السماح لبعض الأنماط المضادة في المنتج، ستحصل في النهاية على المزيد منها، وسينمو "الورم" فقط. يعدُّ هذا صحيح خاصةً بالنسبة للغات البرمجية كائنية التوجه (Java، و C ++، و Ruby وPython) ويرجع ذلك غالبًا إلى أنها ترث العديد من اللغات الإجرائية (C، Fortran وCOBOL) ولأن مطوري OOP يميلون للتفكير بطريقة إجرائية وضرورية. على كلّ حال، بالإضافة إلى قائمة الأنماط المضادة المعروفة الموجودة، أنا أعدّ أيضًا أنَّ هذه الأشياء القليلة منهجيات تشفير سيئة. اقتراحي العملي الوحيد هنا هو القراءة والتعلّم. ربما تساعدك هذه الكتب أو كتاب "Elegant Objects" في ذلك. حاول دائمًا أن تشك في جودة برنامجك، ولا تكتفي بأن "يعمل فقط". كما هو الحال مع السرطان، كلما تم تشخيصه أبكر، كانت فرصة النجاة أكبر. تغييرات لا يمكن تعقبها عندما أنظر إلى سجل الإيداع (commit) يجب أن أكون قادرًا على توضيح كل تغيير، ما الذي تغير، ومن قام بالتغيير، ولماذا تم إجراء التغيير. إضافةً لذلك، يجب قياس الوقت اللازم للحصول على إجابات هذه الأسئلة الثلاثة بالثواني. ولكن في معظم المشاريع لا يتم تطبيق ذلك. إليكم بعض التوصيات العملية: استخدم الملاحظات دائمًا بغض النظر عن مدى صغر حجم المشروع أو الفريق، حتى لو كنت فيه لوحدك فقط، أنشئ ملاحظات (مشكلات GitHub) لكل مشكلة تحلّها. اشرح المشكلة باختصار في الملاحظة ووثّق ما تفكر به هناك. استخدم الملاحظة كمخزن مؤقت لجميع المعلومات المتعلقة بالمشكلة. انشر كل شيء يمكن أن يكون له أي فائدة مستقبلًا، عندما يحاول شخص ما فهم ما هي "تلك الالتزامات الغريبة القليلة". أضف مراجعًا للملاحظات في الإيداعات لا داعٍ للقول أنّه يجب أن يكون لكل إيداع (commit) رسالة. الالتزامات بدون رسائل تعتبر ممارسة سيئة جداً؛ لن أناقش لماذا. ولكن الرسالة بمفردها لا تعد كافية. يجب أن تبدأ كل رسالة برقم الملاحظة التي تعمل عليها. سيقوم GitHub (أنا متأكد من أنك تستخدمه) بربط الالتزامات والملاحظات تلقائيًا، مما يزيد من إمكانية تتبع التغييرات. لا تحذف أي شيء يسمح لنا Git بالقيام برفع (push) "قسري" يقوم بالكتابة فوق كامل الفرع (branch) الذي كان موجودًا سابقًا على الخادم. هذا مجرد مثال واحد على كيفية إتلاف سجل التطوير. شاهدت عدة مرات أشخاصًا يحذفون تعليقاتهم في مناقشات GitHub لجعل الملاحظات تبدو أكثر "نظافة". هذا خطأ، لا تقم أبدًا بحذف أي شيء. حافظ على سجلك بغض النظر عن مدى السوء (أو الفوضوية) التي قد تبدو لك الآن. الإصدارات المخصصة يجب أن يُحزَّم كل جزء من البرنامج قبل أن يتم تسليمه إلى المستخدم النهائي. إذا كنت تتعامل مع مكتبة جافا، فيجب أن تُحزَّم كملف ‎.jar ويتم تصديرها إلى مستودع ما؛ إذا كان تطبيق ويب، فيجب نشره على المنصة، وما إلى ذلك. بغض النظر عن صغر حجم المنتج أو كِبره، فهناك دائمًا إجراء معياري يختبر ويُحزِّم وينشر. سيكون الحل المثالي هو أتمتة هذا الإجراء بحيث يكون من الممكن تنفيذه من سطر الأوامر باستخدام أمر واحد $ ./release.sh ... DONE (took 98.7s) معظم المشاريع بعيدة عن ذلك، إذ تتضمن عملية إصدارها دائمًا بعض السحر، فيتعين على الشخص المسؤول عن المشروع (المعروف أيضًا باسم DevOp) النقر فوق بعض الأزرار هنا وهناك، وتسجيل الدخول إلى مكان ما، والتحقق من بعض المقاييس، وما إلى ذلك. لا تزال عملية الإصدار المخصص هذه خطأً نموذجيًا في صناعة الهندسة البرمجية بأكملها. يمكنني هنا تقديم نصيحة عملية واحدة فقط: "الأتمتة"، أنا أستخدم rultor.com من أجل ذلك، لكن يمكنك استخدام أي أدوات تريدها. المهم أن يكون الإجراء بأكمله مؤتمتًا بالكامل ويمكن تنفيذه من سطر الأوامر. تحليل ثابت طوعي التحليل الثابت هو ما يجعل شيفرتنا تبدو أفضل. وبجعلها تبدو أفضل، فإننا حتمًا نجعلها تعمل بشكل أفضل. لكن هذا يحدث فقط عندما يضطر الفريق بأكمله إلى اتباع القواعد التي يحددها المحلل (أو المحللون) الثابت (static analyzer). لقد كتبت عن ذلك في التحكم الصارم لجودة شيفرة الجافا. يمكنني استخدام qulice.com في مشاريع Java و rubocop.com في Ruby، وهناك العديد من الأدوات المشابهة لكل لغة تقريبًا. يمكنك استخدام أيّا منها، ولكن اجعله إلزاميًا، في معظم المشاريع التي يستخدم فيها التحليل الثابت، يبني المطورون تقارير جيدة المظهر ويواصلون كتابة الشيفرة بالطريقة التي استخدموها من قبل. لا يتم بمثل هذه المنهجية "التلقائية" تقديم أيّة إضافات للمشروع. وعلاوة على ذلك، فإنّها تخلق وَهمْ الجودة. ما أقوله هو أن التحليل الثابت يجب أن يكون خطوة إلزامية عند الإعداد للنشر. لا يمكن أن تتم عملية بناء المشروع في حال انتهاك أي قاعدة من قواعد التحليل الثابت. تغطية اختبار غير معروف ببساطة، تغطية الاختبار هي درجة اختبار البرنامج من خلال اختبارات الوحدة أو التجميع. كلما زادت التغطية، تم تنفيذ "كمية" أكبر من الشيفرة أثناء إجراء الاختبارات. من الواضح أن التغطية الأعلى أمر جيد. لكن الكثير من مطوري المشاريع لا يعرفون مقدار تغطية مشاريعهم ولا يعتمدون هذا المقياس. قد يكون لديهم بعض الاختبارات، لكن لا أحد يعرف مدى عمق اختبارهم للبرنامج وماهي الأجزاء التي لم يتم اختبارها منه على الإطلاق. هذه الحالة أسوأ بكثير من تغطية الاختبارات المنخفضة التي يتم قياسها وإبلاغ الجميع بها. التغطية العالية (High coverage) لا تضمن الجودة العالية لكن التغطية غير المعروفة (unknown coverage) هي مؤشر واضح لمشاكل الصيانة. عندما يدخل مُطور جديد للمشروع، يجب أن يكون قادرًا على إجراء بعض التغييرات ومعرفة مدى تأثر التغطية بها. من الناحية المثالية، يجب فحص تغطية الاختبار بنفس طريقة التحليل الثابت، ويجب أن يفشل البناء إذا كانت أقل من عتبة محددة مسبقًا (عادة ما تكون حوالي 80 بالمائة). تطوير دون توقف ما أقصده بدون توقف هو أنه بدون مراحل وإصدارات. بغض النظر عن نوع البرنامج الذي تكتبه، يجب عليك إصداره وتعديله بشكل متكرر. مشروع بدون سجل إصدار واضح هو فوضى يتعذر إصلاحها. هذا في الغالب لأن الصيانة تدور حول القدرة على الفهم عند قراءة شيفرتك. عندما أنظر إلى الشيفرة المصدرية وسجل الالتزام والإصدار، يجب أن أكون قادرًا على معرفة ما كان يقصده كاتبها أو كاتبوها، وما الذي كان يفعله المشروع قبل عام، وإلى أين يتوجه الآن، وما هي خريطة التوجه الخاصة به، وغير ذلك. يجب أن تكون كل هذه المعلومات في الشيفرة المصدرية، والأهم من ذلك، في سجل Git. وسوم Git وملاحظات إصدار Github هما أداتان قويتان توفران لي هذه المعلومات. استخدمها إلى أقصى حد. ولا تنسَ أن كل إصدار ثنائي للمنتج يجب أن يكون متاحًا للتحميل المباشر. يجب أن أكون قادرًا على تنزيل الإصدار 0.1.3 واختباره مباشرةً، حتى لو كان المشروع يعمل على الإصدار 3.4 في الوقت الحالي. الواجهات غير الموثّقة كل جزء من البرنامج لديه واجهات يُفترض أن يُستَخدم من خلالها. إذا كان البرنامج عبارة عن مدير الحزم Ruby gem، فهناك أصناف وطرائق سأستخدمها بوصفي مستخدم نهائي لها. وإذا كان أحد تطبيقات الويب، فهناك صفحات ويب سيراها المستخدم النهائي ويتحكم بها لاستخدام التطبيق. يحتوي كل مشروع برمجي على واجهات، ويجب أن تكون موثّقةً بعناية. مثل كل شيء سابق، فإنَّ هذا أيضًا له علاقة بالصيانة. بوصفي مبرمجًا جديدًا في مشروع ما، سأبدأ في التعرف عليه من واجهاته. ويجب أن أكون قادرًا على فهم ما يفعله ومحاولة استخدامه بنفسي. أنا أتحدث هنا عن التوثيق للمستخدمين، وليس للمطورين. بشكلٍ عام، أنا ضد التوثيق داخل البرنامج. وأتفق تمامًا مع Agile Manifesto- بأن عمل البرنامج أهم بكثير من الوثائق الشاملة. لكن هذا لا يشير إلى التوثيق "الخارجي"، التي يفترض أن يقرأه المستخدمون، وليس المطورون. لذلك يجب توثيق تفاعل المستخدم النهائي مع البرنامج بشكل واضح. إذا كان برنامجك عبارة عن مكتبة، فإن مستخدميها النهائيين الذين سيستخدمونها هم من مطوري البرامج - لا يساهمون فيها ولكن يستخدمونها ببساطة كـ"الصندوق الأسود". هذه هي المعايير المستخدمة لتقييم المشاريع المفتوحة المصدر التي دخلت المنافسة على الجائزة لدينا. ترجمة -وبتصرف- للمقال Seven Deadly Sins of a Software Project لصاحبه Yegor Bugayenko
  10. أصبحت لغة Go -التي هي لغة برمجية عاميّة الغرض- واحدةً من لغات البرمجة الأكثر شعبيةً لتطوير الواجهة الخلفية. بالتركيز على البساطة، فإنَّ مصممي لغة Go ابتكروا لغةً سهلة التعلّم وأسرع من العديد من اللغات الأخرى لتطبيقات الويب، مستفيدين من الميزات الفعّالة مثل قدرتها على التعامل مع عدة طلبات في وقت واحد بسبب تزامنها. لهذا السبب، سيكون نشر تطبيق ويب بلغة Go مفيدًا للعديد من مطوري الواجهة الخلفية. Nginx هو أحد أشهر خوادم الويب في العالم نظرًا لاستخدامه الخفيف للموارد وموثوقيته في حال وجود حمل زائد. تعتمد العديد من المواقع الضخمة والأكثر زيارةً على Nginx لتقدّم محتواها. في النشر، غالبًا ما يُستخدم Nginx موازن حمل(load balancer) أو وكيل عكسي (reverse proxy) لزيادة الأمان وجعل التطبيق أكثر قوة. بالتزامن مع لغة Go لتطوير الواجهة الخلفية في الويب، يمكن لخادم Nginx تقديم تطبيق ويب سريع وقوي. ستتعلم في هذا الدرس كيفية بناء تطبيق ويب Hello World بلغة Go وننشره على خادم أوبنتو 18.04 مستخدمين Nginx كخادم عكسي. المتطلبات الأساسية لمتابعة هذا الدرس، ستحتاج إلى ما يلي: خادم أوبنتو 18.04 تم ضبطه باتباع التهيئة الأولية لخادم أوبنتو 18.04، متضمنًا مستخدمًا عاديًا بصلاحيات sudo وجدار حماية. يجب أن تكون لغة Go مثبّتة على حاسوبك. خادم Nginx مثبّت باتّباع الدرس كيف تثبّت خادم Nginx على أوبنتو 18.04. لا تتبع الخطوة 5 - إعداد كتل الخادم؛ ستنشئ كتلة خادم Nginx لاحقًا في هذا الدرس. اسم نطاق يشير إلى خادمك. سنستخدم your_domain في هذا الدرس. هذا ضروري للحصول على شهادة SSL لموقعك، حتى يمكنك أن تقدّم تطبيقك بأمان مع تشفير TLS. بالإضافة إلى ذلك، لتحقيق نشر بدرجة الإنتاج لتطبيق الويب Go، من المهم أن تحافظ على خادمك آمنًا بتثبيت شهادة TLS/SSL. أرشح لك هذه الخطوة بشدة. لتؤمّن تطبيق الويب Go، اتّبع كيف تؤمّن خادم Nginx بشهادة Let's Encrypt على أوبنتو بعد الخطوة 3 من هذا الدرس لتحصل على شهادة TLS/SSL مجانية. الخطوة 1: بناء تطبيق ويب بلغة البرمجة Go في هذه الخطوة، ستبني نموذج تطبيق ويب بلغة Go يعرض عبارة Hello World على نطاقك your_domain ويقوم بتحيّة المستخدم على الرابط your_domain/greet/. إذا كنت تريد تعلّم المزيد من أساسيات البرمجة في Go، تفحّص مقالتنا كيف تكتب برنامجك الأول بلغة Go. بدايةً، أنشئ مجلد جديد في مجلد GOPATH ليتضمن الملف المصدري. يمكنك تسمية المجلد بأي اسم تريده، ولكن في هذا الدرس سنستخدم اسم go-web: $ mkdir $GOPATH/go-web باتّباع بنية الملف المقترحة في درس فهم، تنصيب وتهيئة بيئة عمل لغة البرمجة Go، سيعطي هذا مجلدك المسار ‎~/go/go-web: بعد ذلك، نفّذ التعليمة التالية لتغيّر المجلد إلى مجلدك الذي أنشأته حديثًا في GOPATH: $ cd $GOPATH/go-web استخدم محرر النصوص nano أو المحرر المفضل لك لإنشاء ملف باسم main.go، والذي سيتضمن الشيفرة المصدرية لتطبيقك الويب: $ nano main.go لإنشاء وظيفة التطبيق Hello World، أضف شيفرة Go التالية إلى ملف main.go المنشأ حديثًا: package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World") }) http.HandleFunc("/greet/", func(w http.ResponseWriter, r *http.Request) { name := r.URL.Path[len("/greet/"):] fmt.Fprintf(w, "Hello %s\n", name) }) http.ListenAndServe(":9990", nil) } لنتعرّف الآن على ما سيفعله جزء الشيفرة السابق، بدءًا من السطر الأول. كتبت أولًا نقطة الدخول إلى تطبيقك: package main … تخبر تعليمة package main مُصرِّف Go أن يصرّف هذا الملف على أنّه برنامجٌ تنفيذي بدلًا من أن يكون مكتبة مشتركة (shared library). ثمّ لديك تعليمات import: … import ( "fmt" "net/http" ) … يستورد هذا الجزء الوحدات الضرورية لعمل هذه الشيفرة، والتي تشمل حزمة fmt القياسية وحزمة net/http لخادم الويب الخاص بك. يُنشئ الجزء التالي مسارك الأول في الدالة main والتي هي نقطة الدخول لأي تطبيق Go: … func main () { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World") }) … } … أنشئ المسار الأب / ضمن الدالة func main، التي ستعيد النص Hello World عندما تُطلب. كما هو موضح في الجزء التالي فإنّ المسار الثاني يقبل معاملًا في الرابط (URL parameter)، في هذه الحالة اسمًا، لعرضه مصحوبًا بالتحية. … func main () { … http.HandleFunc("/greet/", func(w http.ResponseWriter, r *http.Request) { name := r.URL.Path[len("/greet/"):] fmt.Fprintf(w, "Hello %s\n", name) }) … } … يستخدم هذا URL.Path الخاص بلغة Go لتخزين القيمة مباشرةً بعد /greet/ وتمريرها كاسم من معامل الرابط. أخيرًا، هيّئ الخادم: … func main () { … http.ListenAndServe(":9990", nil) } يشغّل الجزء السابق الخادم ويعرض تطبيقك عبر المنفذ 9990 باستخدام خادم http المدمج بلغة Go. احفظ الملف وأغلق المحرر النصي بمجرد الانتهاء من فحص الشيفرة في main.go. بعد ذلك، ابنِ الملف الثنائي القابل للتنفيذ لتطبيقك باستخدام الأمر: $ go build main.go التعليمة السابقة ستصرِّف الملف main.go لإنتاج ملف قابل للتنفيذ باسم main. لقد أنشأت نموذج تطبيق ويب بلغة Go. بعد ذلك، ستنشئ ملف وحدة systemd ليبقى تطبيقك شغّالًا في الخلفية حتى عندما لا تصل إلى خادمك. الخطوة 2: إنشاء ملف وحدة Systemd في هذه الخطوة، ستنشئ ملف وحدة systemd ليبقى تطبيقك شغّالًا في الخلفية حتى عندما يسجّل المستخدم خروجه من الخادم. سيجعل هذا تطبيقك ثابتًا، مما يجعلك أقرب بخطوة للنشر على مستوى الإنتاج. بدايةً، أنشئ ملفًا جديدًا في مجلد ‎/lib/systemd/system باسم goweb.service باستخدام محرر النصوص nano أو محرر النصوص المفضّل لك: $ sudo nano /lib/systemd/system/goweb.service لضبط معاملات الخدمة، أضف الجزء التالي في الملف. [Unit] Description=goweb [Service] Type=simple Restart=always RestartSec=5s ExecStart=/home/user/go/go-web/main [Install] WantedBy=multi-user.target يحدّد المتغير ExecStart=/home/user/go/go-web/main أنَّ نقطة الدخول لهذه الخدمة عبر الملف main القابل للتنفيذ الموجود في المجلد ‎/home/user/go/go-web، إذ user هو اسم المستخدم لحساب المستخدم العادي الذي يملك صلاحيات sudo على الخادم. Restart=always تضمن أنَّ systemd سيحاول دائمًا أن يعيد تشغيل البرنامج إذا توقف. في السطر التالي، RestartSec=5s يضبط وقت انتظار لخمس ثوانٍ بين محاولات إعادة التشغيل. يحدِّد WantedBy=multi-user.target في أي حالة سيفعّل الخادم الخدمة. احفظ وأغلق الملف. بعد أن كتبت ملف وحدة الخدمة، شغّل خدمة الويب Go باستخدام الأمر: $ sudo service goweb start للتأكّد فيما إذا كانت الخدمة شغّالة، استخدم الأمر التالي: $ sudo service goweb status ستستقبل الخرج التالي: Output ● goweb.service - goweb Loaded: loaded (/lib/systemd/system/goweb.service; disabled; vendor preset: enabled) Active: active (running) since Wed 2019-07-17 23:28:57 UTC; 6s ago Main PID: 1891 (main) Tasks: 4 (limit: 1152) CGroup: /system.slice/goweb.service └─1891 /home/user/go/go-web/main لتتعلم المزيد عن العمل مع ملف وحدة systemd، ألقِ نظرة على فهم وحدات وملفات وحدة Systemd. الآن بعد أن أصبح لديك تطبيقًا شغّالًا، يمكنك ضبط الوكيل العكسي Nginx. الخطوة 3: ضبط وكيل عكسي مع Nginx ستنشئ في هذه الخطوة كتلة خادم Nginx وستضبط الوكيل العكسي Nginx لتعرض تطبيقك على الإنترنت. أولًا، غيّر مجلد العمل الخاص بك إلى مجلد sites-available على الخادم Nginx: $ cd /etc/nginx/sites-available أنشئ ملفًا جديدًا باسم النطاق الذي تريد عرض تطبيقك عليه. سيستخدم هذا الدرس الملف your_domain: $ sudo nano your_domain أضف الأسطر التالية إلى الملف لوضع الإعدادات لـ your_domain: server { server_name your_domain www.your_domain; location / { proxy_pass http://localhost:9990; } } كتلة الخادم Nginx تستخدم proxy_pass لتقديم تطبيق الويب Go على عنوان IP لخادمك المشار إليه على أنّه مضيف محلي localhost لتشغيله على المنفذ 9990 . يشير server_name إلى اسم النطاق المعيّن لعنوان IP الخاص بك، في هذه الحالة your_domain وwww.your_domain. بعد ذلك، أنشئ رابطًا رمزيًا لإعدادات Nginx هذه في مجلد sites-enabled بتشغيل الأمر التالي: $ sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/your_domain الرابط الرمزي هو اختصار لملف في موقع آخر. يشير الاختصار المُنشأ حديثًا إلى الملف الأصلي للتكيّف مع التحديثات عند إجراء التعديلات عليه. يتطلب Nginx نسخة من الإعدادات في كلا المجلدين. بعدها، أعد تحميل إعدادات Nginx بتشغيل تعليمة إعادة التحميل: $ sudo nginx -s reload لتتأكد أن نشرك يعمل، قم بزيارة http://your_domain في متصفحك، إذ ستتم تحيتك بسلسلة نصية Hello World. ملاحظة: كما ذُكر في قسم المتطلبات الأساسية، يُنصح في هذه النقطة أن يتم تفعيل SSL/TLS على خادمك. سيضمن ذلك تشفير جميع الاتصالات بين التطبيق وزوّاره، وهو أمر مهم بشكلٍ خاص إذا طلب التطبيق معلومات حساسة مثل تسجيل الدخول أو كلمة المرور. اتّبع الآن درس كيف تؤمّن خادم Nginx بشهادة Let's Encrypt على أوبنتو لتحصل على شهادة SSL مجانية لخادم Nginx على أوبنتو 18.04. بعد أن تحصل على شهادات SSL/TLS خاصة بك، عُد وأكمل هذا الدرس. ضبطت الآن الوكيل العكسي Nginx لتعرض تطبيقك على اسم النطاق الخاص بك، وقمت بتأمين تطبيق الويب Go مع SSL/TLS. في الخطوة التالية، ستختبر تطبيقك عبر اتصالٍ آمن. الخطوة 4: اختبار التطبيق في هذه الخطوة، ستختبر تطبيقك عبر اتصالٍ آمن لتتأكد من أنَّ كل شيء يعمل. افتح متصفح الويب المفضل لك، وقم بزيارة https://your_domain: ستستقبل رسالة Hello World بسيطة. استقبال هذه الرسالة عند استخدام https://‎ في الرابط يشير إلى أنَّ التطبيق يُعرض عبر اتصالٍ آمن. بعد ذلك، حاول زيارة المسار الثاني https://your_domain/greet/your-name مستبدلًا بالعبارة your-name أيّ اسم آخر تريد أن يلقي عليه تطبيقك التحيّة: سيعيد التطبيق تحيّة بسيطة مع your-name، الذي يعتمد على المعامل الممرّر للرابط. حالما تستقبل هذه النتائج، تكون قد نشرت تطبيق ويب Go بنجاح. خاتمة في هذا الدرس، أنشأت تطبيق ويب بسيط بلغة Go مستخدمًا مكاتبها القياسية، وقمت بضبط وكيل عكسي مستخدمًا Nginx، واستخدمت شهادة SSL على نطاقك لتأمين تطبيقك. لتتعلّم المزيد حول Go، تفحّص توثيقها الرسمي. بإمكانك أيضًا أن تلقي نظرة على قسم لغة GO لتتعلم المزيد حول البرمجة بهذه اللغة الفعالة. ترجمة -وبتصرف- للمقال How To Deploy a Go Web Application Using Nginx on Ubuntu 18.04 لصاحبه Michael Okoh
  11. كتبت قبل بضع سنوات عن المراجعات التقنية المستقلة التي يجب أن يمر بها أي مشروع برمجي بشكل منتظم للتأكد من أنَّ كل شيء تحت السيطرة. ونوّهت مؤخرًا أنه لا يوجد عذر لعدم القيام بها. بالإضافة إلى ذلك، كلما زادت ثقتنا بالمبرمجين، زادت حاجتنا إلى مراجعة مشاريعهم بانتظام. فيما يلي ملخص بسيط لما يجب أن يتضمنه تقرير المراجع. حاولت أن أتطرق إلى هذا الموضوع في بضعة محادثات مؤخرًا مثل: اجعل العملاء يثقون بك في BDMSummit 2017، كيف تكون صادقًا وتحافظ على العميل في PMCon Kharkiv 2017، كيفية تجنب كارثة الاستعانة بمصادر خارجية في Kyiv Outsourcing Forum 2017. وأيضًا، هناك عدد من منشورات المدونة على نفس المنوال، بما في ذلك الأخطاء السبع القاتلة لمشروع برمجيات، وكيفية تجنب كارثة الاستعانة بمصادر خارجية للبرامج، وكيف تنجو من الاستعانة بمصادر خارجية للبرامج. أخيرًا، فيما يلي قائمة كاملة تقريبًا من الأشياء التي يجب أن يتضمنها التقرير الجيد. بشكل أساسي هي قائمة بالأسئلة التي يجب على المُراجع الإجابة عليها. عندما يتم جمع كل الإجابات، يكون التقرير جاهزًا. أهم الأسئلة تمَّ طرحها أوّلاً: هل إجراء الإصدار موثَّق، وآلي، وهل يعمل؟ هل الإصدارات تتجدد بشكل منتظم، على الأقل مرة أسبوعيًا؟ ما حجم الديون التقنية (الأشياء التي يجب إصلاحها "لاحقًا")؟ هل خط التسليم موثوقًا بما يكفي لاستبعاد الأخطاء؟ هل الشيفرة نظيفة؟ كم عدد النماذج المضادّة التي تظهر؟ هل تمَّ تسجيل جميع الأخطاء والميزات كملاحظات؟ هل تم تغطية الشيفرة الأساسية بوحدات اختبار، وهل التغطية مرئية؟ هل تم توقيع اتفاقيات "العمل من أجل التوظيف" مع جميع المطورين؟ هل تم توثيق القرارات التقنية الهيكلية الرئيسية؟ هل التحليل الثابت موضع التنفيذ ومُلزَم لإجراء تغييرات جديدة؟ هل التكاملات المستمرة موضع التنفيذ، وهل تؤخذ تقاريرها في الحسبان؟ هل الفرع الرئيسي (master branch) للقراءة فقط؟ هل يتم جمع المقاييس البرمجية ومراجعتها بانتظام؟ هل مستودع الشيفرة المصدرية يخضع لملكية العميل؟ هل متطلبات التوثيق قصيرة ومحدَّثة؟ هل تحتوي الفئات والطرق والتوابع الرئيسية على توثيق مضمَّن في الشيفرة؟ هل مخلفات مستودع الشيفرة المصدرية مجانية؟ هل تم توثيق واجهات UI/UX؟ هل سجلات الإنتاج مرئية وتتم مراجعتها بانتظام؟ ما مدى استجابة الفريق للملاحظات؟ هل لدى Git تاريخ واضح للتغيرات الموثَّقة؟ بشكلٍ أساسي، هذا تجميع قصير جدًا لأهم الأشياء التي يمكنك العثور عليها في CMMI. إنها تتطلب كل هذا، بالإضافة لقائمة كبيرة من الأشياء الأخرى موجودة في الأعلى. لكن المشروع الصغير لا يحتاج إلى جميع الأسئلة المذكورة. قائمتي أقصر، وأنا متأكد من أنها ستكون كافية لمعظم القرَّاء. بكل الأحوال، يمكنك مشاهدة التقارير التي يقوم المتطوعون بإنشائها للمشاركين في جائزة جودة البرمجيات، إذ يقومون بتحليل المشاريع مفتوحة المصدر والإبلاغ بشكلٍ مختصر عن المشكلات التي يجدونها. أعتقد أنهم يحاولون الإجابة عن نفس مجموعة الأسئلة السابقة تمامًا. ترجمة -وبتصرف- للمقال Software Project Review Checklist لصاحبه Yegor Bugayenko
  12. الواجهة البرمجية Promise رائعة ويمكنك جعلها مذهلة باستخدام async و await. إنّ الشيفرة المتزامنة سهلة التتبع والتنقيح إلا أنّ الشيفرة غير المتزامنة أفضل بشكل عام من حيث الأداء والمرونة، فلماذا "توقف العرض" بينما بإمكانك استقبال الكثير من الطلبات في وقت واحد ثمّ معالجة كلٍّ منها عندما يصبح جاهزًا؟ مع العديد من الواجهات البرمجية API الجديدة التي تم تحقيقها مع مبدأ الوعد، إذ أصبحت الوعود جزءًا كبيرًا في عالم الجافاسكربت. لنلقِ نظرة على كيفية استخدام الواجهة البرمجية للوعود. الوعود قيد التطبيق الواجهة البرمجية XMLHttpRequest غير متزامنة ولكنها لا تستخدم الواجهة البرمجية Promises. هناك عدة واجهات برمجية أصلية تستخدم الوعود الآن، مثل: Battery API. fetch API (بديل XHR) الواجهة البرمجية ServiceWorker ستصبح الوعود أكثر شيوعًا، لذا من المهم أن يعتاد عليها جميع مطوري الواجهات الأمامية، وتجدر الإشارة إلى أنّ Node.js هي منصة أخرى للوعود. (يبدو هذا واضحًا، كما الواجهة البرمجية Promise ميزة أساسية في اللغة). من المحتمل أن يكون اختبار الوعود أسهل مما تعتقد لأنه يمكنك استخدام setTimeout كـ "مهمة" لك غير متزامنة. استخدام Promise الأساسي يجب أن يستخدم الباني new Promise()‎ فقط للمهام الغير متزامنة الموروثة، مثل استخدام setTimeout أو XMLHttpRequest. يتم إنشاء وعدًا جديدًا باستخدام الكلمة المفتاحية new ويتم تمرير توابع resolve و reject لردّ النداء المزوَّد: var p = new Promise(function(resolve, reject) { // ...القيام بمهمة غير متزامنة وثم if(/* شرط جيد */) { resolve('Success!'); } else { reject('Failure!'); } }); p.then(function(result) { /* القيام بفعل ما مع النتيجة */ }).catch(function() { /* خطأ :( */ }).finally(function() { /* تنفّذ بغض النظر عن النجاح أو الفشل */ }); يعود الأمر للمطور فيما إذا كان يريد استدعاء resolve أو reject يدويًا ضمن جسم رد النداء اعتمادًا على نتيجة المهمة المعطاة. مثال واقعي لتحويل XMLHttpRequest إلى مهمة تعتمد على الوعد: // من وعود جاك أرشيبالد والعودة // http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest function get(url) { //إعادة وعد جديد. return new Promise(function(resolve, reject) { // XHR القيام بالعمل الاعتيادي لـ var req = new XMLHttpRequest(); req.open('GET', url); req.onload = function() { // هذا يتم استدعاؤه حتى في حالة 404 // لذا اختبر الحالة if (req.status == 200) { // قم بإنهاء الوعد مع نص الرد resolve(req.response); } else { // وإلا ارفض مع نص الحالة // والذي نأمل أن يكون خطأ ذو معنى reject(Error(req.statusText)); } }; // معالجة أخطاء الشبكة req.onerror = function() { reject(Error("Network Error")); }; // القيام بالطلب req.send(); }); } // استخدمه! get('story.json').then(function(response) { console.log("Success!", response); }, function(error) { console.error("Failed!", error); }); إذا كان من الممكن اتخاذ إجراء غير متزامن قد لا تحتاج إلى إكمال المهام غير المتزامنة ضمن الوعد، لكن سيكون الأفضل هو أن تكون القيمة المعادة وعدًا لذا يمكنك أن تعدّ كم عدد الوعود التي حصلت عليها من تابع معطى. في تلك الحالة يمكنك ببساطة استدعاء Promise.resolve()‎ أو Promise.reject()‎ بدون استخدام الكلمة المفتاحية new. مثلًا: var userCache = {}; function getUserDetail(username) { // في كلتا الحالتين، تم إضافته إلى الذاكرة أو لا، سيتم إعادة وعد if (userCache[username]) { // new إعادة وعد بدون الكلمة المفتاحية return Promise.resolve(userCache[username]); } // لتحصل على المعلومات fetch استخدم الواجهة البرمجية // وعدًا fetch تعيد return fetch('users/' + username + '.json') .then(function(result) { userCache[username] = result; return result; }) .catch(function() { throw new Error('Could not find user: ' + username); }); } بما أن القيمة المعادة هي وعد لذا يمكنك استخدام التوابع then و catch عليها. then كل كائنات الوعد تملك التابع then الذي يسمح لك بالتفاعل مع الوعد. رد النداء الأول لتابع then يستدعي النتيجة المعطاة له عن طريق استدعاء ()resolve: new Promise(function(resolve, reject) { // setTimeout حدث زائف غير متزامن باستخدام setTimeout(function() { resolve(10); }, 3000); }) .then(function(result) { console.log(result); }); // console من الطرفية // 10 رد النداء لـ then يتم تشغيله عندما ينتهي الوعد. يمكنك أيضًا أن تسلسل ردود النداء للتابع then: new Promise(function(resolve, reject) { // setTimeout حدث زائف غير متزامن باستخدام setTimeout(function() { resolve(10); }, 3000); }) .then(function(num) { console.log('first then: ', num); return num * 2; }) .then(function(num) { console.log('second then: ', num); return num * 2; }) .then(function(num) { console.log('last then: ', num);}); // من الـ console // first then: 10 // second then: 20 // last then: 40 كل then تستقبل القيمة المعادة من استدعاء then السابق. إذا أُنهي الوعد قبل أن يتم استدعاء التابع then مجددًا، يتم إيقاف رد النداء مباشرةً. إذا تم رفض الوعد ثم استدعاء التابع then بعد الرفض، لا يتم استدعاء رد النداء أبدًا. catch يتم استدعاء رد النداء catch عندما يُرفض الوعد: new Promise(function(resolve, reject) { // setTimeout حدث زائف غير متزامن باستخدام setTimeout(function() { reject('Done!'); }, 3000); }) .then(function(e) { console.log('done', e); }) .catch(function(e) { console.log('catch: ', e); }); // console من الطرفية // 'catch: Done!' ما تمرره لتابع reject يعود لك ولكن النمط المعتاد هو إرسال Error لـ catch: reject(Error('Data could not be found')); finally رد النداء المعرّف حديثًا finally يتم استدعاؤه بغض النظر عن النجاح أو الفشل: (new Promise((resolve, reject) => { reject("Nope"); })) .then(() => { console.log("success") }) .catch(() => { console.log("fail") }) .finally(res => { console.log("finally") }); // >> fail // >> finally Promise.all فكر بمحمّلات الجافاسكربت: يوجد أوقات يتم فيها تشغيل عدة تفاعلات غير متزامنة وتريد الاستجابة عندما تكتمل جميعها، هنا يأتي دور التابع Promise.all، هذا التابع يأخذ مصفوفة من الوعود ويعطيك رد نداء واحد فقط عند إنهاء جميع الوعود. Promise.all([promise1, promise2]).then(function(results) { // كلا الوعدين تم إنهاؤهما }) .catch(function(error) { // تم رفض وعد واحد أو أكثر }); الطريقة المثالية للتفكير بـ Promise.all هي إطلاق عدة طلبات AJAX (باستخدام fetch) في نفس الوقت. var request1 = fetch('/users.json'); var request2 = fetch('/articles.json'); Promise.all([request1, request2]).then(function(results) { // كلا الوعدين تم تنفيذهما }); يمكنك أن تدمج عدة واجهات برمجية مثل fetch والواجهة البرمجية Battery بما أنّها تعيد وعودًا. Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(results) { // كلا الوعدين تم تنفيذهما }); التعامل مع الرفض صعب بالطبع. إذا تم رفض أيّ وعد سيتم إطلاق catch للرفض الأول: var req1 = new Promise(function(resolve, reject) { // setTimeout حدث زائف غير متزامن باستخدام setTimeout(function() { resolve('First!'); }, 4000); }); var req2 = new Promise(function(resolve, reject) { // setTimeout حدث زائف غير متزامن باستخدام setTimeout(function() { reject('Second!'); }, 3000); }); Promise.all([req1, req2]).then(function(results) { console.log('Then: ', results); }).catch(function(err) { console.log('Catch: ', err); }); // console من الـ // Catch: Second! ستكون Promise.all مفيدة أكثر للواجهات البرمجية التي تتجه لاستخدام الوعود. Promise.race تعدّ الدالة Promise.race مفيدة فبدلًا من أن يتم الانتظار حتى إنهاء جميع الوعود أو رفضها تقوم بتشغيل أيّ وعد في المصفوفة تم إنهاؤه أو رفضه. var req1 = new Promise(function(resolve, reject) { // setTimeout حدث زائف غير متزامن باستخدام setTimeout(function() { resolve('First!'); }, 8000); }); var req2 = new Promise(function(resolve, reject) { // setTimeout حدث زائف غير متزامن باستخدام setTimeout(function() { resolve('Second!'); }, 3000); }); Promise.race([req1, req2]).then(function(one) { console.log('Then: ', one); }).catch(function(one, two) { console.log('Catch: ', one); }); // console من الـ // Then: Second! يمكن أن تشغّل حالة الاستخدام طلبًا إلى مصدر أوليّ ومصدر ثانوي (في حال عدم توفر الأوليّ والثانوي). اعتد على الوعود كانت الوعود موضوعًا مثيرًا للاهتمام خلال السنوات القليلة الماضية (أو خلال السنوات العشرة الأخيرة إذا كنت مستخدم مجموعة أدوات Dojo) وانتقلت من كونها نمط في إطار عمل جافاسكربت إلى كونها من أساس اللغة. وربما من الأفضل افتراض أنك ستشاهد معظم الواجهات البرمجية الجديدة في جافاسكربت يتم تنفيذها بنمط يعتمد على الوعد، وهذا أمر ممتاز. إنّ المطورين قادرون على تجنب جحيم رد النداء وتمرير التفاعلات غير المتزامنة مثل أيّ متغير آخر. تستغرق الوعود بعض الوقت لتكون الأدوات الأصلية وقد حان الوقت لتعلّمها. يمكنك في أي وقت الرجوع إلى توثيق الكائن Promise في موسوعة حسوب كما ننصحك أيضًا بقراءة صفحة «استخدام الوعود» بعد هذه المقالة مباشرةً لوضع ما تعلمته موضع التطبيق. ترجمة -وبتصرف- للمقال JavaScript Promise API لصاحبه David Walsh
  13. Nginx خادم ويب قوي ووكيل عكسي (reverse proxy) يُستخدم لتقديم العديد من المواقع الأكثر شهرةً في العالم. سنوضّح في هذا الدليل كيفية ترقية Nginx الموجود القابل للتنفيذ، بدون قطع اتصالات العميل. المتطلبات الأساسية قبل البدء في هذا الدليل، يجب أن يكون لديك مستخدم غير جذر على خادمك، تمَّ إعداده مع صلاحيات sudo. ستحتاج أيضًا أن يكون لديك خادم Nginx مثبَّتًا. إذا كنت تستخدم أبنتو 14.04، يمكنك تعلّم كيفية ضبط مستخدم بصلاحيات sudo هنا. يمكنك تثبيت Nginx باتّباع هذا الدليل. إذا كنت تستخدم CentOS 7، يمكنك الحصول على ضبط المستخدم بصلاحيات sudo عبر هذا الدليل، متّبعًا هذا الدليل لتثبّت Nginx. كيف تعمل الترقية يعمل Nginx على إنشاء إجراء سيّد أو رئيسي (master process) عندما تبدأ الخدمة. ويشغّل الإجراء السيّد بدوره إجراء تابع (worker process) أو أكثر تتعامل مع اتصالات العميل الفعلية. تم تصميم Nginx لأداء إجراءات معينة عندما يتلقّى إشارات محددة من المدير. يمنحك الفرصة باستخدام هذه الإشارات لتقوم بترقيته أو ترقية إعداداته الموجودة بسهولة، بدون قطع اتصالات العميل. هناك سكربتات (scripts) معينة للخدمة مزوّدة من قِبل القائمين على صيانة حزمة التوزيع ستوفر القدرة على الاستخدام مع الترقيات التقليدية. لكن توفر الترقية يدويًا المزيد من المرونة في المنهجية وتسمح لك بمراجعة الترقية للتراجع بسرعة إذا كان هناك مشاكل. هذا أيضًا سيوفر خيارًا للترقية بأمان إذا قمت بتثبيت Nginx من المصدر أو باستخدام طريقة لا توفر هذه الإمكانية. ستُستخدم الإشارات التالية: USR2: تولّد هذه مجموعة جديدة من إجراءات السيد/التابع بدون التأثير على المجموعة القديمة. WINCH: تخبر هذه الإجراء السيد لـNginx أن يوقف أغراض التابع المرتبطة بأمان. HUP: تخبر هذه الإجراء السيد لـNginx أن يعيد قراءة ملفات إعداداته ويستبدل إجراءات التابع بتلك المرتبطة بالإعدادات الجديدة. إذا كان هناك سيد قديم وجديد قيد التشغيل، فإنَّ إرسال هذا إلى السيد القديم سيولّد توابعًا باستخدام إعداداتهم الأصليّة. QUIT: توقف هذه تشغيل السيّد وتوابعه بأمان. TERM: تهيئ هذه إيقاف تشغيل سريع للسيّد وتوابعه. KILL: توقف هذه السيّد وتوابعه بدون أيّ تنظيف. إيجاد معرّفات إجراء Nginx لإرسال إشارات إلى الإجراءات المختلفة، نحتاج إلى معرفة معرّف الإجراء (PID) المستهدف. هناك طريقتان سهلتان لإيجاد هذا. أولًا، يمكنك استخدام الأداة ps وثمّ grep لـNginx بين النتائج. هذا بسيط ويسمح لك برؤية إجراءات السيّد والتابع: $ ps aux | grep nginx output root 10846 0.0 0.3 47564 3280 ? S 13:26 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf nginx 10847 0.0 0.1 47936 1908 ? S 13:26 0:00 nginx: worker process user 10961 0.0 0.0 112640 964 pts/0 S+ 13:53 0:00 grep --color=auto nginx يحتوي العمود الثاني على معرّفات الإجراءات المُختارة. المميز هو معرّف الإجراء. يوضّح العمود الأخير أنَّ النتيجة الأولى هي الإجراء السيّد لـNginx. طريقة أخرى لإيجاد معرّف الإجراء السيّد لـNginx وهي طباعة محتويات ملف ‎/run/nginx.pid: $ cat /run/nginx.pid output 10846 إذا كان هناك إجراءين سيّد لـNginx قيد التشغيل، سينتقل الإجراء القديم إلى run/nginx.pid.oldbin/. توليد مجموعة سيّد/توابع جديدة الخطوة الأولى لتحديث ملفنا القابل للتنفيذ بأمان هي في الواقع تحديث ملفك الثنائي.قم بذلك باستخدام أيّ طريقة مناسبة لتثبيت Nginx لديك، سواء من خلال مدير الحزمة أو التثبيت المصدري. بعد وضع الملف الثنائي الجديد في مكانه، يمكنك توليد مجموعة ثانية من إجراءات السيّد/التابع التي تستخدم الملف الجديد القابل للتنفيذ. يمكنك القيام بذلك إمّا بإرسال إشارة USR2 مباشرةً إلى رقم المعرّف الذي استعلمت عنه (تأكّد هنا من استبدال معرّف الإجراء السيّد بمعرّف الإجراء السيّد الخاص بخادمك Nginx): $ sudo kill -s USR2 10846 او يمكنك قراءة واستبدال القيمة المخزّنة في ملف PID مباشرةً باستخدام أمر كهذا: $ sudo kill -s USR2 `cat /run/nginx.pid` إذا تفحّصت إجراءاتك الحاليّة، ستشاهد أنّ لديك الآن مجموعتان من إجراءات السيّد/التوابع لـNginx: $ ps aux | grep nginx output root 10846 0.0 0.3 47564 3280 ? S 13:26 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf nginx 10847 0.0 0.1 47936 1908 ? S 13:26 0:00 nginx: worker process root 11003 0.0 0.3 47564 3132 ? S 13:56 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf nginx 11004 0.0 0.1 47936 1912 ? S 13:56 0:00 nginx: worker process user 11031 0.0 0.0 112640 960 pts/0 S+ 14:01 0:00 grep --color=auto nginx يمكنك أيضًا أن تشاهد أنَّ ملف run/nginx.pid/ الأصلي قد انتقل إلى run/nginx.pid.oldbin/ ومعرّف الإجراء السيّد الجديد كُتبَ في الملف run/nginx.pid/: $ tail -n +1 /run/nginx.pid* output ==> /run/nginx.pid <== 11003 ==> /run/nginx.pid.oldbin <== 10846 يمكنك الآن إرسال إشارات إلى أيّ من الإجراءين السيّدين مستخدمًا المعرّفات الموجودة في هذه الملفات. عند هذه النقطة، فإن مجموعتي السيّد/التابع شغّالتين وقادرتين على تلبية طلبات العميل. تستخدم المجموعة الأولى الملف الأصلي القابل للتنفيذ والإعدادات الأصلية لـNginx وتستخدم المجموعة الثانية الإصدارات الأحدث. يمكنهم الاستمرار في العمل جنبًا إلى جنب، لكن يجب أن نبدأ في الانتقال إلى المجموعة جديدة من أجل الاتساق. إيقاف تشغيل توابع السيّد الأول للبدء بالانتقال إلى المجموعة الجديدة، فإنَّ أول شيء يمكننا القيام به هو إيقاف الإجراءات توابع السيّد الأصلي. سينهي التوابع الأصليين معالجة كل اتصالاتهم الحالية ثمَّ يخرجون. أوقف توابع المجموعة الأصلية بإصدار إشارة WINCH إلى إجرائهم السيّد: $ sudo kill -s WINCH `cat /run/nginx.pid.oldbin` سيتيح ذلك لتوابع السيّد الجديد أن يتعاملوا مع اتصالات العميل الجديدة بمفردهم. سيظلّ الإجراء السيّد القديم قيد التشغيل، لكن بدون توابع: $ ps aux | grep nginx output root 10846 0.0 0.3 47564 3280 ? S 13:26 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf root 11003 0.0 0.3 47564 3132 ? S 13:56 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf nginx 11004 0.0 0.1 47936 1912 ? S 13:56 0:00 nginx: worker process user 11089 0.0 0.0 112640 964 pts/0 R+ 14:13 0:00 grep --color=auto nginx يتيح هذا لك مراجعة التوابع الجدد لأنّهم يقبلون الاتصالات بشكلٍ منفصل مع الحفاظ على قابلية العودة إلى الملف القديم القابل للتنفيذ إذا كان هناك خطأٌ ما. تقييم النتيجة واتخاذ الخطوات التالية عند هذه النقطة يجب اختبار النظام ومراجعته للتأكّد من عدم وجود بوادر مشاكل. يمكنك ترك الإعدادات في هذه الحالة طالما ترغب بالتأكّد من أنّ الملف الجديد القابل للتنفيذ لـNginx خالٍ من الأخطاء وقادر على التعامل مع حركة المرور الخاصة بك. ستعتمد خطوتك التالية تمامًا على ما إذا كنت تواجه مشاكل. إذا كانت الترقية ناجحة، أكمل الانتقال إذا لم تواجه أيّ مشاكل مع توابع مجموعتك الجديدة، يمكنك إيقاف تشغيل الإجراء السيّد القديم بأمان. للقيام بذلك، فقط أرسل الإشارة QUIT إلى السيّد القديم: sudo kill -s QUIT `cat /run/nginx.pid.oldbin` سيُغلَق الإجراء السيّد القديم بأمان، تاركًا فقط مجموعتك الجديدة من السيّد/التوابع لـNginx. عند هذه النقطة، تكون قد حدّثت الملف الثنائي الموجود لـNginx بأمان بدون مقاطعة اتصالات العميل. إذا واجهت مشاكل في التوابع الجديدة، عُد إلى الملف الثنائي القديم إذا لاحظت وجود مشاكل في مجموعة التوابع الجديدة، يمكنك العودة إلى الملف الثنائي والإعدادات القديمة. وهذا ممكن خلال الجلسة نفسها. أفضل طريقة للقيام بذلك هي إعادة تشغيل توابع السيّد القديم بإرسال إشارة HUP له. عادةً، عندما ترسل إشارة HUP للسيّد في Nginx، يعيد قراءة ملفات إعداداته ويشغّل توابعًا جديدة. لكن عندما يكون الهدف سيّد أقدم، فقط يولّد توابعًا جديدة باستخدام إعدادات العمل الأصلية: $ sudo kill -s HUP `cat /run/nginx.pid.oldbin` يجب أن تعود الآن ليكون لديك مجموعتين من إجراءات السيّد/التابع: $ ps aux | grep nginx output root 10846 0.0 0.3 47564 3280 ? S 13:26 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf root 11003 0.0 0.3 47564 3132 ? S 13:56 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf nginx 11004 0.0 0.1 47936 1912 ? S 13:56 0:00 nginx: worker process nginx 19918 0.0 0.1 47936 1900 ? S 14:47 0:00 nginx: worker process user 19920 0.0 0.0 112640 964 pts/0 R+ 14:48 0:00 grep --color=auto nginx ترتبط التوابع الجديدة بالتابع القديم. عند هذه النقطة فإنَّ توابع المجموعتين سيقبلون اتصالات العميل. أوقف الآن الإجراء السيّد الجديد الذي يحوي أخطاء وتوابعه بإرسال إشارة QUIT: $ sudo kill -s QUIT `cat /run/nginx.pid` يجب أن تعود للسيّد والتوابع القديمين: $ ps aux | grep nginx output root 10846 0.0 0.3 47564 3280 ? S 13:26 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf nginx 19918 0.0 0.1 47936 1900 ? S 14:47 0:00 nginx: worker process user 19935 0.0 0.0 112640 964 pts/0 R+ 14:50 0:00 grep --color=auto nginx سيستعيد السيّد الأصلي ملف run/nginx.pid/ لمعرّفه. إذا لم يعمل ما سبق لأي سبب من الأسباب، يمكنك أن تحاول فقط إرسال إشارة TERM للسيّد الجديد للخادم، وهذا يجب أن تتهيأ عملية إيقاف التشغيل. ويجب أن يوقف هذا السيّد الجديد وأي تابع من توابعه أثناء البحث التلقائي عن السيّد القديم لبدء إجراءات التابع الخاص به. إذا كان هناك مشاكل خطيرة والتوابع بهم أخطاء ولم يتم إنهاؤهم، يمكنك إرسال إشارة KILL لكلٍّ منهم للتنظيف. يجب أن يكون هذا كحل أخير لأنّه سيؤدي إلى قطع الاتصالات. بعد الانتقال مرة أخرى إلى الملف الثنائي القديم، تذكّر أنّه لا يزال لديك الإصدار الجديد مثبّتًا على نظامك. يجب أن تزيل الإصدار الذي يحوي مشاكل وتعود لإصدارك السابق حتى يعمل Nginx بدون أيّة مشاكل عند إعادة التشغيل. خاتمة يجب أن تكون الآن قادرًا على نقل أجهزتك بسلاسة من ملف ثنائي لـNginx إلى آخر. إنَّ قدرة Nginx على التعامل مع مجموعتي سيّد/توابع مع المحافظة على معلومات علاقاتهم تتيح لنا القدرة على ترقية برنامج الخادم بدون أن يصبح جهاز الخادم في وضع عدم الاتصال. ترجمة -وبتصرف- للمقال How To Upgrade Nginx In-Place Without Dropping Client Connections لصاحبه Justin Ellingwood
  14. إحدى أسوأ الأسرار التي تمَّ الاحتفاظ بها حول AJAX على الويب هو أنّ الواجهة البرمجية (API) الأساسية لها، XMLHttpRequest، لم توجد للغرض الذي نستخدمه الآن. لقد قمنا بعمل جيد في إنشاء واجهة برمجية جيّدة باستخدام الكائن XHR ولكننا نعرف أنه يمكننا القيام بعمل أفضل. نحن نبذل الجهود لتحقيق الأفضل الذي هو الواجهة البرمجية fetch. لنأخذ فكرة عامة عن التابع window.fetch الجديد، المتوفر الآن في Firefox و Chrome Canary. الكائن XMLHttpRequest إنَّ كائن XHR معقد قليلًا برأيي ولا أريد أن أبدأ بشرح لماذا "XML" تُكتب بأحرف كبيرة بينما "Http" تُكتب بأسلوب سنام الجمل. على أيّة حال، لنلاحظ كيف نستخدم XHR. // فوضى XHR فقط الحصول على if (window.XMLHttpRequest) { // موزيلا، سفاري... request = new XMLHttpRequest(); } else if (window.ActiveXObject) { // إنترنت إكسبلورر try { request = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {} } } // فتح، إرسال request.open('GET', 'https://davidwalsh.name/ajax-endpoint', true); request.send(null); بالطبع إنَّ أطر عمل جافاسكربت الخاصة بنا تجعل XHR أكثر متعة للعمل، ولكن ما نراه في الأعلى هو مثال بسيط على فوضى XHR. استخدام fetch الأساسي تم توفير دالة fetch في نطاق window العام، الوسيط الأول له هو الرابط URL: // الرابط (إلزامي)، الخيارات (اختيارية) fetch('https://davidwalsh.name/some/url', { method: 'get' }).then(function(response) { }).catch(function(err) { // خطأ :( }); وهذا يشبه إلى حد بعيد الواجهة البرمجية Battery المحدّثة، إذ تستخدم الواجهة البرمجية fetch الوعود في جافاسكربت لمعالجة النتائج/ردود النداء: // معالجة رد بسيط fetch('https://davidwalsh.name/some/url').then(function(response) { }).catch(function(err) { // خطأ :( }); // "تسلسل لمعالجة أكثر "تطورًا fetch('https://davidwalsh.name/some/url').then(function(response) { return //... }).then(function(returnedValue) { // ... }).catch(function(err) { // خطأ :( }); إذا لم تكن معتادًا على استخدام then، يجب أن تعتاد عليه لأنها ستنتشر قريبًا في كلّ مكان. ترويسات الطلب القدرة على ضبط ترويسات الطلب هي أمر مهم في مرونة الطلب. يمكنك العمل مع ترويسات الطلب بتنفيذ new Headers()‎: // إنشاء كائن ترويسة فارغ var headers = new Headers(); // إضافة بعض الترويسات headers.append('Content-Type', 'text/plain'); headers.append('X-My-Custom-Header', 'CustomValue'); // للترويسة set و get و check قيم headers.has('Content-Type'); // true headers.get('Content-Type'); // "text/plain" headers.set('Content-Type', 'application/json'); // حذف ترويسة headers.delete('X-My-Custom-Header'); // إضافة قيم ابتدائية var headers = new Headers({ 'Content-Type': 'text/plain', 'X-My-Custom-Header': 'CustomValue' }); يمكنك استخدام التوابع append و has و get و set و delete لتعديل ترويسات الطلب. لاستخدام ترويسات الطلب أنشئ الكائن Request: var request = new Request('https://davidwalsh.name/some-url', { headers: new Headers({ 'Content-Type': 'text/plain' }) }); fetch(request).then(function() { /* معالجة الرد*/ }); لنطّلع على عمل الكائنين Response و Request. الكائن Request يمثّل الكائن Request جزء الطلب عند استدعاء التابع fetch، يمكنك إنشاء طلبات مخصصة ومتطورة بتمرير الكائن Request للتابع fetch: method (الطريقة): يمكن أن تكون GET أو POST أو PUT أو DELETE أو HEAD url (الرابط): رابط الطلب headers (الترويسة): ترتبط مع الكائن Headers referrer (المرجع): مرجع الطلب mode (النمط): يكون cors أو no-cors أو same-origin credentials (بيانات الاعتماد): هل تعمل ملفات تعريف الارتباط (cookies) مع الطلب؟ وتأخذ إحدى القيمتين omit، أو same-origin. redirect (إعادة التوجيه): يمكن أن يأخذ القيمة follow أو error أو manual. integrity (التكامل): قيمة تكامل المصدر الفرعي. cache (التخزين المؤقت): وضع التخزين المؤقت ويمكن أن يأخذ إحدى القيم default أو reload أو no-cache عينة عن استخدام الكائن Request: var request = new Request('https://davidwalsh.name/users.json', { method: 'POST', mode: 'cors', redirect: 'follow', headers: new Headers({ 'Content-Type': 'text/plain' }) }); // الآن استخدمه fetch(request).then(function() { /* معالجة الرد */ }); الوسيط الأول URL هو فقط الوسيط الإلزامي، وكل خاصية تصبح للقراءة فقط حالما يتم إنشاء نسخة من الكائن Request، ومن المهم أن نلاحظ أن الكائن Request يملك التابع clone المهم عند استخدام fetch ضمن الواجهة البرمجية Service Worker -- يعد الكائن Request مجرًى ولهذا يجب أن يتم نسخه عند تمريره إلى استدعاء آخر للتابع fetch. بصمة التابع fetch ولكن نفس استخدام الكائن Request لذا يمكنك القيام بما يلي: fetch('https://davidwalsh.name/users.json', { method: 'POST', mode: 'cors', redirect: 'follow', headers: new Headers({ 'Content-Type': 'text/plain' }) }).then(function() { /* معالجة الرد */ }); من المحتمل أن تستخدم نسخًا من الكائن Request فقط ضمن Service Workers بما أن الكائن Request والتابع fetch يؤديان نفس الوظيفة. الكائن Response التابع then الذي يخص fetch مزود بالكائن Response ولكن يمكنك أيضًا إنشاء كائنات Response بنفسك -- حالة أخرى قد تواجهها عند استخدام service workers. يمكنك ضبط ما يلي عند استخدام الكائن Response: type (النوع): يمكن أن يكون basic أو cors url (الرابط) useFinalURL (استخدام الرابط النهائي): قيمة منطقية للرابط url إذا كان رابطًا نهائيًا أم لا status (الحالة): رمز الحالة (مثلًا 200، 400) ok: قيمة منطقية للاستجابة الناجحة (الحالة في المجال بين 200- 299) statusText (نص الحالة): نص يعبّر عن الحالة وفقًا للرمز (مثلًا: OK) headers (الترويسة): كائن Headers المرتبط بالاستجابة // service worker أنشئ ردك لاختبار // جديد (الجسم، الخيارات) Response كائن var response = new Response('.....', { ok: false, status: 404, url: '/' }); // Response يجلب مجدَّدًا نسخة من الكائن fetch الذي يخص then التابع fetch('https://davidwalsh.name/') .then(function(responseObj) { console.log('status: ', responseObj.status); }); يوفر الكائن Response أيضًا التوابع التالية: clone()‎: تُنشئ نسخة من الكائن Response ()error: تعيد كائن Response جديد مرتبط مع خطأ في الشبكة ()redirect: تنشئ استجابة جديدة مع رابط URL مختلف ()arrayBuffer: تعيد وعدًا يُقبل (resolve) مع ArrayBuffer ()blob: تعيد وعدًا يُقبل (resolve) مع Blob ()formData: تعيد وعدًا يُقبل (resolve) مع كائن FormData ()json: تعيد وعدًا يُقبل (resolve) مع كائن JSON ()text: تعيد وعدًا يُقبل (resolve) مع القيمة النصية USVString التعامل مع JSON بفرض أنّك أنشأت طلب لـJSON -- معلومات نتيجة ردود النداء لديها التابع json لتحويل البيانات الخام إلى كائن جافاسكربت: fetch('https://davidwalsh.name/demo/arsenal.json').then(function(response) { // JSON التحويل إلى return response.json(); }).then(function(j) { // هو كائن جافاسكربت j console.log(j); }); بالطبع هذا أسهل من (JSON.parse(jsonString لكن طريقة json تعد اختصارًا سهلًا أيضًا. التعامل مع استجابات Text/HTML الأساسية ليست دائما JSON هي صيغة رد الطلب المرغوبة لذا إليك كيف نجعل الاستجابة على شكل نص أو HTML: fetch('/next/page') .then(function(response) { return response.text(); }).then(function(text) { // <!DOCTYPE .... console.log(text); }); بإمكانك الحصول على استجابة نصية عبر تسلسل طريقة then للوعد مع طريقة text()‎. التعامل مع استجابات بشكل بيانات ثنائية إذا كنت ترغب مثلًا بتحميل صورة باستخدام التابع fetch فإن هذا سيكون مختلفًا. fetch('https://davidwalsh.name/flowers.jpg') .then(function(response) { return response.blob(); }) .then(function(imageBlob) { document.querySelector('img').src = URL.createObjectURL(imageBlob); }); يأخذ التابع blob()‎ تدفق الاستجابة وتقرأه حتى يكتمل. الحصول على بيانات النموذج استخدام آخر شائع للـAJAX وهو إرسال بيانات نموذج، وإليك كيف يمكن أن نستخدم التابع fetch للحصول على بيانات نموذج مرسلة بالطريقة POST: fetch('https://davidwalsh.name/submit', { method: 'post', body: new FormData(document.getElementById('comment-form')) }); وإذا أردت إرسال البيانات بصيغة JSON إلى الخادم: fetch('https://davidwalsh.name/submit-json', { method: 'post', body: JSON.stringify({ email: document.getElementById('email').value, answer: document.getElementById('answer').value }) }); سهل جدًا ومريح للعينين أيضًا. قصة لم تُكتب رغم أن fetch هي واجهة برمجية سهلة الاستخدام، إلا أنّ الواجهة البرمجية الحالية -تاريخ ترجمة المقال- لا تسمح بإلغاء الطلب مما يجعلها غير مستخدمة لكثير من المطورين. إن الواجهة البرمجية fetch الجديدة تبدو أفضل وأسهل استخدامًا من XHR. وبعد كل ذلك تمّ إنشاؤها حتى نتمكّن من استخدام AJAX بالطريقة الصحيحة. تملك fetch فوائد الخبرات السابقة، ولا أطيق الانتظار حتى يتم استخدامها على نطاق واسع. ترجمة -وبتصرف- للمقال ‎fetch API لصاحبه David Walsh
  15. قال لي أحد أصدقائي عندما اتصل بي البارحة: "اسمع، يا صديقي، لقد وثقت بهم لأكثر من عام - لقد كنّا شركاء، وكانوا يبرمجون كل شيء بالوقت الذي كنت أنا مشغولًا في تطوير الأعمال، والآن استقالوا ولم يتبقَ لي شيئًا، ما الذي يتوجب عليَّ فعلهُ بكل ملفات JavaScript هذه؟ كيف لي أن أتأكد بأنَّ هذه الملفات ملكي؟ بالإضافة لذلك، لا يريدون التعاون معي، أشعر بأنني رهين تصرفهم هذا، من فضلك، ساعدني". فكان جوابي له: "ماذا يمكنني أن أقول؟ لقد فات الأوان يا صديقي، ولكن الخبر السّار هو أنك لست أول من واجه هذه المشكلة". السيناريو النموذجي لوصف المشكلة "الثقة، الدفع، الخسارة". أولًا ستثق في مبرمجيك، وسيكونون شركاء لك تؤمن بعملهم، وتشعر بأنّك متأكدًا أنَّك قد اخترت الأفضل، سيظهرون لك ثقةً كبيرةً جدًا، وستشعر بالحماس عندما تنظر إلى سيرهم الذاتية، إنهم يعرفون جافاسكربت و DevOps و GitHub وحتى البيانات الضخمة، إنّهم بالتأكيد الأفضل لك، بالإضافة إلى ذلك، لديهم خبرة في هذا العمل لمدة عشر سنوات، فما الذي تحتاجه أكثر من ذلك، صحيح؟ ثانيًا أنت تدفع لهم، وإلا ما الذي يدفعهم للعمل، صحيح؟ إنَّ الموهبة الحقيقية باهظة الثمن، كلنا نعرف ذلك، إنّك تدفع لهم بانتظام مقابل الوقت الذي يقضونه في العمل على مشروعك. ستشعر بالحماس لرؤية أموالك تتحول إلى برمجيات تعمل، سيوفرون نسخًا جديدة بانتظام، يوجد هناك بعض الأخطاء، لكن من الطبيعي أن تظهر الأخطاء، صحيح؟ إنهم يقومون بشرح كل شيء لك وأنت تستمر في الدفع لهم. أخيرًا ستخسر عندما تدرك أنَّ هذا البرنامج يعود لهم وليس لك، وبأنَّهم قد استقالوا بسبب بعض أسباب العمل ولم يتبقَ لك شيء، أنت لا تستطيع فهم هذه الملفات، وأصلًا لا تملكها إنَّما هي موجودة في مكان ما في مستودع المبرمجين على منصة Git. توظف المزيد من الأشخاص لمساعدتك على حفظ ما تبقى من البرنامج، لكنهم سيخبرونك أنَّ الوقت قد حان لبدء كل شيء من الصفر. ستُحبط بشكلٍ هائل، وستكون مستعدًا للعودة إلى الخطوة الأولى - لأنك تثق بهؤلاء الموظفين الجدد، فإنهم بالتأكيد سيبدون صادقين، وليسوا مثل المحتالين السابقين. هل يبدو هذا مألوفًا؟ بالطبع أنت تتساءل، ما البديل؟ لا تثق، وبدلًا من ذلك، استعن بخبير مستقل قبل البدء بمشروع ما، بحيث يقوم بشكل منتظم (مثاليًا، كل أسبوعين) بمراجعة كل ما يفعله الموظفين الجدد ويقوم بإبلاغك أين وكيف قد تخسر. سيحافظ هذا الخبير على قائمة بالمخاطر المتوقعة، وأنت بدورك ستتخذ الإجراءات الوقائية اللازمة. لا تثق بنا نحن معشر المبرمجين، فإننا أذكياءٌ وكسولون ومدللون، لذا ستخسر دون جدال. ترجمة -وبتصرف- للمقال ‎Trust. Pay. Lose لصاحبه Yegor Bugayenko