الوحدات: وحدات CommonJS
[مستقر: 2 - مستقر]
مستقر: 2 استقرار: 2 - مستقر
تُعد وحدات CommonJS هي الطريقة الأصلية لتغليف تعليمات برمجة JavaScript لـ Node.js. يدعم Node.js أيضًا معيار وحدات ECMAScript الذي تستخدمه المتصفحات وبيئات تشغيل JavaScript الأخرى.
في Node.js، يُعامل كل ملف على أنه وحدة منفصلة. على سبيل المثال، ضع في اعتبارك ملفًا باسم foo.js
:
const circle = require('./circle.js')
console.log(`مساحة دائرة نصف قطرها 4 هي ${circle.area(4)}`)
في السطر الأول، يقوم foo.js
بتحميل الوحدة circle.js
الموجودة في نفس الدليل مثل foo.js
.
فيما يلي محتويات circle.js
:
const { PI } = Math
exports.area = r => PI * r ** 2
exports.circumference = r => 2 * PI * r
قامت الوحدة circle.js
بتصدير الدالتين area()
و circumference()
. يتم إضافة الدوال والكائنات إلى الجذر الأساسي لوحدة ما من خلال تحديد خصائص إضافية على كائن exports
الخاص.
ستكون المتغيرات المحلية للوحدة خاصة، لأن Node.js يلف الوحدة في دالة (انظر لفاف الوحدة). في هذا المثال، المتغير PI
خاص بـ circle.js
.
يمكن تعيين قيمة جديدة لخاصية module.exports
(مثل دالة أو كائن).
في التعليمات البرمجية التالية، يستخدم bar.js
وحدة square
، التي تصدر فئة Square:
const Square = require('./square.js')
const mySquare = new Square(2)
console.log(`مساحة mySquare هي ${mySquare.area()}`)
تم تعريف وحدة square
في square.js
:
// إنشاء تعيين لـ exports لن يغير الوحدة، يجب استخدام module.exports
module.exports = class Square {
constructor(width) {
this.width = width
}
area() {
return this.width ** 2
}
}
تم تنفيذ نظام وحدة CommonJS في وحدة النواة module
.
التمكين
يحتوي Node.js على نظامي وحدات: وحدات CommonJS ووحدات ECMAScript.
بشكل افتراضي، سيعامل Node.js ما يلي على أنه وحدات CommonJS:
- الملفات ذات الامتداد
.cjs
؛ - الملفات ذات الامتداد
.js
عندما يحتوي أقرب ملفpackage.json
رئيسي على حقل من أعلى مستوى"type"
بقيمة"commonjs"
. - الملفات ذات الامتداد
.js
أو بدون امتداد، عندما لا يحتوي أقرب ملفpackage.json
رئيسي على حقل من أعلى مستوى"type"
أو لا يوجد ملفpackage.json
في أي مجلد رئيسي؛ ما لم يحتوي الملف على بناء جملة يسبب أخطاء ما لم يتم تقييمه كوحدة ES. يجب على مؤلفي الحزم تضمين حقل"type"
، حتى في الحزم التي تكون جميع مصادرها من CommonJS. إن التحديد الصريح لنوع الحزمة سيجعل الأمور أسهل لأدوات البناء وأدوات التحميل لتحديد كيفية تفسير الملفات الموجودة في الحزمة. - الملفات ذات الامتداد الذي ليس
.mjs
أو.cjs
أو.json
أو.node
أو.js
(عندما يحتوي أقرب ملفpackage.json
رئيسي على حقل من أعلى مستوى"type"
بقيمة"module"
، سيتم التعرف على هذه الملفات كوحدات CommonJS فقط إذا تم تضمينها عبرrequire()
، وليس عند استخدامها كنقطة دخول سطر الأوامر للبرنامج).
راجع تحديد نظام الوحدة لمزيد من التفاصيل.
استدعاء require()
يستخدم دائمًا محمل وحدة CommonJS. استدعاء import()
يستخدم دائمًا محمل وحدة ECMAScript.
الوصول إلى الوحدة النمطية الرئيسية
عندما يتم تشغيل ملف مباشرةً من Node.js، يتم تعيين require.main
إلى module
الخاص به. وهذا يعني أنه من الممكن تحديد ما إذا كان الملف قد تم تشغيله مباشرةً من خلال اختبار require.main === module
.
بالنسبة لملف foo.js
، ستكون هذه القيمة true
إذا تم تشغيله عبر node foo.js
، ولكن false
إذا تم تشغيله بواسطة require('./foo')
.
عندما لا تكون نقطة الدخول وحدة نمطية CommonJS، يكون require.main
مساويًا لـ undefined
، وتصبح الوحدة النمطية الرئيسية بعيدة المنال.
نصائح حول إدارة الحزم
تم تصميم دلالات دالة Node.js require()
لتكون عامة بما يكفي لدعم هياكل الدلائل المعقولة. من المأمول أن تجد برامج إدارة الحزم مثل dpkg
و rpm
و npm
أنه من الممكن إنشاء حزم أصلية من وحدات Node.js بدون تعديل.
فيما يلي، نقدم هيكلًا مقترحًا للدليل قد يعمل:
لنقل أننا أردنا أن يحتوي المجلد في /usr/lib/node/\<some-package\>/\<some-version\>
على محتويات إصدار محدد من حزمة.
يمكن للحزم أن تعتمد على بعضها البعض. من أجل تثبيت الحزمة foo
، قد يكون من الضروري تثبيت إصدار محدد من الحزمة bar
. قد تحتوي الحزمة bar
نفسها على تبعيات، وفي بعض الحالات، قد تتضارب هذه التبعيات أو تشكل تبعيات دورية.
بسبب قيام Node.js بالبحث عن realpath
لأي وحدات تقوم بتحميلها (أي أنها تحل الارتباطات الرمزية) ثم البحث عن تبعياتها في مجلدات node_modules
، يمكن حل هذا الموقف باستخدام البنية التالية:
/usr/lib/node/foo/1.2.3/
: محتويات حزمةfoo
، الإصدار 1.2.3./usr/lib/node/bar/4.3.2/
: محتويات حزمةbar
التي تعتمد عليهاfoo
./usr/lib/node/foo/1.2.3/node_modules/bar
: رابط رمزي إلى/usr/lib/node/bar/4.3.2/
./usr/lib/node/bar/4.3.2/node_modules/*
: روابط رمزية إلى الحزم التي تعتمد عليهاbar
.
وبالتالي، حتى إذا تم مواجهة دورة، أو إذا كانت هناك تعارضات في التبعيات، ستتمكن كل وحدة نمطية من الحصول على إصدار من تبعيتها يمكنها استخدامه.
عندما يقوم الرمز الموجود في حزمة foo
بتنفيذ require('bar')
، سيحصل على الإصدار الذي تم إنشاء رابط رمزي له في /usr/lib/node/foo/1.2.3/node_modules/bar
. ثم، عندما يقوم الرمز الموجود في حزمة bar
بالاتصال بـ require('quux')
، سيحصل على الإصدار الذي تم إنشاء رابط رمزي له في /usr/lib/node/bar/4.3.2/node_modules/quux
.
علاوة على ذلك، لجعل عملية البحث عن الوحدات النمطية أكثر مثالية، بدلاً من وضع الحزم مباشرةً في /usr/lib/node
، يمكننا وضعها في /usr/lib/node_modules/\<name\>/\<version\>
. عندئذٍ لن يكلف Node.js نفسه عناء البحث عن التبعيات المفقودة في /usr/node_modules
أو /node_modules
.
من أجل جعل الوحدات النمطية متاحة لـ Node.js REPL، قد يكون من المفيد أيضًا إضافة مجلد /usr/lib/node_modules
إلى متغير البيئة $NODE_PATH
. نظرًا لأن عمليات البحث عن الوحدات النمطية باستخدام مجلدات node_modules
كلها نسبية، وبناءً على المسار الحقيقي للملفات التي تقوم بالاتصالات بـ require()
، يمكن أن تكون الحزم نفسها في أي مكان.
تحميل وحدات ECMAScript باستخدام require()
[السجل]
الإصدار | التغييرات |
---|---|
v23.5.0 | لم تعد هذه الميزة تُصدر تحذيرًا تجريبيًا افتراضيًا، على الرغم من أنه لا يزال بإمكان التحذير أن يُصدره --trace-require-module . |
v23.0.0 | لم تعد هذه الميزة خلف علم سطر الأوامر --experimental-require-module . |
v23.0.0 | دعم تصدير التوافق 'module.exports' في require(esm) . |
v22.0.0، v20.17.0 | تمت الإضافة في: v22.0.0، v20.17.0 |
[مستقر: 1 - تجريبي]
مستقر: 1 استقرار: 1.2 - إصدار مرشح
الامتداد .mjs
محجوز لـ وحدات ECMAScript. راجع قسم تحديد نظام الوحدة لمزيد من المعلومات حول الملفات التي يتم تحليلها كوحدات ECMAScript.
يدعم require()
فقط تحميل وحدات ECMAScript التي تلبي المتطلبات التالية:
- الوحدة متزامنة بالكامل (لا تحتوي على
await
على مستوى أعلى)؛ و - يتم استيفاء أحد هذه الشروط:
إذا كانت وحدة ES التي يتم تحميلها تلبي المتطلبات، فيمكن لـ require()
تحميلها وإرجاع كائن مساحة اسم الوحدة. في هذه الحالة، يكون مشابهًا لـ import()
الديناميكي ولكنه يتم تشغيله بشكل متزامن ويعيد كائن مساحة الاسم مباشرةً.
مع وحدات ES التالية:
// distance.mjs
export function distance(a, b) {
return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
}
// point.mjs
export default class Point {
constructor(x, y) {
this.x = x
this.y = y
}
}
يمكن لوحدة CommonJS تحميلها باستخدام require()
:
const distance = require('./distance.mjs')
console.log(distance)
// [Module: null prototype] {
// distance: [Function: distance]
// }
const point = require('./point.mjs')
console.log(point)
// [Module: null prototype] {
// default: [class Point],
// __esModule: true,
// }
للتوافق مع الأدوات الموجودة التي تحول وحدات ES إلى CommonJS، والتي يمكنها بعد ذلك تحميل وحدات ES الحقيقية من خلال require()
، ستحتوي مساحة الاسم المُرجعة على خاصية __esModule: true
إذا كان لديها تصدير افتراضي بحيث يمكن للرمز المُستهلك الذي تم إنشاؤه بواسطة الأدوات التعرف على الصادرات الافتراضية في وحدات ES الحقيقية. إذا كانت مساحة الاسم تُعرّف بالفعل __esModule
، فلن تتم إضافة ذلك. هذه الخاصية تجريبية ويمكن أن تتغير في المستقبل. يجب استخدامها فقط من قبل الأدوات التي تحول وحدات ES إلى وحدات CommonJS، باتباع اتفاقيات النظام الإيكولوجي الحالية. يجب تجنب الاعتماد عليها من قبل الرمز المُؤلف مباشرةً في CommonJS.
عندما تحتوي وحدة ES على كل من الصادرات المسمّاة والتصدير الافتراضي، تكون النتيجة التي تُرجعها require()
هي كائن مساحة اسم الوحدة، والذي يضع التصدير الافتراضي في الخاصية .default
، على غرار النتائج التي تُرجعها import()
. لتخصيص ما يجب أن تُعيده require(esm)
مباشرةً، يمكن لوحدة ES تصدير القيمة المطلوبة باستخدام اسم السلسلة "module.exports"
.
// point.mjs
export default class Point {
constructor(x, y) {
this.x = x
this.y = y
}
}
// `distance` ضائعة على مستهلكي CommonJS لهذه الوحدة، ما لم تُضاف إلى `Point` كخاصية ثابتة.
export function distance(a, b) {
return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
}
export { Point as 'module.exports' }
const Point = require('./point.mjs')
console.log(Point) // [class Point]
// الصادرات المسمّاة ضائعة عند استخدام 'module.exports'
const { distance } = require('./point.mjs')
console.log(distance) // undefined
لاحظ في المثال أعلاه، عند استخدام اسم التصدير module.exports
، ستضيع الصادرات المسمّاة على مستهلكي CommonJS. للسماح لمستهلكي CommonJS بالاستمرار في الوصول إلى الصادرات المسمّاة، يمكن للوحدة التأكد من أن التصدير الافتراضي هو كائن مع الصادرات المسمّاة المرفقة به كخصائص. على سبيل المثال، مع المثال أعلاه، يمكن إرفاق distance
بالتصدير الافتراضي، فئة Point
، كطريقة ثابتة.
export function distance(a, b) {
return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
}
export default class Point {
constructor(x, y) {
this.x = x
this.y = y
}
static distance = distance
}
export { Point as 'module.exports' }
const Point = require('./point.mjs')
console.log(Point) // [class Point]
const { distance } = require('./point.mjs')
console.log(distance) // [Function: distance]
إذا كانت الوحدة التي يتم require()
لها تحتوي على await
على مستوى أعلى، أو الرسم البياني للوحدة الذي تقوم import
به يحتوي على await
على مستوى أعلى، فسيتم طرح ERR_REQUIRE_ASYNC_MODULE
. في هذه الحالة، يجب على المستخدمين تحميل الوحدة غير المتزامنة باستخدام import()
.
إذا تم تمكين --experimental-print-required-tla
، فبدلاً من طرح ERR_REQUIRE_ASYNC_MODULE
قبل التقييم، سيقوم Node.js بتقييم الوحدة، ويحاول تحديد عمليات الانتظار على مستوى الأعلى، ويطبع موقعها لمساعدة المستخدمين على إصلاحها.
يُعد دعم تحميل وحدات ES باستخدام require()
تجريبيًا حاليًا ويمكن تعطيله باستخدام --no-experimental-require-module
. لطباعة مكان استخدام هذه الميزة، استخدم --trace-require-module
.
يمكن اكتشاف هذه الميزة عن طريق التحقق مما إذا كان process.features.require_module
هو true
.
الكل معًا
للحصول على اسم الملف الدقيق الذي سيتم تحميله عند استدعاء require()
، استخدم دالة require.resolve()
.
جمعًا لكل ما سبق، إليك الخوارزمية عالية المستوى بالرمز الكاذب لما تفعله require()
:
require(X) من الوحدة النمطية في المسار Y
1. إذا كان X وحدة أساسية،
أ. أعد الوحدة الأساسية
ب. توقف
2. إذا بدأ X بـ '/'
أ. ضع Y ليكون جذر نظام الملفات
3. إذا بدأ X بـ './' أو '/' أو '../'
أ. LOAD_AS_FILE(Y + X)
ب. LOAD_AS_DIRECTORY(Y + X)
ج. ألقِ "لم يتم العثور عليه"
4. إذا بدأ X بـ '#'
أ. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
5. LOAD_PACKAGE_SELF(X, dirname(Y))
6. LOAD_NODE_MODULES(X, dirname(Y))
7. ألقِ "لم يتم العثور عليه"
MAYBE_DETECT_AND_LOAD(X)
1. إذا تم تحليل X كوحدة نمطية CommonJS، قم بتحميل X كوحدة نمطية CommonJS. توقف.
2. خلاف ذلك، إذا كان بإمكان تحليل شفرة المصدر لـ X كوحدة نمطية ECMAScript باستخدام
<a href="esm#resolver-algorithm-specification">DETECT_MODULE_SYNTAX المُعرّف في
محلّي الوحدات النمطية ESM</a>،
أ. حمّل X كوحدة نمطية ECMAScript. توقف.
3. ألقِ SyntaxError من محاولة تحليل X كـ CommonJS في 1. توقف.
LOAD_AS_FILE(X)
1. إذا كان X ملفًا، حمّل X بتنسيق امتداد الملف الخاص به. توقف
2. إذا كان X.js ملفًا،
أ. ابحث عن نطاق الحزمة الأقرب SCOPE إلى X.
ب. إذا لم يتم العثور على نطاق
1. MAYBE_DETECT_AND_LOAD(X.js)
ج. إذا كان SCOPE/package.json يحتوي على حقل "type"،
1. إذا كان حقل "type" هو "module"، حمّل X.js كوحدة نمطية ECMAScript. توقف.
2. إذا كان حقل "type" هو "commonjs"، حمّل X.js كوحدة نمطية CommonJS. توقف.
د. MAYBE_DETECT_AND_LOAD(X.js)
3. إذا كان X.json ملفًا، حمّل X.json إلى كائن JavaScript. توقف
4. إذا كان X.node ملفًا، حمّل X.node كإضافة ثنائية. توقف
LOAD_INDEX(X)
1. إذا كان X/index.js ملفًا
أ. ابحث عن نطاق الحزمة الأقرب SCOPE إلى X.
ب. إذا لم يتم العثور على نطاق، حمّل X/index.js كوحدة نمطية CommonJS. توقف.
ج. إذا كان SCOPE/package.json يحتوي على حقل "type"،
1. إذا كان حقل "type" هو "module"، حمّل X/index.js كوحدة نمطية ECMAScript. توقف.
2. خلاف ذلك، حمّل X/index.js كوحدة نمطية CommonJS. توقف.
2. إذا كان X/index.json ملفًا، قم بتحليل X/index.json إلى كائن JavaScript. توقف
3. إذا كان X/index.node ملفًا، حمّل X/index.node كإضافة ثنائية. توقف
LOAD_AS_DIRECTORY(X)
1. إذا كان X/package.json ملفًا،
أ. قم بتحليل X/package.json، وابحث عن حقل "main".
ب. إذا كانت قيمة "main" خاطئة، انتقل إلى 2.
ج. دع M = X + (حقل json الرئيسي)
د. LOAD_AS_FILE(M)
هـ. LOAD_INDEX(M)
و. LOAD_INDEX(X) مُعطّل
ز. ألقِ "لم يتم العثور عليه"
2. LOAD_INDEX(X)
LOAD_NODE_MODULES(X, START)
1. دع DIRS = NODE_MODULES_PATHS(START)
2. لكل DIR في DIRS:
أ. LOAD_PACKAGE_EXPORTS(X, DIR)
ب. LOAD_AS_FILE(DIR/X)
ج. LOAD_AS_DIRECTORY(DIR/X)
NODE_MODULES_PATHS(START)
1. دع PARTS = تقسيم المسار(START)
2. دع I = عدد PARTS - 1
3. دع DIRS = []
4. بينما I >= 0،
أ. إذا كان PARTS[I] = "node_modules"، انتقل إلى د.
ب. DIR = ربط المسار(PARTS[0 .. I] + "node_modules")
ج. DIRS = DIR + DIRS
د. دع I = I - 1
5. أعد DIRS + GLOBAL_FOLDERS
LOAD_PACKAGE_IMPORTS(X, DIR)
1. ابحث عن نطاق الحزمة الأقرب SCOPE إلى DIR.
2. إذا لم يتم العثور على نطاق، فارجع.
3. إذا كان SCOPE/package.json "imports" فارغًا أو غير مُعرّف، فارجع.
4. إذا تم تمكين `--experimental-require-module`
أ. دع CONDITIONS = ["node", "require", "module-sync"]
ب. خلاف ذلك، دع CONDITIONS = ["node", "require"]
5. دع MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE),
CONDITIONS) <a href="esm#resolver-algorithm-specification">المُعرّف في محلّي الوحدات النمطية ESM</a>.
6. RESOLVE_ESM_MATCH(MATCH).
LOAD_PACKAGE_EXPORTS(X, DIR)
1. حاول تفسير X كمجموعة من NAME و SUBPATH حيث قد يكون الاسم
يحتوي على بادئة @scope/ ويبدأ المسار الفرعي بعلامة مائلة (`/`).
2. إذا لم يتطابق X مع هذا النمط أو لم يكن DIR/NAME/package.json ملفًا،
فارجع.
3. قم بتحليل DIR/NAME/package.json، وابحث عن حقل "exports".
4. إذا كان "exports" فارغًا أو غير مُعرّف، فارجع.
5. إذا تم تمكين `--experimental-require-module`
أ. دع CONDITIONS = ["node", "require", "module-sync"]
ب. خلاف ذلك، دع CONDITIONS = ["node", "require"]
6. دع MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH،
`package.json` "exports"، CONDITIONS) <a href="esm#resolver-algorithm-specification">المُعرّف في محلّي الوحدات النمطية ESM</a>.
7. RESOLVE_ESM_MATCH(MATCH)
LOAD_PACKAGE_SELF(X, DIR)
1. ابحث عن نطاق الحزمة الأقرب SCOPE إلى DIR.
2. إذا لم يتم العثور على نطاق، فارجع.
3. إذا كان SCOPE/package.json "exports" فارغًا أو غير مُعرّف، فارجع.
4. إذا لم يكن SCOPE/package.json "name" هو الجزء الأول من X، فارجع.
5. دع MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE)،
"." + X.slice("name".length)، `package.json` "exports"، ["node", "require"])
<a href="esm#resolver-algorithm-specification">المُعرّف في محلّي الوحدات النمطية ESM</a>.
6. RESOLVE_ESM_MATCH(MATCH)
RESOLVE_ESM_MATCH(MATCH)
1. دع RESOLVED_PATH = fileURLToPath(MATCH)
2. إذا كان الملف الموجود في RESOLVED_PATH موجودًا، حمّل RESOLVED_PATH بتنسيق امتداد الملف الخاص به. توقف
3. ألقِ "لم يتم العثور عليه"
تخزين مؤقت
يتم تخزين الوحدات النمطية مؤقتًا بعد تحميلها لأول مرة. هذا يعني (من بين أمور أخرى) أن كل مكالمة إلى require('foo')
ستحصل على نفس الكائن المُرجع بالضبط، إذا كان سيُحل إلى نفس الملف.
بشرط عدم تعديل require.cache
، فإن المكالمات المتعددة إلى require('foo')
لن تتسبب في تنفيذ رمز الوحدة النمطية عدة مرات. هذه ميزة مهمة. معها، يمكن إرجاع الكائنات "المكتملة جزئيًا"، مما يسمح بتحميل التبعيات العابرة حتى عندما تسبب دورات.
لجعل وحدة نمطية تُنفذ الكود عدة مرات، قم بتصدير دالة، واستدعاء تلك الدالة.
تحذيرات تخزين الوحدات النمطية مؤقتًا
يتم تخزين الوحدات النمطية مؤقتًا بناءً على اسم الملف المُحل لها. نظرًا لأن الوحدات النمطية قد تُحل إلى اسم ملف مختلف بناءً على موقع الوحدة النمطية المُستدعية (التحميل من مجلدات node_modules
)، فإنه ليس ضمانًا أن require('foo')
ستُرجع دائمًا نفس الكائن بالضبط، إذا كانت ستُحل إلى ملفات مختلفة.
بالإضافة إلى ذلك، في أنظمة الملفات أو أنظمة التشغيل غير حساسة لحالة الأحرف، يمكن لأسماء الملفات المُحلّة المختلفة أن تشير إلى نفس الملف، لكن ذاكرة التخزين المؤقت ستظل تعاملها على أنها وحدات نمطية مختلفة وستُعيد تحميل الملف عدة مرات. على سبيل المثال، require('./foo')
و require('./FOO')
تُرجع كائنين مختلفين، بغض النظر عما إذا كان ./foo
و ./FOO
هما نفس الملف.
الوحدات النمطية المُدمجة
[السجل]
الإصدار | التغييرات |
---|---|
v16.0.0، v14.18.0 | تمت إضافة دعم استيراد node: إلى require(...) . |
يحتوي Node.js على عدة وحدات نمطية مُجمعة في الملف الثنائي. يتم وصف هذه الوحدات النمطية بمزيد من التفاصيل في مكان آخر من هذه الوثائق.
يتم تعريف الوحدات النمطية المُدمجة داخل مصدر Node.js وتقع في مجلد lib/
.
يمكن تحديد الوحدات النمطية المُدمجة باستخدام بادئة node:
، وفي هذه الحالة، تتجاوز ذاكرة التخزين المؤقت require
. على سبيل المثال، require('node:http')
ستُرجع دائمًا وحدة HTTP المُدمجة، حتى لو كان هناك إدخال require.cache
بهذا الاسم.
يتم دائمًا تحميل بعض الوحدات النمطية المُدمجة بشكل تفضيلي إذا تم تمرير مُعرّفها إلى require()
. على سبيل المثال، require('http')
ستُرجع دائمًا وحدة HTTP المُدمجة، حتى لو كان هناك ملف بهذا الاسم. تتوفر قائمة الوحدات النمطية المُدمجة التي يمكن تحميلها دون استخدام بادئة node:
في module.builtinModules
، مدرجة بدون البادئة.
الوحدات المُدمجة مع بادئة node:
الإلزامية
عند التحميل بواسطة require()
، يجب طلب بعض الوحدات المُدمجة باستخدام بادئة node:
. يوجد هذا الشرط لمنع حدوث تعارض بين الوحدات المُدمجة المُضافة حديثًا وحزم مساحة المستخدم التي تحمل نفس الاسم بالفعل. حاليًا، الوحدات المُدمجة التي تتطلب بادئة node:
هي:
تُعرض قائمة هذه الوحدات في module.builtinModules
، بما في ذلك البادئة.
الدورات
عندما تكون هناك مُكالمات require()
دائرية، قد لا يكون جرى تنفيذ وحدة ما بالكامل عند إعادتها.
ضع في اعتبارك هذا الموقف:
a.js
:
console.log('a starting')
exports.done = false
const b = require('./b.js')
console.log('in a, b.done = %j', b.done)
exports.done = true
console.log('a done')
b.js
:
console.log('b starting')
exports.done = false
const a = require('./a.js')
console.log('in b, a.done = %j', a.done)
exports.done = true
console.log('b done')
main.js
:
console.log('main starting')
const a = require('./a.js')
const b = require('./b.js')
console.log('in main, a.done = %j, b.done = %j', a.done, b.done)
عندما تقوم main.js
بتحميل a.js
، تقوم a.js
بدورها بتحميل b.js
. في تلك المرحلة، تحاول b.js
تحميل a.js
. ولمنع حدوث حلقة لا نهائية، يتم إرجاع نسخة غير مكتملة من كائن exports
الخاص بـ a.js
إلى وحدة b.js
. ثم تنتهي b.js
من التحميل، ويتم توفير كائن exports
الخاص بها لوحدة a.js
.
بحلول الوقت الذي تقوم فيه main.js
بتحميل كلا الوحدتين، يكون كلاهما قد انتهى. لذلك سيكون مخرجات هذا البرنامج:
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done = true, b.done = true
يُطلب التخطيط الدقيق للسماح باعتماديات الوحدات الدائرية بالعمل بشكل صحيح داخل التطبيق.
وحدات الملفات
إذا لم يتم العثور على اسم الملف بالضبط، فستحاول Node.js تحميل اسم الملف المطلوب مع ملحقات إضافية: .js
، و.json
، وأخيرًا .node
. عند تحميل ملف يحتوي على امتداد مختلف (مثل .cjs
)، يجب تمرير اسمه الكامل إلى require()
، بما في ذلك امتداد الملف (مثل require('./file.cjs')
).
يتم تحليل ملفات .json
كملفات نصية JSON، ويتم تفسير ملفات .node
كملحقات إضافية مُجمعة يتم تحميلها باستخدام process.dlopen()
. يتم تحليل الملفات التي تستخدم أي امتداد آخر (أو بدون امتداد على الإطلاق) كملفات نصية JavaScript. يرجى الرجوع إلى قسم تحديد نظام الوحدة لفهم هدف التحليل الذي سيتم استخدامه.
الوحدة المطلوبة التي تبدأ بـ '/'
هي مسار مطلق إلى الملف. على سبيل المثال، سيقوم require('/home/marco/foo.js')
بتحميل الملف الموجود في /home/marco/foo.js
.
الوحدة المطلوبة التي تبدأ بـ './'
هي مسار نسبي إلى الملف الذي يستدعي require()
. وهذا يعني أن الملف circle.js
يجب أن يكون في نفس الدليل مثل foo.js
حتى يتمكن require('./circle')
من العثور عليه.
بدون بادئة '/'
أو './'
أو '../'
للإشارة إلى ملف، يجب أن تكون الوحدة إما وحدة أساسية أو يتم تحميلها من مجلد node_modules
.
إذا لم يكن المسار المعطى موجودًا، فسيقوم require()
بإطلاق خطأ MODULE_NOT_FOUND
.
المجلدات كـوحدات
[مستقر: 3 - إرث]
مستقر: 3 ثبات: 3 - إرث: استخدم تصدير المسارات الفرعية أو استيراد المسارات الفرعية بدلاً من ذلك.
هناك ثلاث طرق يمكن من خلالها تمرير مجلد إلى require()
كوسيطة.
الأولى هي إنشاء ملف package.json
في جذر المجلد، والذي يحدد وحدة main
. قد يبدو ملف package.json
مثالياً كالتالي:
{ "name": "some-library", "main": "./lib/some-library.js" }
إذا كان هذا في مجلد في ./some-library
، فسوف يحاول require('./some-library')
تحميل ./some-library/lib/some-library.js
.
إذا لم يكن هناك ملف package.json
موجودًا في الدليل، أو إذا كانت إدخال "main"
مفقودًا أو لا يمكن حله، فستحاول Node.js تحميل ملف index.js
أو index.node
من ذلك الدليل. على سبيل المثال، إذا لم يكن هناك ملف package.json
في المثال السابق، فسوف يحاول require('./some-library')
تحميل:
./some-library/index.js
./some-library/index.node
إذا فشلت هذه المحاولات، فستبلغ Node.js عن الوحدة بأكملها على أنها مفقودة مع الخطأ الافتراضي:
Error: Cannot find module 'some-library'
في جميع الحالات الثلاث أعلاه، ستؤدي مكالمة import('./some-library')
إلى خطأ ERR_UNSUPPORTED_DIR_IMPORT
. يمكن أن يوفر استخدام تصدير مسارات فرعية أو استيراد مسارات فرعية نفس فوائد تنظيم المحتوى مثل المجلدات كوحدات، وتعمل مع كل من require
و import
.
التحميل من مجلدات node_modules
إذا لم يكن مُعرّف الوحدة المُمرّر إلى require()
وحدة مدمجة، ولم يبدأ بـ '/'
، أو '../'
، أو './'
، فإن Node.js يبدأ من مُجلّد الوحدة الحالية، ويضيف /node_modules
، ويحاول تحميل الوحدة من هذا الموقع. لن يُضيف Node.js node_modules
إلى مسار ينتهي بالفعل بـ node_modules
.
إذا لم يتم العثور عليه هناك، فإنه ينتقل إلى المُجلّد الأب، وهكذا حتى يتم الوصول إلى جذر نظام الملفات.
على سبيل المثال، إذا كان الملف الموجود في '/home/ry/projects/foo.js'
قد دعا require('bar.js')
، فسيبحث Node.js في المواقع التالية، بهذا الترتيب:
/home/ry/projects/node_modules/bar.js
/home/ry/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
هذا يسمح للبرامج بتوطين اعتماداتها، بحيث لا تتضارب.
من الممكن طلب ملفات محددة أو وحدات فرعية موزعة مع وحدة معينة عن طريق تضمين لاحقة مسار بعد اسم الوحدة. على سبيل المثال، require('example-module/path/to/file')
سيُحلّ path/to/file
نسبةً إلى مكان وجود example-module
. تتبع لاحقة المسار نفس دلالات حل الوحدة.
التحميل من المجلدات العالمية
إذا تم تعيين متغير البيئة NODE_PATH
إلى قائمة مفصولة بفاصلات نقطتين من المسارات المطلقة، فسيبحث Node.js عن تلك المسارات للوحدات إذا لم يتم العثور عليها في أماكن أخرى.
في Windows، يتم فصل NODE_PATH
بواسطة الفواصل المنقوطة (;
) بدلاً من النقاط.
تم إنشاء NODE_PATH
في الأصل لدعم تحميل الوحدات من مسارات متنوعة قبل تعريف خوارزمية حل الوحدة الحالية.
لا يزال يتم دعم NODE_PATH
، ولكنه أصبح أقل ضرورة الآن بعد أن استقر نظام Node.js البيئي على اتفاقية لتحديد موقع الوحدات التابعة. في بعض الأحيان، تُظهر عمليات النشر التي تعتمد على NODE_PATH
سلوكًا مُفاجئًا عندما لا يدرك الأشخاص أنه يجب تعيين NODE_PATH
. في بعض الأحيان تتغير اعتماديات الوحدة، مما يتسبب في تحميل إصدار مختلف (أو حتى وحدة مختلفة) أثناء البحث في NODE_PATH
.
بالإضافة إلى ذلك، سيقوم Node.js بالبحث في القائمة التالية من GLOBAL_FOLDERS:
- 1:
$HOME/.node_modules
- 2:
$HOME/.node_libraries
- 3:
$PREFIX/lib/node
حيث $HOME
هو مُجلّد المستخدم الرئيسي، و $PREFIX
هو node_prefix
المُهيّأ في Node.js.
هذه الأسباب في الغالب تاريخية.
يُنصح بشدة بوضع الاعتماديات في مُجلّد node_modules
المحلي. سيتم تحميلها بشكل أسرع، وبشكل أكثر موثوقية.
غلاف الوحدة النمطية
قبل تنفيذ شفرة الوحدة النمطية، سيقوم Node.js بتغليفها باستخدام غلاف دالة يشبه ما يلي:
;(function (exports, require, module, __filename, __dirname) {
// توجد شفرة الوحدة النمطية هنا بالفعل
})
من خلال القيام بذلك، يحقق Node.js بعض الأمور:
- يحافظ على المتغيرات العليا (المعرّفة باستخدام
var
أوconst
أوlet
) ضمن نطاق الوحدة النمطية بدلاً من الكائن العام. - يساعد في توفير بعض المتغيرات التي تبدو عامة والتي هي في الواقع خاصة بالوحدة النمطية، مثل:
- كائنات
module
وexports
التي يمكن للمُنفذ استخدامها لتصدير القيم من الوحدة النمطية. - متغيرات الراحة
__filename
و__dirname
، التي تحتوي على اسم الملف المطلق لمسار دليل الوحدة النمطية.
- كائنات
نطاق الوحدة النمطية
__dirname
مُضاف في: v0.1.27
اسم دليل الوحدة النمطية الحالية. هذا هو نفسه مثل path.dirname()
من __filename
.
مثال: تشغيل node example.js
من /Users/mjr
console.log(__dirname)
// يُطبع: /Users/mjr
console.log(path.dirname(__filename))
// يُطبع: /Users/mjr
__filename
مُضاف في: v0.0.1
اسم ملف الوحدة النمطية الحالية. هذا هو المسار المطلق لملف الوحدة النمطية الحالي مع حل الارتباطات الرمزية.
بالنسبة للبرنامج الرئيسي، ليس هذا بالضرورة هو نفسه اسم الملف المستخدم في سطر الأوامر.
راجع __dirname
للحصول على اسم دليل الوحدة النمطية الحالية.
أمثلة:
تشغيل node example.js
من /Users/mjr
console.log(__filename)
// يُطبع: /Users/mjr/example.js
console.log(__dirname)
// يُطبع: /Users/mjr
بافتراض وجود وحدتين نمطيتين: a
و b
، حيث أن b
هي تبعية لـ a
ويوجد بنية دليل:
/Users/mjr/app/a.js
/Users/mjr/app/node_modules/b/b.js
ستُرجع مراجع __filename
داخل b.js
قيمة /Users/mjr/app/node_modules/b/b.js
بينما ستُرجع مراجع __filename
داخل a.js
قيمة /Users/mjr/app/a.js
.
exports
مضاف في: v0.1.12
مرجع إلى module.exports
وهو أقصر في الكتابة. راجع قسم اختصار exports للحصول على تفاصيل حول متى تستخدم exports
ومتى تستخدم module.exports
.
module
مضاف في: v0.1.16
مرجع إلى الوحدة النمطية الحالية، راجع قسم module
object. على وجه الخصوص، يتم استخدام module.exports
لتحديد ما تقوم الوحدة النمطية بتصديره وما يجعله متاحًا من خلال require()
.
require(id)
مضاف في: v0.1.13
يستخدم لاستيراد الوحدات النمطية و JSON
والملفات المحلية. يمكن استيراد الوحدات النمطية من node_modules
. يمكن استيراد الوحدات النمطية المحلية وملفات JSON باستخدام مسار نسبي (مثل ./
، ./foo
، ./bar/baz
، ../foo
) سيتم حله مقابل الدليل المسمى بـ __dirname
(إذا تم تعريفه) أو دليل العمل الحالي. يتم حل المسارات النسبية لأسلوب POSIX بطريقة مستقلة عن نظام التشغيل، مما يعني أن الأمثلة المذكورة أعلاه ستعمل على Windows بنفس الطريقة التي تعمل بها على أنظمة Unix.
// استيراد وحدة نمطية محلية بمسار نسبي إلى `__dirname` أو دليل العمل الحالي. (على Windows، سيتم حل هذا إلى .\path\myLocalModule.)
const myLocalModule = require('./path/myLocalModule')
// استيراد ملف JSON:
const jsonData = require('./path/filename.json')
// استيراد وحدة نمطية من node_modules أو وحدة نمطية مدمجة في Node.js:
const crypto = require('node:crypto')
require.cache
مضاف في: v0.3.0
يتم تخزين الوحدات النمطية مؤقتًا في هذا الكائن عند طلبها. عن طريق حذف قيمة مفتاح من هذا الكائن، سيعيد require
التالي تحميل الوحدة النمطية. لا ينطبق هذا على إضافات native، حيث سيؤدي إعادة التحميل إلى حدوث خطأ.
إضافة أو استبدال الإدخالات ممكن أيضًا. يتم التحقق من هذه ذاكرة التخزين المؤقت قبل الوحدات النمطية المدمجة وإذا تمت إضافة اسم يطابق وحدة نمطية مدمجة إلى ذاكرة التخزين المؤقت، فإن مكالمات node:
-prefixed require فقط ستتلقى الوحدة النمطية المدمجة. استخدم بحذر!
const assert = require('node:assert')
const realFs = require('node:fs')
const fakeFs = {}
require.cache.fs = { exports: fakeFs }
assert.strictEqual(require('fs'), fakeFs)
assert.strictEqual(require('node:fs'), realFs)
require.extensions
مضاف في: v0.3.0
مُحذّف منذ: v0.10.6
توجيه require
حول كيفية التعامل مع امتدادات الملفات المعينة.
معالجة الملفات التي تحمل الامتداد .sjs
كملفات .js
:
require.extensions['.sjs'] = require.extensions['.js']
مُحذّف. في الماضي، تم استخدام هذه القائمة لتحميل وحدات غير جافا سكريبت في Node.js عن طريق تجميعها عند الطلب. ومع ذلك، في الممارسة العملية، هناك طرق أفضل بكثير للقيام بذلك، مثل تحميل الوحدات عبر برنامج Node.js آخر، أو تجميعها إلى جافا سكريبت مسبقًا.
تجنب استخدام require.extensions
. قد يتسبب الاستخدام في أخطاء خفية، ويصبح حل الامتدادات أبطأ مع كل امتداد مُسجّل.
require.main
مضاف في: v0.1.17
كائن Module
الذي يمثل البرنامج النصي الرئيسي المُحمّل عند تشغيل عملية Node.js، أو undefined
إذا لم تكن نقطة دخول البرنامج وحدة CommonJS. راجع "الوصول إلى الوحدة الرئيسية".
في برنامج نصي entry.js
:
console.log(require.main)
node entry.js
Module {
id: '.',
path: '/absolute/path/to',
exports: {},
filename: '/absolute/path/to/entry.js',
loaded: false,
children: [],
paths:
[ '/absolute/path/to/node_modules',
'/absolute/path/node_modules',
'/absolute/node_modules',
'/node_modules' ] }
require.resolve(request[, options])
[السجل]
الإصدار | التغييرات |
---|---|
v8.9.0 | خيار paths مدعوم الآن. |
v0.3.0 | مضاف في: v0.3.0 |
request
<string> مسار الوحدة لحلها.options
<Object>paths
<string[]> مسارات لحل موقع الوحدة منها. إذا وُجدت، فسيتم استخدام هذه المسارات بدلاً من مسارات الحل الافتراضية، باستثناء GLOBAL_FOLDERS مثل$HOME/.node_modules
، والتي يتم تضمينها دائمًا. يتم استخدام كل من هذه المسارات كنقطة بداية لخوارزمية حل الوحدة، مما يعني أنه يتم فحص التسلسل الهرميnode_modules
من هذا الموقع.
المُرجَع: <string>
استخدم آلية require()
الداخلية للبحث عن موقع وحدة، ولكن بدلاً من تحميل الوحدة، قم فقط بإرجاع اسم الملف المُحل.
إذا تعذر العثور على الوحدة، فسيتم إطلاق خطأ MODULE_NOT_FOUND
.
require.resolve.paths(request)
مضاف في: v8.9.0
request
<string> مسار الوحدة التي يتم استرداد مسارات البحث عنها.- القيمة المُرجعه: <string[]> | <null>
يرجع مصفوفة تحتوي على المسارات التي تم البحث عنها أثناء حل request
أو null
إذا كانت سلسلة request
تشير إلى وحدة أساسية، مثل http
أو fs
.
كائن module
مضاف في: v0.1.16
في كل وحدة، يكون المتغير الحر module
مرجعًا للكائن الذي يمثل الوحدة الحالية. للتسهيل، يمكن الوصول إلى module.exports
أيضًا عبر المُتغير العام للوحدة exports
. module
ليس متغيرًا عالميًا في الواقع، بل محليًا لكل وحدة.
module.children
مضاف في: v0.1.16
كائنات الوحدات المطلوبة لأول مرة بواسطة هذه الوحدة.
module.exports
مضاف في: v0.1.16
يتم إنشاء كائن module.exports
بواسطة نظام Module
. في بعض الأحيان لا يكون هذا مقبولاً؛ يريد الكثيرون أن تكون وحدتهم مثيلًا لبعض الفئات. للقيام بذلك، قم بتعيين كائن التصدير المطلوب إلى module.exports
. إن تعيين الكائن المطلوب إلى exports
سيؤدي ببساطة إلى إعادة ربط المتغير المحلي exports
، وهو ما لا يُرجّح أن يكون مرغوبًا فيه.
على سبيل المثال، لنفترض أننا نقوم بإنشاء وحدة تسمى a.js
:
const EventEmitter = require('node:events')
module.exports = new EventEmitter()
// القيام ببعض العمل، وبعد مرور بعض الوقت، انبعاث
// حدث 'ready' من الوحدة نفسها.
setTimeout(() => {
module.exports.emit('ready')
}, 1000)
ثم في ملف آخر يمكننا أن نفعل:
const a = require('./a')
a.on('ready', () => {
console.log('module "a" is ready')
})
يجب أن يتم التعيين إلى module.exports
على الفور. لا يمكن القيام بذلك في أي استدعاءات مرتدة. هذا لا يعمل:
x.js
:
setTimeout(() => {
module.exports = { a: 'hello' }
}, 0)
y.js
:
const x = require('./x')
console.log(x.a)
اختصار exports
مُضاف في: v0.1.16
متغير exports
متوفر ضمن نطاق الملف في المُستوى الأعلى للوحدة، ويتم تعيين قيمة module.exports
له قبل تقييم الوحدة.
يسمح باختصار، بحيث يمكن كتابة module.exports.f = ...
بشكل أكثر إيجازًا كـ exports.f = ...
. ومع ذلك، انتبه إلى أنه مثل أي متغير، إذا تم تعيين قيمة جديدة لـ exports
، فلن يكون مرتبطًا بعد الآن بـ module.exports
:
module.exports.hello = true // مُصدر من `require` للوحدة
exports = { hello: false } // غير مُصدر، متوفر فقط في الوحدة
عندما يتم استبدال خاصية module.exports
بالكامل بكائن جديد، فمن الشائع إعادة تعيين exports
:
module.exports = exports = function Constructor() {
// ... إلخ
}
لتوضيح السلوك، تخيل هذا التنفيذ الافتراضي لـ require()
، والذي يشبه إلى حد كبير ما يتم فعله بالفعل بواسطة require()
:
function require(/* ... */) {
const module = { exports: {} }
;((module, exports) => {
// كود الوحدة هنا. في هذا المثال، قم بتعريف دالة.
function someFunc() {}
exports = someFunc
// في هذه المرحلة، لم يعد `exports` اختصارًا لـ `module.exports`، وستصدر هذه الوحدة كائنًا افتراضيًا فارغًا.
module.exports = someFunc
// في هذه المرحلة، ستصدر الوحدة الآن `someFunc`، بدلاً من الكائن الافتراضي.
})(module, module.exports)
return module.exports
}
module.filename
مُضاف في: v0.1.16
اسم الملف الكامل المُحل لوحدة.
module.id
مُضاف في: v0.1.16
المُعرّف للوحدة. عادةً ما يكون هذا هو اسم الملف الكامل المُحل.
module.isPreloading
مُضاف في: v15.4.0، v14.17.0
- النوع: <boolean>
true
إذا كانت الوحدة تعمل أثناء مرحلة التحميل المسبق لـ Node.js.
module.loaded
مُضاف في: v0.1.16
ما إذا كان جاري تحميل الوحدة النمطية أو تم الانتهاء من تحميلها.
module.parent
مُضاف في: v0.1.16
مُهمل منذ: v14.6.0, v12.19.0
[مستقر: 0 - مُهمل]
مستقر: 0 الثبات: 0 - مُهمل: يرجى استخدام require.main
و module.children
بدلاً من ذلك.
الوحدة النمطية التي طلبتها أولاً، أو null
إذا كانت الوحدة النمطية الحالية هي نقطة دخول العملية الحالية، أو undefined
إذا تم تحميل الوحدة النمطية بواسطة شيء ليس وحدة نمطية CommonJS (مثل: REPL أو import
).
module.path
مُضاف في: v11.14.0
اسم الدليل للوحدة النمطية. هذا عادةً ما يكون نفس path.dirname()
من module.id
.
module.paths
مُضاف في: v0.4.0
مسارات البحث للوحدة النمطية.
module.require(id)
مُضاف في: v0.5.1
توفر طريقة module.require()
طريقة لتحميل وحدة نمطية كما لو تم استدعاء require()
من الوحدة النمطية الأصلية.
من أجل القيام بذلك، من الضروري الحصول على مرجع لكائن module
. بما أن require()
تُرجع module.exports
، و module
متوفر عادةً فقط داخل رمز وحدة نمطية محددة، فيجب تصديره صراحةً لاستخدامه.
كائن Module
تم نقل هذا القسم إلى الوحدات: وحدة module
الأساسية.
دعم خريطة المصدر الإصدار 3
تم نقل هذا القسم إلى الوحدات: وحدة module
الأساسية.