أثناء عملية التطوير، غالبًا ما يتم استخدام Node.js، والذي يستخدم الإمكانات التي يوفرها V8 لتوسيع قدرات JS. في Node.js، يمكننا استخدام وحدة المسار غير الموجودة في JS لكي نكون أكثر دراية بالتطبيق، دعونا نلقي نظرة عليه ~
إصدار Node.js من هذه المقالة هو 16.14.0. ، ويأتي الكود المصدري لهذه المقالة من هنا Version. آمل أنه بعد قراءة هذه المقالة، سيكون من المفيد للجميع قراءة الكود المصدري.
يتم استخدام المسار لمعالجة مسارات الملفات والأدلة. توفر هذه الوحدة بعض وظائف الأداة التي يسهل على المطورين تطويرها لمساعدتنا في إصدار أحكام المسار المعقدة وتحسين كفاءة التطوير. على سبيل المثال:
تكوين الأسماء المستعارة في المشروع يجعل تكوين الاسم المستعار من السهل علينا الرجوع إلى الملفات وتجنب البحث لأعلى خطوة بخطوة.
إعادة الحب: { الاسم المستعار: { // مسار دليل __dirname 'src' حيث يوجد الملف الحالي: path.resolve(__dirname, './src')، // دليل العمل الحالي لـprocess.cwd '@': path.join(process.cwd(), 'src')، }, }
في حزمة الويب، يمكن أيضًا إنشاء مسار إخراج الملف إلى الموقع المحدد من خلال التكوين الخاص بنا.
وحدة التصدير = { الإدخال: "./path/to/my/entry/file.js"، الإخراج: { المسار: path.resolve(__dirname, 'dist')، اسم الملف: "my-first-webpack.bundle.js"، }, };
أو بالنسبة لعمليات المجلد
Let fs = require("fs"); دع المسار = يتطلب ("المسار")؛ // احذف المجلد Let deleDir = (src) => { // اقرأ المجلد Let children = fs.readdirSync(src); children.forEach(العنصر => { Let Childpath = path.join(src, item); // التحقق من وجود الملف Let file = fs.statSync(childpath).isFile(); إذا (ملف) { // احذف الملف إذا كان موجودًا fs.unlinkSync(childpath) } آخر { // تابع اكتشاف المجلد deleDir(childpath) } }) // احذف المجلد الفارغ fs.rmdirSync(src) }يشرح
deleDir("../floor")
باختصار سيناريوهات استخدام المسار. وبعد ذلك، سندرس آلية تنفيذه وكيفية تنفيذه بناءً على استخدامه.
عند تقديم وحدة المسار واستدعاء وظيفة أداة المسار، سيتم إدخال منطق المعالجة للوحدة الأصلية.
استخدم وظيفة _load
لاستخدام اسم الوحدة الذي قدمته كمعرف لتحديد أن الوحدة التي سيتم تحميلها هي وحدة JS أصلية. بعد ذلك، سيتم استخدام وظيفة loadNativeModule
لاستخدام المعرف للعثور على كود ASCII المقابل من _source
. سلسلة التعليمات البرمجية المصدر التي تحفظ وحدة JS الأصلية). يتم تحميل البيانات في وحدة JS الأصلية.
قم بتنفيذ ملف lib/path.js واستخدم العملية لتحديد نظام التشغيل، اعتمادًا على نظام التشغيل، قد تكون هناك معالجة تفاضلية لأحرف التشغيل في معالجة الملف، ولكن الطريقة هي نفسها تقريبًا بعد المعالجة المتصل.
بإرجاع المسار المطلق للمسار الحالي،
ويقوم بربط معلمات متعددة بالتسلسل لإنشاء مسار مطلق جديد.
حل (...الوسائط) { السماح بحل الجهاز = ''; دع حل الذيل = ''; دع حلها مطلق = خطأ؛ // كشف المعلمات من اليمين إلى اليسار for (let i = args.length - 1; i >= -1; i--) { ... } // المسار الطبيعيsolveTail = NormalizeString(resolvedTail, !resolvedAbsolute, '\', isPathSeparator); عودة حلها مطلقا؟ `${resolvedDevice}\${resolvedTail}` : `${resolvedDevice}${resolvedTail}` || '.'; }
الحصول على المسار وفقًا للمعلمات، واجتياز المعلمات المستلمة، وبدء الربط عندما يكون طول المعلمات أكبر من أو يساوي 0، وإجراء التحقق غير السلسلة على المسار المقسم، إذا كانت هناك أي معلمات غير متطابقة ، throw new ERR_INVALID_ARG_TYPE(name, 'string', value)
، إذا تم استيفاء المتطلبات، فسيتم الحكم على طول المسار إذا كانت هناك قيمة، فسيتم استخدام المسار += للخطوة التالية.
دع المسار؛ إذا (أنا >= 0) { المسار = الحجج [i]؛ // داخلي/المدققين validateString(path, 'path'); // إذا كان طول المسار هو 0، فسوف يقفز مباشرةً خارج حلقة for الخاصة بكتلة التعليمات البرمجية أعلاه if (path. length === 0) { يكمل؛ } } وإلا إذا (resolvedDevice.length === 0) { // طول ResolveDevice هو 0، قم بتعيين القيمة للمسار باعتباره دليل العمل الحالي path =process.cwd(); } آخر { // قم بتعيين القيمة لكائن البيئة أو مسار دليل العمل الحالي =process.env[`=${resolvedDevice}`] || إذا (المسار === غير محدد || (StringPrototypeToLowerCase(StringPrototypeSlice(path, 0, 2)) !== StringPrototypeToLowerCase(resolvedDevice) && StringPrototypeCharCodeAt(path, 2) === CHAR_BACKWARD_SLASH)) { // احكم على المسار إلى المسارات غير الفارغة والمطلقة للحصول على المسار path = `${resolvedDevice}\`; } }
حاول مطابقة المسار الجذر، وحدد ما إذا كان هناك فاصل مسار واحد فقط ('') أو أن المسار هو مسار مطلق، ثم حدد المسار المطلق وقم بتعيين علامة اعتراض rootEnd
على 1 (منخفض). إذا كان العنصر الثاني لا يزال فاصل مسار ('')، فحدد قيمة الاعتراض على أنها 2 (منخفض)، واستخدم last
لحفظ قيمة الاعتراض للحكم اللاحق.
تابع لتحديد ما إذا كان العنصر الثالث عبارة عن فاصل مسار ('')، إذا كان الأمر كذلك، فهو مسار مطلق، ومعرف اعتراض rootEnd
هو 1 (منخفض)، ولكنه قد يكون أيضًا مسار UNC (servernamesharename). ، اسم خادم اسم الخادم (اسم المورد المشترك). إذا كانت هناك قيم أخرى، فستستمر القيمة المعترضة في الزيادة وقراءة القيم التالية، واستخدم firstPart
لحفظ قيمة البت الثالث بحيث يمكن الحصول على القيمة عند ربط الدليل، والاحتفاظ بالقيم الأخيرة والمعترضة متسقة لإنهاء الحكم.
const len = path. length; Let rootEnd = 0; // نهاية اعتراض المسار Let devices = ''; Let isAbsolute = false; // ما إذا كان مسار جذر القرص const code = StringPrototypeCharCodeAt(path, 0); // طول المسار هو 1 إذا (لين === 1) { // يوجد فاصل مسار واحد فقط للمسار المطلق if (isPathSeparator(code)) { rootEnd = 1; isAbsolute = true; } } وإلا إذا (isPathSeparator(code)) { // قد يكون جذر UNC، بدءًا من المحدد ، والذي يكون واحدًا منه على الأقل نوعًا من المسار المطلق (UNC أو غيره) isAbsolute = true; // ابدأ في مطابقة فاصل المسار المزدوج if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) { دع ي = 2؛ دع الأخير = ي؛ // قم بمطابقة واحد أو أكثر من محددات غير المسار while (j < len && !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { ي++; } إذا (ي < لين && ي !== الأخير) { const firstPart = StringPrototypeSlice(path, last, j); الأخير = ي؛ // تطابق واحد أو أكثر من فواصل المسار while (j < len && isPathSeparator(StringPrototypeCharCodeAt(path, j))) { ي++; } إذا (ي < لين && ي !== الأخير) { الأخير = ي؛ بينما (ي < لين && !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { ي++; } إذا (ي === لين || ي !== الأخير) { الجهاز= `\\${firstPart}\${StringPrototypeSlice(path, last, j)}`; rootEnd = j; } } } } آخر { rootEnd = 1; } // اكتشاف مطابقة دليل جذر القرص: D:، C: } else if (isWindowsDeviceRoot(code) && StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) { الجهاز = StringPrototypeSlice(path, 0, 2); rootEnd = 2; if (len > 2 && isPathSeparator(StringPrototypeCharCodeAt(path, 2))) { isAbsolute = true; جذر إند = 3؛ } }
اكتشف المسار وقم بإنشائه، وتحقق من وجود الدليل الجذر للقرص أو حل ما إذا كان resolvedAbsolute
هو المسار المطلق.
// اكتشف دليل جذر القرص إذا (device.length > 0) { // يمتلك الجهاز قيمة إذا (resolvedDevice.length > 0) { إذا (StringPrototypeToLowerCase(device) !== StringPrototypeToLowerCase(resolvedDevice)) يكمل؛ } آخر { // ليس لدىsolveDevice قيمة ويتم تعيين قيمة الدليل الجذر للقرصsolveDevice = Device; } } // المسار المطلق إذا (تم حله) { // توجد حلقة نهائية في حالة وجود دليل جذر القرص (resolvedDevice.length > 0) استراحة؛ } آخر { // احصل على بادئة المسار للربطsolveTail = `${StringPrototypeSlice(path, rootEnd)}\${resolvedTail}`; solveAbsolute =isAbsolute; إذا (isAbsolute &&solveDevice.length > 0) { // تنتهي الحلقة عند وجود جذر القرص. } }
ينفذ الانضمام ربط المسار بناءً على أجزاء المسار الواردة
تلقي معلمات متعددة، واستخدام فواصل محددة كمحددات لربط جميع معلمات المسار معًا وإنشاء مسار عادي جديد.
بعد استلام المعلمات، قم بالتحقق منها. إذا لم تكن هناك معلمات، فسوف تُرجع "." مباشرة. وإلا، فسيتم اجتياز كل معلمة والتحقق throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
من خلال طريقة validateString
المضمنة. throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
join
حرف الهروب هي أنه عند استخدامه بمفرده، يتم اعتباره هروبًا من السلسلة بعد الشرطة المائلة، لذلك يتم استخدام خطوط مائلة عكسية مزدوجة للهروب من الشرطة المائلة العكسية ('').
وأخيرًا، يتم التحقق من السلسلة المتسلسلة وإعادتها بالتنسيق.
إذا (args. length === 0) يعود '.'؛ دعونا انضم؛ دع الجزء الأول؛ // كشف المعلمات من اليسار إلى اليمين for (let i = 0; i < args.length; ++i) { const arg = args[i]; // داخلي/المدققين validateString(arg, 'path'); إذا (arg. length > 0) { إذا (انضم === غير محدد) // قم بتعيين السلسلة الأولى للانضمام، واستخدم متغير firstPart لحفظ السلسلة الأولى لاستخدامها لاحقًا join = firstPart = arg; آخر // الانضمام له قيمة، قم بإجراء += عملية الربط join += `\${arg}`; } } إذا (انضم === غير محدد) return '.'
ضمن نظام النوافذ، تكون معالجة مسار الشبكة مطلوبة بسبب استخدام الخط المائل العكسي ('') ومسار UNC (يشير بشكل أساسي إلى اسم Windows 2000 الكامل للموارد على الشبكة المحلية)، ('') يمثل تنسيق مسار الشبكة، وبالتالي فإن طريقة join
المثبتة ضمن Win32 سيتم اعتراضها بشكل افتراضي.
إذا تمت مطابقة شرطة مائلة عكسية ('')، فسيتم زيادة slashCount
وطالما أن هناك أكثر من خطين مائلين عكسيين ('') متطابقين، فسيتم اعتراض المسار المقسم وربطه يدويًا والهروب منه. '').
دع needReplace = صحيح؛ دع slashCount = 0؛ // استخرج كود الكود الخاص بالسلسلة الأولى بالتسلسل وفقًا لـ StringPrototypeCharCodeAt، وقم بمطابقته مع كود الكود المحدد من خلال طريقة isPathSeparator if (isPathSeparator(StringPrototypeCharCodeAt(firstPart, 0))) { ++slashCount; const firstLen = firstPart.length; إذا (firstLen > 1 && isPathSeparator(StringPrototypeCharCodeAt(firstPart, 1))) { ++slashCount; إذا (فيرست لين > 2) { إذا (isPathSeparator(StringPrototypeCharCodeAt(firstPart, 2))) ++slashCount; آخر { needReplace = false; } } } } إذا (يحتاج إلى استبدال) { بينما (slashCount < join.length && isPathSeparator(StringPrototypeCharCodeAt(joined, slashCount))) { slashCount++; } إذا (slashCount >= 2) join = `\${StringPrototypeSlice(joined, slashCount)}`; }
فرز نتائج التنفيذ
حل | الانضمام | ||
---|---|---|---|
على معلمات | ، | المسار المطلق للملف الحالي | ،|
مسار مطلق | |||
، | يتم ربط المسار المطلق للملف الحالي | بالترتيب | |
تقسيمه إلى المسار المطلق | للمسارات اللاحقة غير المطلقة. | ||
المعلمة | اللاحقة للمسار هي | معلمة | مسار مطلقة|
هي (./) | ولها معلمات لاحقة. لا تحتوي معلمة ربط المسار المطلق للملف الحالي على معلمات | لاحقة بواسطة المعلمات اللاحقة لا تحتوي على معلمات لاحقة (./) | |
تحليلها | |||
على | معلمات | لاحقة | |
المعلمة الأولى هي (../) | وهناك معلمات لاحقة. لا تحتوي معلمات الربط بعد دليل المستوى الأخير الذي يغطي المسار المطلق للملف الحالي | على معلمات لاحقة الربط لا توجد معلمات لاحقة للمعلمات اللاحقة (../) | |
سيتم الكتابة فوق دليل المستوى العلوي الذي يظهر فيه (../) | . | سيتم الكتابة فوق دليل المستوى العلوي. بعد العودة (/)، سيتم ربط المعلمات اللاحقة وسيتم | تغطية الدليل العلوي الذي يظهر (../). بعد الكتابة فوق الدليل العلوي، |
بعد التعليمات البرمجية المصدر، وستقوم طريقة resolve
بمعالجة المعلمات والنظر في شكل المسار ورمي المسار المطلق في النهاية. عند استخدامه، إذا كنت تقوم بعمليات مثل الملفات، فمن المستحسن استخدام طريقة resolve
. وبالمقارنة، ستعيد طريقة resolve
مسارًا حتى إذا لم تكن هناك معلمات ليعملها المستخدم، وستتم معالجة المسار أثناء عملية التنفيذ. تقوم طريقة join
فقط بإجراء الربط القياسي للمعلمات الواردة، وهو أكثر عملية لإنشاء مسار جديد ويمكن إنشاؤه وفقًا لرغبات المستخدم. ومع ذلك، كل طريقة لها مزاياها، ويجب عليك اختيار الطريقة المناسبة وفقًا لسيناريوهات الاستخدام الخاصة بك واحتياجات المشروع.