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

إنشاء وحدات برمجية Modules في Node.js


Hassan Hedr

الوحدة البرمجية module في نود Node.js هي أجزاء من شيفرات جافاسكربت منعزلة قابلة للاستخدام في أكثر من تطبيق، حيث يعد الغرض من الوحدة البرمجية هو تقسيم منطقي لوظيفة عمل الشيفرة، فأي ملف أو مجموعة ملفات يمكن اعتبارها وحدة برمجية في حال أمكن استخدام البيانات والتوابع فيها من قبل برامج أخرى خارجية.

ويَنشأ عن التقسيم الوظيفي للشيفرات بتلك الطريقة وحدات برمجية أخرى يمكن إعادة استخدامها في عدة مشاريع أكبر أو مع مطورين آخرين أي أن الوحدات تبنى باعتماد بعضها على بعضها الآخر بطريقة هرمية، ما يؤدي لتطوير برمجيات غير مترابطة سهلة التطوير والتوسع وتوفر درجة من التعقيد أعلى من الوحدات المكونة لها، ما يفتح بابًا للمساهمة بمشاركة تلك الوحدات البرمجية والتي توفر بيانات وتوابع مفيدة مع مجتمع نود، وهي الطريقة التي جرى فيها تحزيم ونشر كل الوحدات البرمجية على مستودع npm، لهذا كمبرمج نود من الضروري أن تتعلم طريقة إنشاء الوحدات البرمجية.

أخذنا في المقال السابق: إدارة الوحدات البرمجية في Node.js باستخدام npm وملف package.json فكرة أساسية عن ماهية الوحدات في نود وتعرفنا على مدير حزم نود npm وأهمية الملف package.json لإدارة الوحدات التي يعتمد عليها مشروعنا، وسنتعلم في هذا المقال كيفية إنشاء وحدة برمجية وظيفتها اقتراح الألوان على مطور الويب لاستخدامها في التصميم، فسنخزن الألوان المتاحة في مصفوفة داخل الوحدة وسنوفر تابعًا للمستخدمين يختار لهم إحداها عشوائيًا، بعدها سنتعلم عدة طرق يمكننا بها استيراد تلك الوحدة واستخدامها ضمن تطبيقات ومشاريع نود الأخرى.

يلزمك في هذا المقال معرفةً باستخدام حلقة REPL التي يوفرها نود، حيث سنستخدمها لاختبار الوحدة التي سنطورها، لذا يفضل الاطلاع على مقال استخدام الوضع التفاعلي REPL في Node.js من هذه السلسلة "دليل تعلم Node.js" إن لم تطلع عليه مسبقًا.

إنشاء وحدة برمجية في Node.js

سنشرح في هذه الفقرة طريقة إنشاء وحدة برمجية جديدة في نود، حيث ستحتوي الوحدة التي سنطورها على مصفوفة من الألوان وتابع يختار إحداها عشوائيًا ويعيده للمستخدم، وسنستخدم في ذلك خاصية التصدير exports في نود لإتاحة التابع والمصفوفة للبرامج الخارجية.

بدايةً، لنعتمد هيكلية معينة للبيانات التي سنخزنها ضمن الوحدة، حيث سنمثل كل لون بكائن سيحوي الخاصية name التي تعبر عن اسم ذلك اللون بصيغة مقروءة، والخاصية code وهي سلسلة نصية تمثل ترميز ذلك اللون لاستخدامه في HTML، والصيغة المعتمدة لتمثيل الألوان في HTML هي ستة أرقام بالترميز الست عشري.

نبدأ باختيار بعض تلك الألوان التي ستوفرها وحدتنا البرمجية ونضعها في مصفوفة بالاسم allColors وليكن عددها ستة ألوان كما ستحتوي وحدتنا على تابع بالاسم getRandomColor()‎ لاختيار لون عشوائي من تلك المصفوفة وإعادته.

ننتقل إلى الخطوات العملية، ننشئ مجلدًا جديدًا لاحتواء المشروع نسميه colors وننتقل إليه كالتالي:

mkdir colors
cd colors

نُهيئ ملف الحزمة package.json ضمن مجلد المشروع لتتمكن باقي البرامج من استيراده واستخدامه لاحقًا كالتالي:

npm init -y

يمكن باستخدام الخيار ‎-y تخطي الأسئلة التي تظهر عادةً عند تخصيص محتوى ملف الحزمة package.json، وفي حال كنا ننوي نشر تلك الوحدة يجب تخصيص القيم داخل ذلك الملف كما شرحنا في المقال السابق.

