دروس تعلمتها بصعوبة: نصائح أساسية قبل البدء بتطوير تطبيقات سطح المكتب باستخدام Electron.js
في هذا المقال، أشارككم خلاصة تجربتي الشخصية والدروس المستفادة من رحلتي في تعلم وتطوير التطبيقات باستخدام Electron.js. آمل أن تساعدكم هذه النصائح على تجنب بعض الأخطاء التي وقعت فيها خلال مسيرتي. تجدر الإشارة إلى أن هذا ليس دليلاً تعليمياً برمجياً، بل هو نقاش حول أهم الاستنتاجات والتوصيات التي أرى أنها حاسمة لأي مطور ينوي استخدام هذه التقنية.
قبل بضعة أشهر، قررت التركيز بشكل أكبر على بناء منتجي الجانبي، والذي أطلقت عليه اسم taggr. جاء الإلهام لبناء هذا التطبيق من الكم الهائل من الصور المخزنة على جهاز الكمبيوتر الخاص بي. فبالنسبة للكثيرين منا ممن يحتفظون بنسخ احتياطية لصورهم، غالباً ما تصبح هذه المجموعات ضخمة ومعقدة لدرجة أن إدارتها تتحول إلى مهمة تتطلب وقتاً وجهداً كبيراً. قد تحتوي مزيج من المجلدات والمجلدات الفرعية على نسخ احتياطية لصور المراسلة الفورية، وصور عالية الدقة من رحلتك إلى بالي، أو صور حفل زفاف عمك، أو حفلة العزوبية للعام الماضي. إن الحفاظ على هذه المجموعات مرتبة ومنظمة هو أمر شاق (صدقوني، لقد حاولت لسنوات). كما أنه من الصعب اكتشاف اللقطات التي تحبها أكثر، والتي تكون مدفونة بعمق داخل المجلدات.
لذا، فإن taggr هو تطبيق سطح مكتب يحل هذه المشكلة. إنه يتيح للمستخدمين استعادة ذكرياتهم مع الحفاظ على خصوصيتهم. أنا أقوم ببناء taggr كتطبيق سطح مكتب متعدد المنصات (cross-platform). هنا سأشارك بعض الأشياء التي تعلمتها حول تطوير التطبيقات متعددة المنصات باستخدام Electron.js والتي تمنيت لو أنني عرفتها منذ البداية. لنبدأ!
خلفية تقنية: لماذا اخترت Electron.js لتطوير تطبيق Taggr؟
قبل أن أقدم استنتاجاتي حول هذه الرحلة المستمرة مع Electron، أود أن أقدم بعض الخلفية عن نفسي وعن متطلبات تطبيق taggr. يأتي كل مطور من خلفية مختلفة، وكذلك متطلبات التطبيقات التي يطورونها. إن وضع سياق للخيارات التي اتخذتها لهذا المشروع قد يساعد المطورين المستقبليين على اختيار الأدوات المناسبة بناءً على احتياجاتهم وخبراتهم (بدلاً من ما هو رائج ومثير للضجة في السوق – GitHub، أنظر إليك).

