ในระหว่างกระบวนการพัฒนา มักจะใช้ 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'), - }
ใน webpack สามารถสร้างเส้นทางเอาต์พุตของไฟล์ไปยังตำแหน่งที่ระบุผ่านการกำหนดค่าของเราเอง
โมดูล.ส่งออก = { รายการ: './path/to/my/entry/file.js', เอาท์พุท: { เส้นทาง: path.resolve(__dirname, 'dist'), ชื่อไฟล์: 'my-first-webpack.bundle.js', - };
หรือสำหรับการทำงานของโฟลเดอร์
ให้ fs = need("fs"); ให้ path = need("path"); // ลบโฟลเดอร์ ให้ deleDir = (src) => { // อ่านโฟลเดอร์ให้เด็ก ๆ = fs.readdirSync(src); เด็ก.forEach (รายการ => { ให้ childpath = path.join(src, item); // ตรวจสอบว่ามีไฟล์อยู่หรือไม่ ให้ file = fs.statSync(childpath).isFile(); ถ้า (ไฟล์) { // ลบไฟล์หากมีอยู่ fs.unlinkSync(childpath) } อื่น { // ดำเนินการตรวจสอบโฟลเดอร์ deleDir (childpath) ต่อไป - - // ลบโฟลเดอร์ว่าง fs.rmdirSync(src) - deleDir("../floor")
เข้าใจสถานการณ์การใช้งานของพาธโดยสังเขป ต่อไป เราจะศึกษากลไกการดำเนินการและวิธีการนำไปใช้ตามการใช้งาน
เมื่อมีการแนะนำโมดูลพาธและเรียกใช้ฟังก์ชันเครื่องมือของพาธ ตรรกะการประมวลผลของโมดูลดั้งเดิมจะถูกป้อน
ใช้ฟังก์ชัน _load
เพื่อใช้ชื่อโมดูลที่คุณแนะนำเป็น ID เพื่อพิจารณาว่าโมดูลที่จะโหลดนั้นเป็นโมดูล JS ดั้งเดิม หลังจากนั้น ฟังก์ชัน loadNativeModule
จะถูกใช้เพื่อใช้ id เพื่อค้นหารหัส ASCII ที่เกี่ยวข้องจาก _source
( สตริงซอร์สโค้ดที่บันทึกโมดูล JS ดั้งเดิม) ข้อมูลจะถูกโหลดลงในโมดูล JS ดั้งเดิม
ดำเนินการไฟล์ lib/path.js และใช้กระบวนการเพื่อกำหนดระบบปฏิบัติการ อาจมีการประมวลผลที่แตกต่างกันของอักขระปฏิบัติการในการประมวลผลไฟล์ แต่วิธีการจะเหมือนกันโดยประมาณหลังจากประมวลผลแล้ว ผู้โทร
แก้ไขส่งกลับเส้นทางที่แน่นอนของเส้นทางปัจจุบัน
แก้ไขประกบพารามิเตอร์หลายตัวตามลำดับเพื่อสร้างเส้นทางที่แน่นอนใหม่
แก้ไข (... args) { ให้แก้ไขอุปกรณ์ = ''; ให้ solvedTail = ''; ให้ SolvedAbsolute = false; // ตรวจจับพารามิเตอร์จากขวาไปซ้ายสำหรับ (ให้ i = args.length - 1; i >= -1; i--) { - - // เส้นทางที่ทำให้เป็นมาตรฐาน solvedTail = NormalizeString (resolvedTail, !resolvedAbsolute, '\', isPathSeparator); กลับได้รับการแก้ไขแน่นอน? `${resolvedDevice}\${resolvedTail}` : `${resolvedDevice}${resolvedTail}` ||.'; -
รับเส้นทางตามพารามิเตอร์ สำรวจพารามิเตอร์ที่ได้รับ เริ่มประกบเมื่อความยาวของพารามิเตอร์มากกว่าหรือเท่ากับ 0 ทำการตรวจสอบแบบไม่มีสตริงบนเส้นทางที่ประกบกัน หากมีพารามิเตอร์ใด ๆ ที่ไม่ตรงกัน throw new ERR_INVALID_ARG_TYPE(name, 'string', value)
หากเป็นไปตามข้อกำหนด ความยาวของเส้นทางจะถูกตัดสิน หากมีค่า += path จะถูกนำมาใช้สำหรับขั้นตอนต่อไป
ให้เส้นทาง; ถ้า (ผม >= 0) { เส้นทาง = args[i]; // ภายใน/เครื่องมือตรวจสอบความถูกต้อง validateString(เส้นทาง, 'เส้นทาง'); // หากความยาวของเส้นทางเป็น 0 มันจะกระโดดออกจากลูป for ของบล็อกโค้ดด้านบนโดยตรงถ้า (path.length === 0) { ดำเนินการต่อ; - } อื่นถ้า (resolvedDevice.length === 0) { //ความยาวของ SolvedDevice คือ 0 กำหนดค่าให้กับพาธเป็นไดเร็กทอรีการทำงานปัจจุบัน path = process.cwd(); } อื่น { // กำหนดค่าให้กับวัตถุสภาพแวดล้อมหรือเส้นทางไดเร็กทอรีการทำงานปัจจุบัน = process.env[`=${resolvedDevice}`] || process.cwd(); ถ้า (เส้นทาง === ไม่ได้กำหนด || (StringPrototypeToLowerCase(StringPrototypeSlice(path, 0, 2)) !== StringPrototypeToLowerCase (อุปกรณ์ที่แก้ไขแล้ว) && StringPrototypeCharCodeAt (เส้นทาง 2) === CHAR_BACKWARD_SLASH)) { //ตัดสินเส้นทางไปยังเส้นทางที่ไม่ว่างเปล่าและเส้นทางสัมบูรณ์เพื่อให้ได้เส้นทาง path = `${resolvedDevice}\`; - -
พยายามจับคู่พาธรูท ตรวจสอบว่ามีตัวคั่นพาธเพียงตัวเดียว ('') หรือพาธเป็นพาธสัมบูรณ์ จากนั้นทำเครื่องหมายพาธสัมบูรณ์และตั้งค่าแฟล็กการสกัดกั้น rootEnd
เป็น 1 (ตัวห้อย) หากรายการที่สองยังคงเป็นตัวคั่นเส้นทาง ('') ให้กำหนดค่าการสกัดกั้นเป็น 2 (ตัวห้อย) และใช้ last
เพื่อบันทึกค่าการสกัดกั้นสำหรับการตัดสินในภายหลัง
ดำเนินการต่อเพื่อตรวจสอบว่ารายการที่สามเป็นตัวคั่นเส้นทาง ('') หรือไม่ หากเป็นเช่นนั้น เป็นเส้นทางที่แน่นอน และตัวระบุการสกัดกั้น rootEnd
คือ 1 (ตัวห้อย) แต่อาจเป็นเส้นทาง UNC (servernamesharename) , ชื่อเซิร์ฟเวอร์ ชื่อเซิร์ฟเวอร์) sharename ชื่อทรัพยากรที่ใช้ร่วมกัน) หากมีค่าอื่นๆ ค่าที่ดักจับจะยังคงเพิ่มขึ้นและอ่านค่าต่อไปนี้ และใช้ firstPart
เพื่อบันทึกค่าของบิตที่สามเพื่อให้สามารถรับค่าได้เมื่อประกบไดเร็กทอรี และเก็บค่าสุดท้ายและค่าที่ดักไว้ สม่ำเสมอในการยุติคำพิพากษา
const len = path.length; ให้ rootEnd = 0; // ตัวห้อยสิ้นสุดการสกัดกั้นเส้นทาง ให้อุปกรณ์ = ''; // ดิสก์รูท D:, C: ให้ isAbsolute = false; // ไม่ว่าจะเป็นเส้นทางรากของดิสก์ const code = StringPrototypeCharCodeAt (เส้นทาง, 0); // ความยาวเส้นทางคือ 1 ถ้า (เลน === 1) { // มีตัวคั่นพาธเพียงตัวเดียวเท่านั้น สำหรับพาธสัมบูรณ์ถ้า (isPathSeparator(code)) { รากสิ้นสุด = 1; เป็นสัมบูรณ์ = จริง; - } อื่นถ้า (isPathSeparator (รหัส)) { // อาจเป็นรากของ UNC โดยเริ่มต้นด้วยตัวคั่น อย่างน้อยหนึ่งในนั้นเป็นเส้นทางที่แน่นอน (UNC หรืออื่น ๆ ) เป็นสัมบูรณ์ = จริง; // เริ่มจับคู่ตัวคั่นเส้นทางคู่ถ้า (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) { ให้เจ = 2; ให้สุดท้าย = j; // จับคู่ตัวคั่นที่ไม่ใช่เส้นทางตั้งแต่หนึ่งตัวขึ้นไปในขณะที่ (j < len && !isPathSeparator(StringPrototypeCharCodeAt(เส้นทาง, j))) { เจ++; - ถ้า (j < len && j !== สุดท้าย) { const firstPart = StringPrototypeSlice (เส้นทาง, สุดท้าย, j); สุดท้าย = เจ; // จับคู่ตัวคั่นเส้นทางตั้งแต่หนึ่งตัวขึ้นไปในขณะที่ (j < len && isPathSeparator (StringPrototypeCharCodeAt (เส้นทาง, j))) { เจ++; - ถ้า (j < len && j !== สุดท้าย) { สุดท้าย = เจ; ในขณะที่ (j <เลน && !isPathSeparator(StringPrototypeCharCodeAt(เส้นทาง, j))) { เจ++; - ถ้า (j === len || j !== สุดท้าย) { อุปกรณ์= `\\${ส่วนแรก}\${StringPrototypeSlice(เส้นทาง, สุดท้าย, j)}`; rootEnd = เจ; - - - } อื่น { รากสิ้นสุด = 1; - // ตรวจหาไดเร็กทอรีรากของดิสก์ตัวอย่างที่ตรงกัน: D:, C: } อื่นถ้า (isWindowsDeviceRoot (รหัส) && StringPrototypeCharCodeAt (เส้นทาง 1) === CHAR_COLON) { อุปกรณ์ = StringPrototypeSlice (เส้นทาง, 0, 2); รากสิ้นสุด = 2; ถ้า (len > 2 && isPathSeparator (StringPrototypeCharCodeAt (เส้นทาง 2))) { เป็นสัมบูรณ์ = จริง; รากสิ้นสุด = 3; - }
ตรวจหาเส้นทางและสร้าง ตรวจสอบว่ามีไดเร็กทอรีรากของดิสก์อยู่หรือแก้ไขว่า resolvedAbsolute
เป็นเส้นทางที่แน่นอนหรือไม่
// ตรวจหาไดเรกทอรีรากของดิสก์ถ้า (device.length > 0) { // solvedDevice มีค่าถ้า (resolvedDevice.length > 0) { ถ้า (StringPrototypeToLowerCase (อุปกรณ์) !== StringPrototypeToLowerCase (อุปกรณ์ที่แก้ไขแล้ว)) ดำเนินการต่อ; } อื่น { // solvedDevice ไม่มีค่าและได้รับการกำหนดค่าของไดเร็กทอรีรากของดิสก์ solvedDevice = อุปกรณ์; - - // เส้นทางที่แน่นอนถ้า (แก้ไขแล้วแน่นอน) { // มีการวนรอบสุดท้ายหากมีไดเรกทอรีรากของดิสก์อยู่ (resolvedDevice.length > 0) หยุดพัก; } อื่น { // รับคำนำหน้าเส้นทางสำหรับการประกบ solvedTail = `${StringPrototypeSlice(path, rootEnd)}\${resolvedTail}`; ได้รับการแก้ไขแน่นอน = isAbsolute; ถ้า (isAbsolute && แก้ไขอุปกรณ์ความยาว > 0) { // การวนซ้ำจะสิ้นสุดลงเมื่อรูทของดิสก์เสียหาย - }
เข้าร่วมดำเนินการประกบเส้นทางตามส่วนของเส้นทางที่เข้ามา
รับพารามิเตอร์หลายตัว ใช้ตัวแยกเฉพาะเป็นตัวคั่นเพื่อเชื่อมต่อพารามิเตอร์เส้นทางทั้งหมดเข้าด้วยกัน และสร้างเส้นทางมาตรฐานใหม่
หลังจากได้รับพารามิเตอร์ throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
ให้ตรวจสอบ หากไม่มีพารามิเตอร์ จะส่งกลับ validateString
.' โดยตรง throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
join
ของอักขระ Escape คือเมื่อใช้เพียงอย่างเดียว จะถือว่า Escape สตริงหลังเครื่องหมายทับ ดังนั้นจึงใช้เครื่องหมายแบ็กสแลชคู่เพื่อยกเว้นเครื่องหมายแบ็กสแลช ('')
ในที่สุด สตริงที่ต่อกันจะได้รับการตรวจสอบและส่งคืนในรูปแบบ
ถ้า (หาเรื่องความยาว === 0) กลับ '.'; มาร่วม; ให้ก่อนส่วน; // ตรวจจับพารามิเตอร์จากซ้ายไปขวาสำหรับ (ให้ i = 0; i < args.length; ++i) { const หาเรื่อง = หาเรื่อง [i]; // ภายใน/เครื่องมือตรวจสอบความถูกต้อง validateString(arg, 'เส้นทาง'); ถ้า (ความยาวหาเรื่อง > 0) { ถ้า (เข้าร่วม === ไม่ได้กำหนด) // กำหนดสตริงแรกที่จะเข้าร่วม และใช้ตัวแปร firstPart เพื่อบันทึกสตริงแรกเพื่อใช้ในภายหลัง join = firstPart = arg; อื่น // เข้าร่วมมีค่าดำเนินการ += การดำเนินการประกบเข้าร่วม += `\${arg}`; - - ถ้า (เข้าร่วม === ไม่ได้กำหนด) return '.';
ภายใต้ระบบหน้าต่าง จำเป็นต้องมีการประมวลผลเส้นทางเครือข่ายเนื่องจากการใช้แบ็กสแลช ('') และ UNC (ส่วนใหญ่อ้างอิงถึงชื่อทรัพยากร Windows 2000 ที่สมบูรณ์บนเส้นทาง LAN) ('') แสดงถึงรูปแบบเส้นทางเครือข่าย ดังนั้นวิธี join
ที่ติดตั้งภายใต้ win32 จะถูกดักจับตามค่าเริ่มต้น
หากจับคู่แบ็กสแลช ('') slashCount
จะเพิ่มขึ้น ตราบใดที่มีแบ็กสแลชมากกว่าสองอัน ('') ที่ตรงกัน เส้นทางที่ประกบกันจะถูกดักและประกบกันด้วยตนเองและหลบหนี ( '')
ให้ needReplace = จริง; ให้ slashCount = 0; // แยกโค้ดโค้ดของสตริงแรกตามลำดับตาม StringPrototypeCharCodeAt และจับคู่กับโค้ดโค้ดที่กำหนดผ่านเมธอด isPathSeparator if (isPathSeparator(StringPrototypeCharCodeAt(firstPart, 0))) { ++สแลชนับ; const firstLen = firstPart.length; ถ้า (firstLen > 1 && isPathSeparator (StringPrototypeCharCodeAt (ส่วนแรก, 1))) { ++สแลชนับ; ถ้า (firstLen > 2) { ถ้า (isPathSeparator (StringPrototypeCharCodeAt (FirstPart, 2))) ++สแลชนับ; อื่น { needReplace = เท็จ; - - - - ถ้า (ต้องการแทนที่) { ในขณะที่ (slashCount < join.length && isPathSeparator (StringPrototypeCharCodeAt (เข้าร่วม, slashCount))) { slashCount++; - ถ้า (slashCount >= 2) เข้าร่วม = `\${StringPrototypeSlice(เข้าร่วม, slashCount)}`; }
การเรียงลำดับผลลัพธ์การดำเนินการ
การแก้ไขการ | รวม | |
---|---|---|
ไม่มีพารามิเตอร์ | เส้นทาง | ที่แน่นอนของไฟล์ปัจจุบัน |
พารามิเตอร์ไม่มี | เส้นทางที่แน่นอนของไฟล์ปัจจุบันถูกเชื่อม | ต่อตาม |
ลำดับ | ของไฟล์ปัจจุบันและถูกต่อเข้ากับพาธสัมบูรณ์ของ | พาธที่ไม่แน่นอนที่ตามมา | พา
ธ | จะเขียนทับพาธสัมบูรณ์ของไฟล์ปัจจุบันและเขียนทับ | พาธที่ประกบกันด้วย | พารามิเตอร์ล่วงหน้า
พารามิเตอร์แรกคือ (./) | และมีพารามิเตอร์ที่ตามมา พารามิเตอร์ splicing เส้นทางสัมบูรณ์ของไฟล์ปัจจุบันไม่มีพารามิเตอร์ที่ตาม | มา โดยพารามิเตอร์ที่ตามมาไม่มีพารามิเตอร์ที่ตามมา (./) |
พารามิเตอร์หลังมี (./) | พารามิเตอร์การแยกวิเคราะห์เส้นทางสัมบูรณ์ | มีพารามิเตอร์ที่ตามมาไม่มีพารามิเตอร์ที่ตามมาและการประกบกัน (/ |
) พารามิเตอร์แรกคือ (../) | และมีพารามิเตอร์ที่ตามมา พารามิเตอร์ splicing หลังจากไดเร็กทอรีระดับสุดท้ายที่ครอบคลุมเส้นทางสัมบูรณ์ของไฟล์ปัจจุบันไม่มีพารามิเตอร์ที่ตาม | มา การเชื่อมต่อ ไม่มีพารามิเตอร์ที่ตามมาสำหรับพารามิเตอร์ที่ตามมา (../) |
พารามิเตอร์หลังมี (../ | )ไดเรกทอรีระดับบนที่ (../) ปรากฏจะถูกเขียนทับ เขียนทับ ไดเร็กทอรีระดับบนจะถูกเขียนทับ หลังจากนั้น ให้ส่งคืน (/) พารามิเตอร์ที่ตามมาจะถูกเชื่อมต่อและ | ไดเร็กทอรีด้านบนที่ปรากฏ (../) จะถูกเขียนทับจำนวนเลเยอร์ที่ปรากฏในส่วนต่อท้าย หลังจากที่ไดเร็กทอรีด้านบนถูกเขียนทับ |
ถูกอ่าน หลังจากซอร์สโค้ด วิธี resolve
จะประมวลผลพารามิเตอร์ พิจารณารูปแบบของเส้นทาง และโยนเส้นทางสัมบูรณ์ออกไปที่ส่วนท้าย เมื่อใช้งาน หากคุณกำลังดำเนินการต่างๆ เช่น ไฟล์ ขอแนะนำให้ใช้วิธี resolve
ในการเปรียบเทียบ วิธี resolve
จะส่งคืนเส้นทางแม้ว่าจะไม่มีพารามิเตอร์ให้ผู้ใช้ดำเนินการก็ตาม และเส้นทางจะถูกประมวลผล ในระหว่างกระบวนการดำเนินการ วิธี join
จะดำเนินการประกบพารามิเตอร์ขาเข้าที่เป็นมาตรฐานเท่านั้น ซึ่งมีประโยชน์มากกว่าสำหรับการสร้างเส้นทางใหม่และสามารถสร้างได้ตามความต้องการของผู้ใช้ อย่างไรก็ตาม แต่ละวิธีมีข้อดีของตัวเอง คุณควรเลือกวิธีที่เหมาะสมตามสถานการณ์การใช้งานและความต้องการของโครงการ