سنحصل بعد تنفيذ الأمر على الخرج التالي:

{
  "name": "colors",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

الآن نُنشئ ملف جافاسكربت جديد سيحوي على شيفرة الوحدة البرمجية وسيكون المدخل لها، ونفتحه باستخدام أي محرر نصوص أو شيفرات برمجية، مثلًا باستخدام nano كالتالي:

nano index.js

نبدأ بتعريف الصنف Color والذي سنمرر له اسم اللون وترميزه الذي سيستخدم ضمن HTML كالتالي:

class Color {
  constructor(name, code) {
    this.name = name;
    this.code = code;
  }
}

بعد تعريف هيكلية البيانات التي ستمثل اللون، نُنشئ من ذلك الصنف بعض الكائنات ونخزنها ضمن مصفوفة الألوان كالتالي:

class Color {
  constructor(name, code) {
    this.name = name;
    this.code = code;
  }
}

const allColors = [
  new Color('brightred', '#E74C3C'),
  new Color('soothingpurple', '#9B59B6'),
  new Color('skyblue', '#5DADE2'),
  new Color('leafygreen', '#48C9B0'),
  new Color('sunkissedyellow', '#F4D03F'),
  new Color('groovygray', '#D7DBDD'),
];

بعدها نُعرّف الدالة التي ستجلب لنا لونًا عشوائيًا عند استدعاءها، لتصبح الشيفرة بالكامل كالتالي:

class Color {
  constructor(name, code) {
    this.name = name;
    this.code = code;
  }
}

const allColors = [
  new Color('brightred', '#E74C3C'),
  new Color('soothingpurple', '#9B59B6'),
  new Color('skyblue', '#5DADE2'),
  new Color('leafygreen', '#48C9B0'),
  new Color('sunkissedyellow', '#F4D03F'),
  new Color('groovygray', '#D7DBDD'),
];

exports.getRandomColor = () => {
  return allColors[Math.floor(Math.random() * allColors.length)];
}

exports.allColors = allColors;

تشير الكلمة المفتاحية exports إلى كائن عام توفره نود لكل وحدة برمجية، حيث ستكون كل الكائنات والتوابع المُعرّفة كخصائص ضمن ذلك الكائن متاحة عند استيراد هذه الوحدة واستخدامها من قبل الوحدات البرمجية الأخرى، ولذلك لاحظ كيف عرّفنا التابع getRandomColor()‎ مباشرةً كخاصية ضمن الكائن exports، وبعدها أضفنا الخاصية allColors ضمن ذلك الكائن التي تشير قيمتها إلى مصفوفة الألوان allColors المُنشئة سابقًا.

بناءً على ما سبق، ستتمكن أي وحدة برمجية أخرى بعد استيرادها لهذه الوحدة من الوصول إلى التابع getRandomColor()‎ والمصفوفة allColors واستخدامهما، وبهذا نكون قد أنشأنا وحدة برمجية توفر للوحدات الأخرى مصفوفة من الألوان وتابعًا يختار إحداها عشوائيًا لتتمكن من استخدامهما.

سنستخدم في الفقرة التالية الوحدة التي طورناها ضمن تطبيق آخر لنفهم فائدة الكائن export أكثر.

اختبار الوحدة البرمجية باستخدام REPL

يفضل قبل البدء باستخدام هذه الوحدة اختبارها أولًا للتأكد من صحة عملها، فسنستخدم في هذه الفقرة الوضع التفاعلي REPL لتحميل الوحدة colors واستدعاء التابع getRandomColor()‎ التي توفره لنختبر صحة عمله.

نبدأ أولًا جلسة REPL جديدة ضمن مجلد المشروع الحاوي على الملف index.js كالتالي:

node

نلاحظ ظهور الرمز ‎>‎ في بداية السطر عند الدخول إلى وضع REPL ويمكن الآن إدخال أوامر وشيفرات جافاسكربت لتنفيذها فورًا كما يلي:

colors = require('./index');

سيُحمّل التابع ‎require()‎ الوحدة colors وتحديدًا ملف المدخل entry point لها بعد الضغط على زر الإدخال ENTER لتنفيذ السطر السابق ونلاحظ ظهور الخرج التالي:

{
  getRandomColor: [Function],
  allColors: [
    Color { name: 'brightred', code: '#E74C3C' },
    Color { name: 'soothingpurple', code: '#9B59B6' },
    Color { name: 'skyblue', code: '#5DADE2' },
    Color { name: 'leafygreen', code: '#48C9B0' },
    Color { name: 'sunkissedyellow', code: '#F4D03F' },
    Color { name: 'groovygray', code: '#D7DBDD' }
  ]
}

ظهرت لنا قيمة الوحدة البرمجية colors التي تم استيرادها، وهي عبارة عما صدّرناه منها، حيث يعيد التابع require عند استدعائه قيمة الكائن exports من الوحدة المستوردة وهي colors في حالتنا، والذي أضفنا إليه داخلها تابعًا بالاسم getRandomColor()‎ وخاصيةً بالاسم allColors، وهو ما ظهر ضمن الخرج، ويمكننا الآن اختبار التابع getRandomColor()‎ كالتالي:

colors.getRandomColor();

نلاحظ كيف أعاد لنا لونًا عشوائيًا:

Color { name: 'groovygray', code: '#D7DBDD' }

سيظهر لك لونًا مختلفًا عند تنفيذ الأمر في كل مرة، وذلك لأن الاختيار عشوائي، والآن بعد إتمام الاختبار يمكننا الخروج من جلسة REPL بتنفيذ أمر الخروج التالي الذي سيعيدنا إلى سطر الأوامر:

.exit

تحققنا في هذه الفقرة من صحة عمل الوحدة البرمجية التي أنشأناها سابقًا وذلك باستخدام REPL، وسنطبق في الفقرة التالية نفس الخطوات لاستيراد واستخدام الوحدة لكن هذه المرة ضمن مشروع حقيقي.

تثبيت وحدة منشأة محليًا كاعتمادية

استوردنا الوحدة البرمجية أثناء اختبارها ضمن صدفة REPL في الفقرة السابقة بذكر المسار النسبي لها، أي ذكرنا مسار مجلد الملف index.js بدءًا من المسار الحالي، ولا تُعتمد طريقة الاستيراد هذه إلا في حالات خاصة إذ تُستورد الوحدات بذكر أسمائها لتجنب المشاكل التي قد تحدث عند نقل مجلدات المشاريع التي نعمل عليها أو تعديل مساراتها، وسنثبت في هذه الفقرة الوحدة البرمجية colors باستخدام أمر التثبيت install من npm، لذلك ننشئ بدايةً وحدة برمجية جديدة خارج مجلد الوحدة colors، بالرجوع إلى المجلد الأب له وإنشاء مجلد جديد كالتالي:

cd ..
mkdir really-large-application

وننتقل لمجلد المشروع الجديد:

cd really-large-application

ثم نُهيئ كما تعلمنا سابقًا ملف الحزمة package.json لهذا المشروع بتنفيذ الأمر:

npm init -y

سيتم توليد ملف package.json بالمحتوى التالي:

{
  "name": "really-large-application",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

نثبت الآن الوحدة colors كالتالي:

npm install --save ../colors
.

بذلك نكون قد ثبتنا الوحدة colors ضمن المشروع الجديد، ونعاين الآن الملف package.json لنرى كيف تُحفَظ الاعتماديات المحلية فيه:

nano package.json

نُلاحظ إضافة سطر جديد ضمن الخاصية dependencies يُذكر فيه اسم الوحدة ومسارها النسبي:

{
  "name": "really-large-application",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "colors": "file:../colors"
  }
}

حيث نُسخِت الوحدة colors إلى مجلد الاعتماديات node_modules للمشروع الجديد، ويمكننا التأكد من ذلك باستعراض محتوياته باستخدام الأمر التالي:

ls node_modules

يظهر اسم مجلد الاعتمادية موجودًا ضمنه:

colors

يمكن الآن استخدام تلك الوحدة ضمن هذا المشروع، لذلك نُنشئ ملف جافاسكربت جديد:

nano index.js

ونستورد بدايةً الوحدة colors ونستخدم منها الدالة getRandomColor()‎ لاختيار لون عشوائي، ثم نطبع رسالة إلى الطرفية تخبر المستخدم باللون الذي يمكنه استخدامه، لذا نكتب داخل الملف index.js الشيفرة التالية:

const colors = require('colors');

const chosenColor = colors.getRandomColor();
console.log(`You should use ${chosenColor.name} on your website. It's HTML code is ${chosenColor.code}`);

نحفظ الملف ونخرج منه، والآن عند تنفيذ هذا البرنامج سيخبرنا بلون عشوائي يمكننا استخدامه:

node index.js

نحصل على خرج مشابه للتالي:

You should use leafygreen on your website. It's HTML code is #48C9B0

بهذا نكون ثبتنا الوحدة البرمجية colors ضمن المشروع ويمكننا التعامل معها وإدارتها كأي اعتمادية أخرى ضمن المشروع، لكن يجب الانتباه أنه في كل مرة نعدل شيفرة الوحدة colors مثلًا لإضافة ألوان جديدة يجب علينا حينها تنفيذ أمر الترقية npm update ضمن مشروع التطبيق لتحديث الاعتمادية واستخدام المزايا الجديدة، ولتجنب تكرار تنفيذ ذلك عند كل تعديل في الفقرة التالية سنستخدم الوحدة colors بطريقة مختلفة تمكننا من استخدام أحدث إصدار لها ضمن المشاريع المعتمدة عليها أثناء العمل عليها وتطويرها.

ربط وحدة محلية

قد نمر في حالة نعمل فيها على تطوير وحدة برمجية محليًا ونستخدمها في الوقت نفسه ضمن مشروع آخر، وسيصعب آنذاك ترقيتها باستمرار ضمن المشروع كما أشرنا سابقًا، والحل يكمن في ربط الوحدات البرمجية بدلًا من تثبيتها لاستخدامها مباشرة وهي قيد التطوير والبناء.

سنتعلم ذلك في هذه الفقرة عن طريق ربط الوحدة colors ضمن التطبيق الذي يستخدمها، وسنختبر الربط بإجراء تعديلات على الوحدة colors ونتحقق من التحديث الآني لتلك التعديلات ضمن اعتمادية التطبيق دون الحاجة للترقية أو لتثبيت الوحدة من جديد، لذلك نزيل بدايةً تثبيت الوحدة من التطبيق بتنفيذ الأمر التالي:

npm un colors

يربط مدير الحزم npm الوحدات البرمجية مع بعضها باستخدام الوصلات الرمزية symbolic links والتي تمثل مؤشرًا يشير إلى ملف أو مجلد ما ضمن نظام الملفات، ويُنفذ الربط هذا على مرحلتين:

  1. إنشاء وصلة أو رابط عام global link للوحدة حيث يُنشئ npm وصلة رمزية بين مجلد الوحدة البرمجية ومجلد الاعتماديات العام node_modules الذي تُثبَّت فيه كل الحزم العامة على مستوى النظام كله، أي الحزم المُثبَّتة باستخدام الخيار ‎-g.
  2. إنشاء وصلة محلية local link بحيث يُنشئ npm وصلة رمزية بين المشروع المحلي وبين الرابط العام للوحدة البرمجية المراد استخدامها فيه.

ننشئ الرابط العام بالدخول إلى مجلد الوحدة colors واستخدام الأمر link كالتالي:

cd ../colors
sudo npm link

سيظهر لنا خرج كالتالي:

/usr/local/lib/node_modules/colors -> /home/hassan/colors

أنشِئت بذلك وصلة رمزية في مجلد node_modules العام تشير إلى مجلد الوحدة colors، والآن نعود إلى مجلد المشروع really-large-application لربط الوحدة ضمنه كالتالي:

cd ../really-large-application
sudo npm link colors

سنلاحظ ظهور خرج مشابه للتالي:

/home/hassan/really-large-application/node_modules/colors -> /usr/local/lib/node_modules/colors -> /home/hassan/colors

ملاحظة: يمكن اختصار الأمر link بكتابة ln بدلًا منه، ليصبح أمر الربط كالتالي npm ln colors وسنحصل على نفس النتيجة.

وكما يُظهر خرج أمر الربط السابق فقد أنشِئت وصلة رمزية في مجلد node_modules للمشروع really-large-application تشير إلى الوصلة الرمزية لمجلد الوحدة colors الموجودة في مجلد node_modules العام على مستوى النظام، والتي بدورها تشير إلى مجلد الوحدة colors الفعلي، وبهذا تكون عملية الربط اكتملت ويمكن تشغيل ملف المشروع للتأكد بأن الربط صحيح ولا زال المشروع يعمل كما هو:

node index.js

نحصل على خرج مشابه للتالي:

OutputYou should use sunkissedyellow on your website. It's HTML code is #F4D03F

نلاحظ عدم تأثر المشروع ولا زال يعمل كما هو، والآن لنختبر ما إذا كانت التعديلات على الوحدة التي طورناها ستنعكس مباشرة ضمن المشروع الذي يستخدمها، لذلك نفتح الملف index.js الخاص بالوحدة colors ضمن محرر النصوص:

cd ../colors
nano index.js

ونضيف مثلًا دالةً جديدةً مهمتها جلب درجة من درجات اللون الأزرق من الألوان المتوفرة، ولا تحتاج لتمرير معاملات وستعيد العنصر الثالث من مصفوفة الألوان المحلية allColors مباشرةً والذي هو من درجات اللون الأزرق، لذا نضيف الأسطر الأخيرة إلى الملف كالتالي:

class Color {
  constructor(name, code) {
    this.name = name;
    this.code = code;
  }
}

const allColors = [
  new Color('brightred', '#E74C3C'),
  new Color('soothingpurple', '#9B59B6'),
  new Color('skyblue', '#5DADE2'),
  new Color('leafygreen', '#48C9B0'),
  new Color('sunkissedyellow', '#F4D03F'),
  new Color('groovygray', '#D7DBDD'),
];

exports.getRandomColor = () => {
        return allColors[Math.floor(Math.random() * allColors.length)];
        }

exports.allColors = allColors;

exports.getBlue = () => {
  return allColors[2];
}

نحفظ الملف ونخرج منه، ونفتح ملف index.js ضمن مجلد المشروع really-large-application:

cd ../really-large-application
nano index.js

ونستخدم داخله الدالة الجديدة ‎getBlue()‎ المضافة إلى الوحدة ونطبع إلى الطرفية جملة تحوي خصائص ذلك اللون كالتالي:

const colors = require('colors');

const chosenColor = colors.getRandomColor();
console.log(`You should use ${chosenColor.name} on your website. It's HTML code is ${chosenColor.code}`);

const favoriteColor = colors.getBlue();
console.log(`My favorite color is ${favoriteColor.name}/${favoriteColor.code}, btw`);

نحفظ الملف ونخرج منه، وبذلك يصبح المشروع يستخدم التابع الجديد الذي أنشأناه ‎getBlue()‎، والآن ننفذ البرنامج ونرى النتيجة:

node index.js

سنحصل على خرج مشابه لما يلي:

OutputYou should use brightred on your website. It's HTML code is #E74C3C
My favorite color is skyblue/#5DADE2, btw

نلاحظ كيف تمكنا من استخدام آخر التعديلات التي أجريناها ضمن الوحدة colors مباشرةً دون الحاجة لتنفيذ أمر الترقية npm update لتلك الوحدة، حيث يسهل ذلك عملية تطوير الوحدات البرمجية ويخفف من تكرار تنفيذ نفس الأوامر بكثرة.

حاول التفكير دومًا عند تطوير التطبيقات الكبيرة والمعقدة نسبيًا كيف يمكن تجميع الشيفرات التي يتم تطويرها ضمن وحدات برمجية منفصلة تعتمد على بعضها، ويمكن إعادة استخدامها في عدة مشاريع، أما في حال كانت الوحدة البرمجية تستخدم فقط ضمن برنامج واحد عندها يفضل إبقاءها ضمن نفس مجلد المشروع ذاك وربطها عن طريق المسار النسبي لها، وأما في حال التخطيط لمشاركة الوحدة بشكل منفصل لاحقًا أو في استخدامها في مشروع مختلف عن المشروع الحالي فانظر إن كان الربط أنسب لحالتك أم التثبيت كما تعلمت إلى الآن، إذ الفائدة الأكبر من ربط الوحدات قيد التطوير استخدام أحدث إصدار منها دومًا دون الحالة لترقيتها كل حين، وإلا فمن الأسهل تثبيتها باستخدام الأمر npm install.

ختامًا

غصنا عميقًا في هذا المقال في وحدات نود والتي هي مجموعة من التوابع والكائنات في جافاسكربت خصوصًا في كيفية استخدامها من قبل البرامج الأخرى، فأنشأنا وحدة برمجية وحددنا داخلها بعض الدوال والكائنات كخصائص للكائن exports لإتاحتها للاستخدام من قبل التطبيقات الخارجية، واستوردنا تلك الوحدة إلى برنامج جديد واستخدمناها ضمنه.

أصبح بإمكانك الآن استخراج بعض المكونات من البرامج التي تعمل عليها إلى وحدات برمجية منفصلة بتحديد ما تود إعادة استخدامها ضمنها، وبذلك تجمع البيانات والتوابع الخاصة بها معًا ضمن وحدة منفصلة وتعزلها عن باقي التطبيقات مما يمكنك من إعادة استخدامها وتطويرها وحتى مشاركتها مع الآخرين، وكلما كتبت وحدات أكثر وصقلت مهارتك البرمجية فيها، اكتسبت خبرة كبيرة تخولك من تطوير برامج نود عالية الجودة.

ترجمة -وبتصرف- للمقال How To Create a Node.js Module.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...