تطوير JavaScript باختصار.
كما ذكرت سابقاً، تصورت taggr منذ البداية كتطبيق متعدد المنصات. سيقوم التطبيق بإجراء جميع عمليات المعالجة المسبقة ومهام التعلم الآلي المطلوبة على جانب العميل (client-side) بسبب التركيز على الخصوصية. وبصفتي مطوراً وحيداً، أردت أن أتمكن من كتابة تطبيقي مرة واحدة ونشره على أنظمة مختلفة دون أن أفقد صوابي.
من جانبي، أنا مهندس واجهات أمامية (front-end engineer) أعشق الويب وJavaScript. لقد عملت سابقاً مع Java وC#، لكنني أستمتع بالمرونة التي يوفرها الويب وبيئته الغنية. بعد أن اختبرت شخصياً صعوبة استخدام أدوات مثل Eclipse RCP لبناء تطبيقات جانب العميل من قبل، كنت أعلم أنني لا أريد العمل مع تلك التقنيات مرة أخرى. باختصار، تطلبت حزمة التقنيات (stack) الخاصة بي لتطبيق taggr ما يلي:
- يجب أن يوفر دعماً متعدد المنصات، ويفضل أن يكون على مستوى الإطار (
framework). - يجب أن يسمح لي بكتابة الكود مرة واحدة، وتعديله لكل منصة إذا لزم الأمر.
- يجب أن يمكّن الوصول إلى قدرات التعلم الآلي (
machine-learning capabilities)، بغض النظر عن بيئة الاستضافة، دون الحاجة إلى تثبيت بيئات تشغيل (runtimes) محددة. - يجب أن يكون إعداده سهلاً وغير معقد.
- إذا كان ممكناً، يجب أن يستخدم تقنيات الويب. سيكون رائعاً الاستفادة من معرفتي الحالية.
كما ترون، المتطلبات لا تقول: يجب أن أستخدم React مع Redux، observables، وWebSockets. هذه تفاصيل تنفيذية ذات مستوى أدنى، ويجب اتخاذ القرار بشأنها عند الحاجة. اختر الأداة المناسبة للمهمة بدلاً من اختيار حزمة تقنيات (stack) من البداية، متجاهلاً المشكلات المطروحة.
لذا، بعد بحث مكثف على Google، قررت تجربة Electron. لم أكن قد استخدمت هذا الإطار من قبل، لكنني كنت أعلم أن العديد من الشركات تستخدمه بنجاح في منتجات مثل Atom، VS Code، Discord، Signal، وSlack وغيرها. كونه مفتوح المصدر ومتوافقاً بشكل مباشر مع بيئتي JS وNode (تم بناء Electron باستخدام Chromium وNode)، كان Electron.js أداة جذابة للمهمة المطروحة.
لن أخوض في تفاصيل كثيرة بخصوص بقية حزمة التقنيات، حيث قمت بتغيير الأجزاء الأساسية (طبقات الثبات والعرض) بشكل متكرر عند الحاجة، وهذا يقع خارج نطاق هذا المقال. ومع ذلك، أود أن أذكر Tensorflow.js، الذي يمكّن من تشغيل تدريب ونشر نماذج التعلم الآلي مباشرة في المتصفح (باستخدام WebGL) وNode (باستخدام C bindings)، دون تثبيت بيئات تشغيل محددة للتعلم الآلي في المضيف.
نعود إلى Electron – معتقداً أنه مثالي، بدأت المتعة. يكفي الحديث عن الخلفية. دعنا نغوص في الدروس المستفادة.
دروس قيمة تعلمتها من رحلتي مع Electron.js
1. ابدأ صغيراً وتدرج ببطء: تجنب الإفراط في التعقيد منذ البداية
هذا ليس مفهوماً جديداً، لكنه يستحق الذكر بشكل دوري. لمجرد وجود الكثير من المشاريع الأولية الرائعة لـ Electron المتاحة، لا يعني أنه يجب عليك اختيار أحدها على الفور. انتظر.
البطء هو السلاسة، والسلاسة هي السرعة. — مقولة للبحرية الأمريكية
الراحة تأتي مع التعقيد
بينما تتضمن هذه المشاريع الأولية العديد من التكاملات المفيدة (مثل Webpack، Babel، Vue، React، Angular، Express، Jest، Redux)، إلا أنها تحتوي أيضاً على مشكلاتها الخاصة. بصفتي مبتدئاً في Electron، قررت اختيار قالب بسيط يتضمن الأساسيات لـ ‘إنشاء ونشر وتثبيت تطبيقات Electron‘ دون الإضافات والميزات الزائدة. حتى أنني لم أستخدم Webpack في البداية.
أوصي بالبدء بشيء مشابه لـ electron-forge للبدء بسرعة. يمكنك إعداد مخطط تبعياتك وهيكلك فوقه لتعلم أساسيات Electron. عندما تظهر المشكلات (وهي ستظهر بالتأكيد)، ستكون في وضع أفضل إذا قمت ببناء مشروعك الأولي المخصص بدلاً من اختيار مشروع يحتوي على أكثر من 30 سكربت npm وأكثر من 180 تبعية للبدء به. ومع ذلك، بمجرد أن تشعر بالراحة مع أساسيات Electron، لا تتردد في رفع مستوى اللعبة باستخدام Webpack/React/Redux/الإطار الساخن التالي. لقد فعلت ذلك بشكل تدريجي وعند الحاجة. لا تضف قاعدة بيانات في الوقت الفعلي إلى تطبيق قائمة المهام الخاص بك لمجرد أنك قرأت مقالاً رائعاً عنها في مكان ما.
2. هيكلة تطبيقك بعناية: فصل واجهة المستخدم عن منطق الخلفية
استغرق هذا الأمر وقتاً أطول مما أود الاعتراف به لأتقنه بشكل صحيح. في البداية، قد يكون من المغري خلط كود واجهة المستخدم (UI) وكود الخلفية (Backend) (مثل الوصول إلى الملفات، عمليات وحدة المعالجة المركزية المكثفة)، لكن الأمور تتعقد بسرعة كبيرة. مع نمو تطبيقي في الميزات والحجم والتعقيد، أصبح الحفاظ على قاعدة بيانات واحدة متشابكة لـ UI+Backend أكثر تعقيداً وعرضة للأخطاء. كما أن هذا الترابط جعل من الصعب اختبار كل جزء على حدة.
عند بناء تطبيق سطح مكتب يقوم بأكثر من مجرد صفحة ويب مضمنة (الوصول إلى قواعد البيانات، الوصول إلى الملفات، مهام وحدة المعالجة المركزية المكثفة…)، أوصي بتقسيم التطبيق إلى وحدات (modules) وتقليل الترابط بينها. يصبح اختبار الوحدات (Unit testing) سهلاً للغاية، وهناك مسار واضح نحو اختبار التكامل (integration testing) بين الوحدات. بالنسبة لتطبيق taggr، اتبعت بشكل فضفاض الهيكل المقترح هنا.
علاوة على ذلك، هناك الأداء. قد تختلف المتطلبات وتوقعات المستخدمين في هذا الشأن بشكل كبير اعتماداً على التطبيق الذي تقوم ببنائه. لكن إعاقة الخيوط الرئيسية (main thread) أو خيوط العرض (render threads) بمكالمات مكلفة ليست فكرة جيدة أبداً.
3. صمم مع الأخذ في الاعتبار نموذج الترابط (Threading Model)
لن أخوض في تفاصيل كثيرة هنا – أنا أركز بشكل أساسي على ما هو موضح بشكل ممتاز في الوثائق الرسمية. في الحالة المحددة لتطبيق taggr، هناك العديد من العمليات المكثفة لوحدة المعالجة المركزية (CPU)، وحدة معالجة الرسوميات (GPU)، وعمليات الإدخال/الإخراج (IO) التي تستغرق وقتاً طويلاً. عند تنفيذ هذه العمليات في الخيط الرئيسي (main thread) أو خيط العرض (renderer thread) في Electron، ينخفض عدد الإطارات في الثانية (FPS) عن 60، مما يجعل واجهة المستخدم تبدو بطيئة.
يوفر Electron عدة بدائل لتفريغ هذه العمليات من الخيوط الرئيسية والعرض، مثل WebWorkers، Node Worker Threads، أو مثيلات BrowserWindow. لكل منها مزاياه وعيوبه، وستحدد حالة الاستخدام التي تواجهها أي منها هو الأنسب. بغض النظر عن البديل الذي تختاره لتفريغ العمليات من الخيوط الرئيسية والعرض (عند الحاجة)، ضع في اعتبارك كيف ستكون واجهة الاتصال (communication interface). لقد استغرق الأمر مني بعض الوقت للتوصل إلى واجهة كنت راضياً عنها، حيث إنها تؤثر بشكل كبير على كيفية هيكلة تطبيقك وعمله. وجدت أنه من المفيد تجربة مقاربات مختلفة قبل اختيار واحدة. على سبيل المثال، إذا كنت تعتقد أن واجهة تمرير الرسائل (message passing interface) لـ WebWorkers قد لا تكون الأسهل في تصحيح الأخطاء، فجرب comlink.

