Node.js: وحدتا path و url
آخر تحديث: 2026-08-26
1. القصة: كارثة في عملية النشر بسبب فاصل
أُجريت اختبارات على أداة CLI التي طورها بوب على نظام macOS وكانت النتائج مثالية — حيث استُخدمت / في ربط المسارات، كما سارت عمليات قراءة التكوين وكتابة السجلات دون أي مشاكل. ومع ذلك، بعد نشرها على خادم Linux، أبلغ البرنامج على الفور عن حدوث خطأ: ENOENT: no such file or directory. وعند التحقيق في الأمر، تبين أن بوب قد قام بترميز / بشكل ثابت كفاصل للمسارات في الكود، في حين أن بعض منطق معالجة المسارات في نظام Windows يستخدم \، مما أدى إلى حدوث خلط في تحليل المسارات. وقد منح هذا الحادث بوب فهمًا عميقًا للغرض من وحدة path — ألا يتم أبدًا ربط المسارات يدويًّا.
(1) ستتعلم
- الطرق الأساسية في وحدة «path»: combine / resolve / parse / format / extname / basename / dirname
- الثوابت المشتركة بين الأنظمة «path.sep» و«path.delimiter»
- url.URL / url.parse / url.fileURLToPath
- مُنشئ عناوين URL و searchParams
- __dirname / __filename مقابل import.meta.url
- أفضل الممارسات في معالجة المسارات عبر الأنظمة الأساسية المختلفة
2. الطرق الأساسية في وحدة «path»
الوحدة النمطية path هي وحدة نمطية مدمجة في Node.js توفر أدوات لتسلسل مسارات الملفات وتحليلها وتنسيقها، مع معالجة الاختلافات في فواصل المسارات عبر أنظمة التشغيل تلقائيًّا.
(1) ربط المسارات — تسلسل المسارات
path.join() يدمج عدة مقاطع مسار في مسار واحد موحد، وذلك تلقائيًا باستخدام الفاصل المستخدم حاليًا في النظام.
▶ مثال: التسلسل الأساسي للمسارات باستخدام path.join
const path = require('path');
const fullPath = path.join('/app', 'src', 'utils', 'helper.js');
console.log(fullPath);
// macOS/Linux: /app/src/utils/helper.js
// Windows: \app\src\utils\helper.js
▶ مثال: path.join — التوحيد التلقائي
const path = require('path');
console.log(path.join('/app', '../config', 'settings.json'));
// /config/settings.json
console.log(path.join('src', '.', 'index.js'));
// src/index.js
(2) path.resolve — تحويل المسار إلى مسار مطلق
path.resolve() قم بربط المسارات من اليمين إلى اليسار حتى يتم الحصول على مسار مطلق. إذا لم يتم الحصول على مسار مطلق، فاستخدم دليل العمل الحالي كأساس.
▶ مثال: الاستخدام الأساسي لـ path.resolve
const path = require('path');
console.log(path.resolve('src', 'index.js'));
// /current/working/dir/src/index.js
console.log(path.resolve('/app', 'src', 'index.js'));
// /app/src/index.js
console.log(path.resolve('/app', '/tmp', 'file.txt'));
// /tmp/file.txt(Use the absolute path on the far right as the reference.)
(3) path.parse و path.format — تحليل المسار وإعادة بنائه
يقسم path.parse() المسار إلى خمسة أجزاء: root وdir وbase وext وname؛ بينما يعيد path.format() تجميع الكائن ليشكل سلسلة نصية للمسار.
▶ مثال: تحليل المسار باستخدام path.parse
const path = require('path');
const parsed = path.parse('/app/src/utils/helper.js');
console.log(parsed);
{
root: '/',
dir: '/app/src/utils',
base: 'helper.js',
ext: '.js',
name: 'helper'
}
graph LR
A["/app/src/utils/helper.js"] --> B["root: /"]
A --> C["dir: /app/src/utils"]
A --> D["base: helper.js"]
D --> E["name: helper"]
D --> F["ext: .js"]
style A fill:#4CAF50,color:#fff
style B fill:#FF9800,color:#fff
style C fill:#2196F3,color:#fff
style D fill:#9C27B0,color:#fff
style E fill:#E91E63,color:#fff
style F fill:#FF5722,color:#fff
▶ مثال: path.format — إعادة كتابة المسارات
const path = require('path');
const filePath = path.format({
dir: '/app/src/utils',
base: 'helper.js'
});
console.log(filePath);
// /app/src/utils/helper.js
(4) path.extname / path.basename / path.dirname
تستخرج هذه الطرق الثلاث امتداد الملف واسم الملف وجزء الدليل من المسار، على التوالي.
▶ مثال: استخراج أجزاء المسار
const path = require('path');
const filePath = '/app/src/utils/helper.js';
console.log(path.extname(filePath)); // .js
console.log(path.basename(filePath)); // helper.js
console.log(path.basename(filePath, '.js')); // helper
console.log(path.dirname(filePath)); // /app/src/utils
3. ثوابت المسار عبر الأنظمة الأساسية
تختلف فواصل المسارات وفواصل متغيرات البيئة باختلاف أنظمة التشغيل؛ وتوفر الوحدة النمطية path ثوابت لمراعاة هذه الاختلافات.
(1) path.sep — فاصل المسار
| المنصة | فاصل المسار |
|---|---|
| macOS / Linux | / |
| ويندوز | \ |
(2) path.delimiter — فاصل متغيرات البيئة
| المنصة | فاصل المسار |
|---|---|
| macOS / Linux | : |
| ويندوز | ; |
▶ مثال: استخدام path.sep وpath.delimiter
const path = require('path');
console.log('Delimiter:', JSON.stringify(path.sep));
// macOS/Linux: "/"
// Windows: "\\"
const envPaths = process.env.PATH.split(path.delimiter);
console.log('PATH Number of entries:', envPaths.length);
4. الوحدة النمطية url ومنشئ عناوين URL
(1) url.parse (مهملة) مقابل new URL()
url.parse() هو إصدار قديم من واجهة برمجة التطبيقات (API) وقد تم تصنيفه على أنه قديم. نوصي باستخدام مُنشئ new URL()، الذي يتوافق مع معيار WHATWG.
| ميزة | url.parse() | new URL() |
|---|---|---|
| المعيار | Node.js القديم | معيار WHATWG |
| الحالة | مهمل | موصى به |
| معالجة الأخطاء | إرجاع القيمة null دون إظهار أي رسالة | إثارة استثناء TypeError |
| searchParams | لا شيء | URLSearchParams المدمجة |
| الأداء | أبطأ | أسرع |
▶ مثال: الاستخدام القديم لـ url.parse (غير موصى به)
const url = require('url');
const parsed = url.parse('https://example.com/api/users?name=Bob&page=1');
console.log(parsed.hostname);
console.log(parsed.query);
example.com
name=Bob&page=1
▶ مثال: الاستخدام الموصى به لـ new URL()
const myUrl = new URL('https://example.com/api/users?name=Bob&page=1');
console.log(myUrl.hostname);
console.log(myUrl.pathname);
console.log(myUrl.searchParams.get('name'));
console.log(myUrl.searchParams.get('page'));
example.com
/api/users
Bob
1
(2) url.searchParams —— التعامل مع معلمات الاستعلام
تعد الخاصية searchParams للكائن URL مثيلًا لـ URLSearchParams، والتي توفر طرقًا ملائمة لإضافة المعلمات وحذفها وتحديثها والاستعلام عنها.
▶ مثال: عمليات CRUD لـ searchParams
const myUrl = new URL('https://example.com/search');
myUrl.searchParams.set('q', 'nodejs');
myUrl.searchParams.set('lang', 'zh');
myUrl.searchParams.append('tag', 'バックエンド');
myUrl.searchParams.append('tag', 'tutorial');
myUrl.searchParams.remove('lang');
console.log(myUrl.toString());
// https://example.com/search?q=nodejs&tag=バックエンド&tag=tutorial
console.log(myUrl.searchParams.getAll('tag'));
// [ 'バックエンド', 'tutorial' ]
(3) url.fileURLToPath — يحول عنوان URL للملف إلى مسار محلي
في وحدة ESM، تُرجع الدالة import.meta.url عنوان URL بتنسيق file://، والذي يجب تحويله إلى مسار في نظام الملفات باستخدام الدالة url.fileURLToPath().
▶ مثال: تحويل fileURLToPath
const { fileURLToPath } = require('url');
const fileUrl = 'file:///app/src/index.js';
const filePath = fileURLToPath(fileUrl);
console.log(filePath);
// macOS/Linux: /app/src/index.js
// Windows: \app\src\index.js
5. __dirname / __filename مقابل import.meta.url
هذا هو الفرق الرئيسي بين نظامي الوحدات النمطية CJS و ESM فيما يتعلق بالحصول على مسار الملف الحالي.
| السمة | __dirname / __filename | import.meta.url |
|---|---|---|
| نظام الوحدات | CJS (require) | ESM (import) |
| نوع القيمة المُرجعة | سلسلة المسار المطلق | سلسلة عنوان URL من نوع file:// |
| التوفر | متغير عام، يُستخدم مباشرةً | يتطلب وجود دالة fileURLToPath |
| مسار الدليل | __dirname (استرجاع مباشر) | يتطلب استخدام dirname(fileURLToPath()) |
| مسار الملف | __filename (الوصول المباشر) | يتطلب التحويل باستخدام fileURLToPath() |
▶ مثال: استخدام __dirname في CJS
const path = require('path');
console.log('__dirname:', __dirname);
console.log('__filename:', __filename);
const configPath = path.join(__dirname, 'config', 'settings.json');
console.log(configPath);
▶ مثال: استخدام import.meta.url في ESM
import { fileURLToPath } from 'url';
import path from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
console.log('__dirname:', __dirname);
console.log('__filename:', __filename);
6. جدول مرجعي سريع لطرق المسار الشائعة
| الطريقة | المعلمات | القيمة المرجعة | الغرض |
|---|---|---|---|
path.join() |
...paths |
string |
ربط أجزاء المسار وتوحيدها تلقائيًا |
path.resolve() |
...paths |
string |
تحويل المسار إلى مسار مطلق |
path.parse() |
pathString |
object |
تقسيم المسار إلى الأجزاء المكونة له |
path.format() |
pathObject |
string |
إعادة بناء كائن المسار كسلسلة نصية |
path.extname() |
pathString |
string |
الحصول على امتداد الملف |
path.basename() |
pathString[, ext] |
string |
الحصول على اسم الملف (يمكن حذف الامتداد) |
path.dirname() |
pathString |
string |
الانتقال إلى جدول المحتويات |
path.isAbsolute() |
pathString |
boolean |
التحقق مما إذا كان المسار مسارًا مطلقًا |
path.normalize() pathString string المسار المعياري (معالجة ..، .) |
|||
path.relative() |
from, to |
string |
الحصول على المسار النسبي من «from» إلى «to» |
7. أفضل الممارسات في التعامل مع المسارات عبر الأنظمة الأساسية المختلفة
(1) المبادئ الأساسية
| القاعدة | الوصف | مثال خاطئ | مثال صحيح |
|---|---|---|---|
| استخدام path.join | المعالجة التلقائية للفواصل | 'src' + '/' + 'index.js' |
path.join('src', 'index.js') |
| استخدام path.sep | ثابت فاصل الاقتباس | str.split('/') |
str.split(path.sep) |
| استخدام path.resolve | الحصول على المسار المطلق | process.cwd() + '/' + file |
path.resolve(file) |
| استخدام fileURLToPath | تحويل عنوان URL للملف | import.meta.url.slice(7) |
fileURLToPath(import.meta.url) |
| تجنب استخدام __dirname في ESM | غير متوفر | استخدم __dirname مباشرةً | استخدم import.meta.url بدلاً من ذلك |
▶ مثال:(2) أنماط الأخطاء الشائعة
const path = require('path');
// ❌ Hard-coded delimiters
const bad1 = '/app/data/' + 'config.json';
// ✅ Usage path.join
const good1 = path.join('/app', 'data', 'config.json');
// ❌ Manually Merge Working Directories
const bad2 = process.cwd() + '/output/result.log';
// ✅ Usage path.resolve
const good2 = path.resolve('output', 'result.log');
// ❌ String Replacement Delimiter
const bad3 = somePath.replace(/\\/g, '/');
// ✅ Usage path.normalize
const good3 = path.normalize(somePath);
8. مثال شامل: أداة مسار الملفات عبر الأنظمة الأساسية
يحاكي المثال التالي المنطق الأساسي لأداة واجهة سطر الأوامر (CLI) المعدلة التي طورها بوب: قراءة مسار التكوين، وإنشاء دليل البيانات، وتحليل عنوان URL، واستخراج المعلمات.
const path = require('path');
const { fileURLToPath } = require('url');
class PathTool {
constructor(baseDir) {
this.baseDir = baseDir || process.cwd();
}
resolveConfigPath(configRelativePath) {
return path.resolve(this.baseDir, configRelativePath);
}
buildDataPath(...segments) {
return path.join(this.baseDir, 'data', ...segments);
}
parseUrl(urlString) {
const myUrl = new URL(urlString);
return {
protocol: myUrl.protocol,
hostname: myUrl.hostname,
pathname: myUrl.pathname,
params: Object.fromEntries(myUrl.searchParams.entries())
};
}
extractFileInfo(filePath) {
const parsed = path.parse(filePath);
return {
directory: parsed.dir,
fileName: parsed.name,
extension: parsed.ext,
fullPath: filePath
};
}
toFilePath(urlOrPath) {
if (urlOrPath.startsWith('file://')) {
return fileURLToPath(urlOrPath);
}
return path.resolve(urlOrPath);
}
}
const tool = new PathTool('/app/project');
// 1. Parsing the Configuration Path
const configPath = tool.resolveConfigPath('config/app.json');
console.log('Configuration Path:', configPath);
// /app/project/config/app.json
// 2. Data Merge Catalog
const dataPath = tool.buildDataPath('users', 'profiles.json');
console.log('Data Path:', dataPath);
// /app/project/data/users/profiles.json
// 3. Analysis URL and extract the parameters
const parsed = tool.parseUrl('https://api.example.com/v1/users?role=admin&active=true');
console.log('URL Analysis:', parsed);
// { protocol: 'https:', hostname: 'api.example.com',
// pathname: '/v1/users', params: { role: 'admin', active: 'true' } }
// 4. Extract File Information
const info = tool.extractFileInfo('/app/project/data/users/profiles.json');
console.log('File Information:', info);
// { directory: '/app/project/data/users',
// fileName: 'profiles', extension: '.json', fullPath: '...' }
// 5. file URL File Path
const localPath = tool.toFilePath('file:///app/project/config/app.json');
console.log('Local Path:', localPath);
// /app/project/config/app.json
❓ أسئلة شائعة
path.join وpath.resolve؟path.join ببساطة بربط أجزاء المسار وتوحيدها؛ وهي لا تضمن أن تكون النتيجة مسارًا مطلقًا. أما path.resolve فيحل المسار من اليمين إلى اليسار حتى يتم إنتاج مسار مطلق؛ وإذا لم يتم العثور على مسار مطلق، فإنه يستخدم process.cwd() كأساس.path.dirname(fileURLToPath(import.meta.url)) للحصول على القيم المكافئة.url.parse؟url.parse على أنه مهمل. نوصي باستخدام مُنشئ new URL() الوارد في معيار WHATWG، والذي يوفر معالجة أفضل للأخطاء ودعمًا مدمجًا لـ searchParams.path.join أو path.resolve لربط المسارات، واستخدم path.sep لتحديد الفاصل، واستخدم path.delimiter للتعامل مع متغيرات البيئة، وتجنب أي سلاسل فاصلة مكتوبة بشكل ثابت في الكود.import.meta.url؟file:// الخاص بالوحدة الحالية (على سبيل المثال، file:///app/src/index.js)، والتي يجب تحويلها إلى مسار في نظام الملفات باستخدام fileURLToPath().path.isAbsolute بنفس الطريقة عبر الأنظمة الأساسية المختلفة؟/usr/local مسارًا مطلقًا، بينما في نظام Windows، يُعد C:\Users مسارًا مطلقًا، في حين أن /usr/local ليس كذلك. ويحدد path.isAbsolute النتيجة بناءً على النظام الأساسي الحالي.📖 ملخص
- مقال واحد: المفاهيم الأساسية واستخدامات كارثة النشر الناجمة عن فاصل
- المفاهيم الأساسية واستخدامات الأساليب الأساسية في وحدة «2-Path»
- المفاهيم الأساسية واستخدام الثوابت متعددة المنصات ذات المسارات الثلاثة
- 4 المفاهيم الأساسية واستخدام وحدة url ومنشئ URL
- 5 مفاهيم أساسية واستخدامات __dirname / __filename مقابل import.meta.url
- المسار 6: دليل مرجعي سريع للمفاهيم الأساسية وطرق الاستخدام
- 7 مفاهيم أساسية وأفضل الممارسات لمعالجة المسارات عبر الأنظمة الأساسية المختلفة
- 8 مثال شامل: المفاهيم الأساسية واستخدام أداة مسار الملفات عبر الأنظمة الأساسية
📝 تمارين
- أكمل جميع أمثلة الأكواد الواردة في هذا الدرس وتأكد من أن كل منها يعمل بشكل صحيح.
- Modify the comprehensive example and add your own extensions
- راجع الوثائق الرسمية، وحدد واجهة برمجة تطبيقات (API) واحدة أو اثنتين لم يتم تناولهما في هذا الدرس، واكتب كود اختبار لهما.
- التأمل: كيف ستطبق ما تعلمته في هذا الدرس على مشروع في الواقع العملي؟
- حاول أن تجمع بين ما تعلمته في هذا الدرس والمواد التي درستها في الدروس السابقة لإنشاء مشروع صغير.