Node.js / الأساسي: بيئة تشغيل Node.js / الدرس 2
الوحدات والحزم وnpm
ابن حدودا واضحة للوحدات، وافهم بيانات الحزمة وتثبيت الاعتماديات بصورة قابلة للتكرار قبل إضافة شيفرة خارجية.
في هذه الصفحة
أهداف الدرس
- اشرح المفاهيم والمفاضلات المتعلقة بموضوع «الوحدات والحزم وnpm».
- توقع المثالين وتحقق من مخرجاتهما وسلوك تنظيف الموارد فيهما.
- شخّص الإخفاقات الثلاثة الشائعة وطبق الحلول المشروحة على مدخلات مشابهة.
فصل المسؤوليات
تجمع الوحدة سلوكا مترابطا خلف صادرات محددة، ويعتمد المستهلك عليها دون المتغيرات المحلية كلها. افصل البرنامج عند تغير المسؤولية: تحليل المدخلات وتطبيق قاعدة وكتابة المخرجات حدود مفيدة. تجنب كثرة الملفات الصغيرة بلا غرض، وكذلك الملف الذي يتولى كل شيء. تُقيّم الوحدات عند تحميلها وتُخزّن عادة بحسب هويتها المحلولة؛ لذلك قد تتحول الآثار الجانبية العليا إلى حالة مشتركة مفاجئة. فضّل تصدير دوال تستقبل الإعدادات صراحة حتى يكون استيرادها آمنا في الاختبارات ولا يبدأ خادما عرضا.
تحديد صيغة الوحدة صراحة
تستخدم الدورة ملفات .mjs لاختيار وحدات ECMAScript صراحة. التزم بـ import وexport، واكتب امتداد الملف في الاستيراد النسبي، واستخدم السابقة node: للوحدات المدمجة. تستخدم CommonJS امتداد .cjs وrequire، ويمكن لـ package.json تحديد نوع ملفات .js. توجد آليات للتوافق، لكن ينبغي ألا يخلط المبتدئ الصيغ عرضا. يجب أن يطابق الاستيراد المسمّى تصديرا مسمّى، وللتصدير الافتراضي صيغة استيراد خاصة. تُحل مسارات الاستيراد نسبة إلى الوحدة المستوردة، بينما تعتمد مسارات ملفات كثيرة على مجلد العمل.
ملاحظة تصدير حقيقي
يصدر المثال الأول دالة ويستدعيها من الملف نفسه؛ فلا يغير التصدير الحساب ولا ينشئ نقطة HTTP تلقائيا. يكتب المثال الثاني وحدة صغيرة في مجلد مؤقت، ثم يستوردها عبر رابط ملف ويحذف المجلد داخل finally. يوضح ذلك حدا حقيقيا بين الوحدات دون الحاجة إلى ملف مرافق غير معروض. تعالج pathToFileURL المحارف ذات المعنى الخاص في الروابط. تعيش وحدات التطبيق عادة في المستودع؛ والتوليد المؤقت هنا أسلوب تعليمي لإبقاء العرض مكتفيا بذاته.
export function subtotal(price, count) { return price * count; }
console.log(subtotal(7, 3));
console.log(import.meta.url.startsWith('file:'));
21
true
يبقى التصدير المسمّى دالة عادية. وتحدد import.meta.url هوية الوحدة كرابط ملف.
فهم npm قبل التثبيت
يسجل package.json بيانات الحزمة والسكربتات ونطاقات الاعتماديات. ويسجل ملف القفل الرسم المحلول للاعتماديات اللازم لتكرار التثبيت. قد يحدّث npm install الحل عند الحاجة، بينما يتطلب npm ci ملف قفل متوافقا ويجري تثبيتا نظيفا مناسبا للأتمتة. لا يثبت أي منهما موثوقية الشيفرة. قد تشغّل الاعتمادية سكربتات دورة حياة وتجلب حزما انتقالية؛ لذا راجع الحاجة إليها وصيانتها وتراخيصها وتنبيهاتها الأمنية. لا تحتاج هذه الدروس إلى اعتماديات خارجية، والوحدات المدمجة جزء من Node ولا تُثبت من حزم مشابهة الاسم.
import { mkdtemp, writeFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { pathToFileURL } from 'node:url';
const dir = await mkdtemp(join(tmpdir(), 'module-'));
try {
const file = join(dir, 'math.mjs');
await writeFile(file, 'export const double = n => n * 2;');
const { double } = await import(pathToFileURL(file).href);
console.log(double(9));
} finally { await rm(dir, { recursive: true, force: true }); }
18
يضاعف التصدير المسمّى المستورد ديناميكيا العدد تسعة. ويجري التنظيف حتى إذا فشل تقييم الوحدة.
تصميم مشروع قابل لإعادة الإنتاج
احفظ بيان الحزمة وملف القفل معا، ووثّق بيئة التشغيل، وعرّف سكربتات مفيدة مثل test وstart. ميّز أدوات التطوير عن الاعتماديات اللازمة للتطبيق المنشور. راجع تغييرات القفل كما تراجع الشيفرة، فقد يستبدل تعديل صغير حزما كثيرة. عند فشل الاستيراد افحص المسار المحلول وصادرات الحزمة قبل إعادة تثبيت كل شيء. تجنب أيضا دورات التهيئة؛ فإذا احتاج ملفان إلى قيم لم يكتمل إعدادها لدى بعضهما، فغالبا توجد مسؤولية ينبغي نقلها إلى وحدة ثالثة.
الأخطاء الشائعة وإصلاحها
ReferenceError: require is not defined
- لماذا يحدث؟
- وُضعت صياغة CommonJS داخل ملف ESM.
- كيف تصلحه؟
- استخدم import أو اعزل CommonJS عمدا داخل .cjs.
- كيف تتجنب تكراره؟
- اختر صيغة وحدات صريحة لشيفرة التطبيق.
ERR_MODULE_NOT_FOUND
- لماذا يحدث؟
- حُذف امتداد المسار النسبي أو أشار إلى مجلد آخر.
- كيف تصلحه؟
- صحح المسار بالنسبة إلى الملف المستورد.
- كيف تتجنب تكراره؟
- اختبر الاستيراد على نظام ملفات يراعي حالة الأحرف في التكامل المستمر.
يرفض npm ci ملف القفل
- لماذا يحدث؟
- يصف البيان وملف القفل حالتين مختلفتين للاعتماديات.
- كيف تصلحه؟
- أعد التوليد عمدا باستخدام npm install وراجع التغييرين.
- كيف تتجنب تكراره؟
- احفظ الملفين في التغيير نفسه.
اختبر فهمك
1. اختر الاستيراد الصحيح لعبارة export const tax = 2 في tax.mjs.
اعرض الحل المشروح
استخدم import { tax } from "./tax.mjs". تختار الأقواس التصدير المسمّى، وتحدد السابقة النسبية ملفا محليا، ويزيل الامتداد الغموض. تطبع tax العدد 2. أما الاستيراد الافتراضي فيطلب عقد تصدير مختلفا.
2. يريد الفريق اعتماديات متطابقة في التكامل المستمر دون تحديث الإصدارات. ماذا يحفظ ويشغّل؟
اعرض الحل المشروح
احفظ package.json وpackage-lock.json، واختر بيئة Node/npm الموثقة ثم شغّل npm ci. إذا فشل بسبب عدم التطابق فأصلح الملفين بتغيير تطوير يخضع للمراجعة، بدلا من إعادة توليد القفل خفية داخل التكامل المستمر.
أهم ما تعلمته
- تجمع الوحدة سلوكا مترابطا خلف صادرات محددة، ويعتمد المستهلك عليها دون المتغيرات المحلية كلها.
- تستخدم الدورة ملفات .mjs لاختيار وحدات ECMAScript صراحة.
- احفظ بيان الحزمة وملف القفل معا، ووثّق بيئة التشغيل، وعرّف سكربتات مفيدة مثل test وstart.