سبونج بوب يعرف الأفضل.
4. اختبر، اختبر، ثم اختبر! أهمية الاختبارات في Electron.js
أخبار قديمة، أليس كذلك؟ أردت إضافة هذه النقطة كآخر نقطة، بسبب بعض ‘المشكلات’ القصصية التي واجهتها مؤخراً. يرتبط بناء مشروعك الأولي المخصص وارتكاب الأخطاء مبكراً ارتباطاً وثيقاً بالنقطتين الأولى والثانية، مما سيوفر لك وقتاً ثميناً في تصحيح الأخطاء لاحقاً في عملية التطوير. إذا اتبعت توصياتي بتقسيم واجهة المستخدم (UI) والخلفية (Backend) للتطبيق إلى وحدات ذات واجهة نظيفة بين الاثنين، فإن إعداد اختبارات الوحدات (Unit tests) واختبارات التكامل (Integration tests) الآلية يجب أن يكون سهلاً. مع نضوج التطبيق، قد ترغب في إضافة دعم لاختبارات النهاية إلى النهاية (e2e testing) أيضاً.
مشكلة استخراج موقع GPS: اختلافات البيئات بين التطوير والإنتاج
قبل يومين، أثناء تنفيذ ميزة استخراج موقع GPS لتطبيق taggr، بمجرد أن أصبحت اختبارات الوحدات (unit tests) خضراء وعملت الميزة في بيئة التطوير (باستخدام Webpack)، قررت تجربتها في بيئة الإنتاج. بينما عملت الميزة بشكل جيد في التطوير، فشلت فشلاً ذريعاً في الإنتاج. تم قراءة معلومات EXIF من الصور كبيانات ثنائية (binary) وتمت معالجتها بواسطة مكتبة طرف ثالث. بينما تم تحميل المعلومات الثنائية بشكل صحيح في كلتا البيئتين (تم التحقق باستخدام diff)، فشلت مكتبة الطرف الثالث عند تحليل هذه البيانات في بناء الإنتاج (production build).
الحل: اكتشفت أن إعدادات الترميز (encoding settings) في بيئتي التطوير والإنتاج التي تم تعيينها بواسطة Webpack لم تكن متطابقة. تسبب هذا في تحليل البيانات الثنائية كـ UTF-8 في التطوير ولكن ليس في الإنتاج. تم إصلاح المشكلة عن طريق إعداد رؤوس الترميز المناسبة في ملفات HTML التي تم تحميلها بواسطة Electron.
مشكلة الصور التالفة: التعامل مع ملفات JPEG غير الصالحة
عند معالجة الصور والعمل عليها، قد تعتقد أنه إذا كانت صورة JPEG ‘تعمل’ على جهاز الكمبيوتر الخاص بك، فهي JPEG صالحة. هذا خطأ. أثناء العمل مع مكتبة معالجة الصور Node المسماة sharp، تسببت إعادة تحجيم بعض صور JPEG في تعطل التطبيق. بعد الفحص الدقيق، كان السبب صور JPEG غير صحيحة تم إنشاؤها بواسطة برامج Samsung firmware.
الحل: إعداد حدود محسنة للأخطاء في التطبيق (على سبيل المثال، كتل try-catch)، وتعديل وحدة تحليل JPEG، والشك في كل شيء.
ملخص لأهم الممارسات في تطوير Electron.js
تتطور بيئات Node وJavaScript بسرعة مذهلة، مع ظهور العديد من الأدوات القوية التي يتم إنشاؤها ومشاركتها يومياً. تجعل كثرة الخيارات من الصعب اختيار مسار واضح للبدء في بناء تطبيق Electron الرائع الجديد الخاص بك. بغض النظر عن الأطر التي تختارها، أوصي بالتركيز على ما يلي:
- ابدأ صغيراً وأضف التعقيد تدريجياً: تجنب القفز إلى المشاريع الضخمة المعقدة في البداية.
- هيكل تطبيقك بعناية: حافظ على فصل اهتمامات الخلفية (
backend) وواجهة المستخدم (UI) في وحدات مستقلة. - صمم مع الأخذ في الاعتبار نموذج الترابط (
threading model): حتى عند بناء تطبيقات صغيرة، فكر في كيفية إدارة المهام الثقيلة لتجنب تباطؤ واجهة المستخدم. - اختبر واختبر مرة أخرى: لا تتوقف عن الاختبار، فهو يساعد على اكتشاف معظم الأخطاء مبكراً ويوفر الكثير من الصداع لاحقاً.
شكراً لكم على متابعتكم حتى النهاية!
تطبيق taggr هو تطبيق سطح مكتب متعدد المنصات يمكّن المستخدمين من استعادة ذكرياتهم الرقمية مع الحفاظ على خصوصيتهم. ستصدر النسخة التجريبية (Open-alpha) قريباً لأنظمة Linux وWindows وMac OS. لذا، ترقبوا التحديثات على Twitter وInstagram، حيث أنشر تحديثات التطوير، والميزات القادمة، والأخبار.
الخلاصة التقنية
يُعد Electron.js أداة قوية لبناء تطبيقات سطح المكتب باستخدام تقنيات الويب المألوفة، لكن النجاح فيه يتطلب أكثر من مجرد معرفة بـ JavaScript. تتجلى القيمة الحقيقية في التخطيط المسبق، بدءاً من هيكلة التطبيق بشكل معياري، وفصل منطق الواجهة عن الخلفية، وصولاً إلى فهم دقيق لنموذج الترابط لتجنب اختناقات الأداء. كما أن تبني ثقافة الاختبار الشاملة منذ المراحل الأولى للتطوير لا يقل أهمية، فهو الدرع الواقي ضد المشكلات الخفية التي قد تظهر فقط في بيئات الإنتاج، والتي قد تكلف وقتاً وجهداً باهظين لإصلاحها. إن التركيز على هذه الممارسات الأساسية يضمن ليس فقط تطبيقاً وظيفياً، بل أيضاً قابلاً للصيانة والتوسع ويوفر تجربة مستخدم سلسة.