개발 과정에서는 JS의 기능을 확장하기 위해 V8에서 제공하는 기능을 사용하는 Node.js가 자주 사용됩니다. Node.js에서는 JS에는 없는 path 모듈을 사용할 수 있는데, 애플리케이션에 좀 더 친숙해질 수 있도록 살펴보겠습니다~
이 글의 Node.js 버전은 16.14.0입니다. , 이 기사의 소스 코드는 여기 Version. 이 기사를 읽은 후 소스 코드를 읽는 모든 사람에게 도움이 되기를 바랍니다.
Path는 파일 및 디렉터리의 경로를 처리하는 데 사용됩니다. 이 모듈은 개발자가 복잡한 경로 판단을 내리고 개발 효율성을 향상시키는 데 도움이 되는 개발에 편리한 몇 가지 도구 기능을 제공합니다. 예를 들면 다음과 같습니다
. 프로젝트에서 별칭을 구성하면 파일을 더 쉽게 참조하고 단계별로 위쪽으로 검색할 필요가 없습니다.
다시 사랑하다: { 별칭: { // __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', }, };
또는 폴더 작업의 경우
let fs = require("fs"); let path = require("경로"); // 폴더 삭제 let deleDir = (src) => { // 폴더 읽기 let children = fs.readdirSync(src); children.forEach(항목 => { let childpath = path.join(src, item); // 파일이 존재하는지 확인합니다. let file = fs.statSync(childpath).isFile(); if (파일) { // 파일이 있으면 삭제합니다. fs.unlinkSync(childpath) } 또 다른 { //deleDir(childpath) 폴더 검색을 계속합니다. } }) // 빈 폴더 삭제 fs.rmdirSync(src) } deleDir("../floor")는
path의 사용 시나리오를 간략하게 이해하고 그 실행 메커니즘과 사용법에 따라 어떻게 구현되는지 살펴보겠습니다.
path 모듈이 도입되고 path의 도구 기능이 호출되면 네이티브 모듈의 처리 로직이 입력됩니다.
_load
함수를 사용하여 ID로 도입한 모듈 이름을 사용하여 로드할 모듈이 기본 JS 모듈인지 확인한 후 loadNativeModule
함수를 사용하여 ID를 사용하여 _source
( 네이티브 JS 모듈을 저장하는 소스 코드 문자열) 데이터가 네이티브 JS 모듈에 로드됩니다.
lib/path.js 파일을 실행하고 프로세스를 사용하여 운영체제를 결정합니다. 운영체제에 따라 파일 처리 시 동작 문자의 차등 처리가 있을 수 있으나 처리 후 반환되는 방식은 대략 동일합니다. 발신자.
현재 경로의 절대 경로를 반환합니다.
해결은 여러 매개변수를 순차적으로 연결하여 새로운 절대 경로를 생성합니다.
해결(...args) { 해결된 장치 = ''; 해결됨테일 = ''; receivedAbsolute = false로 설정합니다. // 오른쪽에서 왼쪽으로 매개변수를 감지합니다. for (let 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)
. 요구 사항이 충족되면 경로 길이가 판단됩니다. 값이 있으면 다음 단계에 += 경로가 사용됩니다.
경로를 허용하십시오; 만약 (i >= 0) { 경로 = 인수[i]; // 내부/검증기 verifyString(경로, '경로'); // 경로의 길이가 0이면 위 코드 블록의 for 루프에서 직접 점프합니다. if (path.length === 0) { 계속하다; } } else if (resolvedDevice.length === 0) { //resolvedDevice의 길이는 0이며 현재 작업 디렉터리인 path에 값을 할당합니다. path = process.cwd(); } 또 다른 { // 환경 객체 또는 현재 작업 디렉터리에 값을 할당합니다. path = process.env[`=${resolvedDevice}`] || if (경로 === 정의되지 않음 || (StringPrototypeToLowerCase(StringPrototypeSlice(경로, 0, 2)) !== StringPrototypeToLowerCase(resolvedDevice) && StringPrototypeCharCodeAt(경로, 2) === CHAR_BACKWARD_SLASH)) { //경로를 얻기 위해 비어 있지 않은 절대 경로에 대한 경로를 판단합니다. path = `${resolvedDevice}\`; } }
루트 경로를 일치시키고 경로 구분 기호('')가 하나만 있는지 또는 경로가 절대 경로인지 확인한 다음 절대 경로를 표시하고 rootEnd
차단 플래그를 1(아래 첨자)로 설정합니다. 두 번째 항목이 여전히 경로 구분자('')인 경우 차단 값을 2(아래 첨자)로 정의하고 last
사용하여 후속 판단을 위해 차단 값을 저장합니다.
세 번째 항목이 경로 구분 기호('')인지 계속 확인합니다. 그렇다면 절대 경로이고 rootEnd
차단 식별자는 1(아래 첨자)이지만 UNC 경로(servernamesharename)일 수도 있습니다. , 서버 이름 서버 이름). 공유 이름 공유 리소스 이름). 다른 값이 있으면 가로채는 값은 계속해서 증가하여 다음 값을 읽고 디렉터리를 스플라이싱할 때 값을 얻을 수 있도록 firstPart
사용하여 세 번째 비트의 값을 저장하고 마지막 및 가로채는 값을 유지합니다. 일관되게 판결을 종료합니다.
const len = path.length; let rootEnd = 0; // 경로 차단 끝 첨자 let device = '' // 디스크 루트 D:, C: let isAbsolute = false; // 디스크 루트 경로인지 여부 const code = StringPrototypeCharCodeAt(path, 0); // 경로 길이는 1입니다. if (len === 1) { // 절대 경로에 대한 경로 구분 기호는 하나만 있습니다. if (isPathSeparator(code)) { 루트엔드 = 1; isAbsolute = 사실; } } else if (isPathSeparator(코드)) { // 구분 기호 로 시작하는 UNC 루트일 수 있으며, 그 중 적어도 하나는 일종의 절대 경로(UNC 또는 기타)입니다. isAbsolute = 사실; // 이중 경로 구분 기호 일치를 시작합니다. if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) { j=2라고 하자; 마지막 = j로 두십시오; // 하나 이상의 경로가 아닌 구분 기호와 일치합니다. while (j < len && !isPathSeparator(StringPrototypeCharCodeAt(경로, j))) { j++; } if (j < len && j !== 마지막) { const firstPart = StringPrototypeSlice(경로, 마지막, j); 마지막 = j; // 하나 이상의 경로 구분 기호를 일치시킵니다. while (j < len && isPathSeparator(StringPrototypeCharCodeAt(path, j))) { j++; } if (j < len && j !== 마지막) { 마지막 = j; 동안(j < len && !isPathSeparator(StringPrototypeCharCodeAt(경로, j))) { j++; } if (j === len || j !== 마지막) { 장치= `\\${firstPart}\${StringPrototypeSlice(path, last, j)}`; 루트엔드 = j; } } } } 또 다른 { 루트엔드 = 1; } // 디스크 루트 디렉터리 일치 감지 예: D:, C: } else if (isWindowsDeviceRoot(code) && StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) { 장치 = StringPrototypeSlice(경로, 0, 2); 루트엔드 = 2; if (len > 2 && isPathSeparator(StringPrototypeCharCodeAt(path, 2))) { isAbsolute = 사실; 루트엔드 = 3; } }
경로를 감지하고 생성하고, 디스크 루트 디렉터리가 존재하는지 확인하거나, resolvedAbsolute
절대 경로인지 확인합니다.
//(device.length > 0) {인 경우 디스크 루트 디렉터리를 감지합니다. //solvedDevice에는 값이 있습니다. if (resolvedDevice.length > 0) { if (StringPrototypeToLowerCase(device) !== StringPrototypeToLowerCase(resolvedDevice)) 계속하다; } 또 다른 { // 해결된 장치에는 값이 없으며 디스크 루트 디렉터리의 값이 할당됩니다. 해결된 장치 = 장치; } } // 절대 경로 if (resolvedAbsolute) { // 디스크 루트 디렉터리가 존재하는 경우 종료 루프가 있습니다(resolvedDevice.length > 0). 부서지다; } 또 다른 { // 연결을 위한 경로 접두어를 가져옵니다.solvedTail = `${StringPrototypeSlice(path, rootEnd)}\${resolvedTail}`; 해결된 앱솔루트 = isAbsolute; if (isAbsolute &&solvedDevice.length > 0) { // 디스크 루트가 존재하면 루프가 종료됩니다. break; } }
Join은 들어오는 경로 조각을 기반으로 경로 접합을 수행합니다.
여러 매개변수를 수신하고 특정 구분 기호를 구분 기호로 사용하여 모든 경로 매개변수를 함께 연결하고 새로운 정규화된 경로를 생성합니다.
매개변수를 받은 후 매개변수가 없으면 '.'을 직접 반환합니다. 그렇지 않으면 내장된 validateString
메서드를 통해 각 매개변수를 탐색하고 확인합니다. 위반이 있으면 직접 throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
이스케이프 문자의 join
은 단독으로 사용하면 슬래시 뒤의 문자열을 이스케이프하는 것으로 간주하므로 이중 백슬래시를 사용하여 백슬래시('')를 이스케이프하는 것입니다.
마지막으로 연결된 문자열이 확인되어 형식으로 반환됩니다.
if (인수.길이 === 0) 반품 '.'; 가입하자; 하자 firstPart; // 왼쪽에서 오른쪽으로 매개변수를 감지합니다. for (let i = 0; i < args.length; ++i) { const 인수 = 인수[i]; // 내부/검증기 verifyString(arg, '경로'); if (인수 길이 > 0) { if (결합 === 정의되지 않음) // 조인할 첫 번째 문자열을 할당하고 firstPart 변수를 사용하여 나중에 사용할 수 있도록 첫 번째 문자열을 저장합니다. Join = firstPart = arg; 또 다른 // 결합된 값이 있고 += 접합 작업을 수행합니다. 결합 += `\${arg}`; } } if (결합 === 정의되지 않음) return '.';
윈도우 시스템에서는 백슬래시('') 및 UNC(주로 LAN에 있는 리소스의 전체 Windows 2000 이름을 나타냄) 경로,('') 사용으로 인해 네트워크 경로 처리가 필요합니다. 나타내는 것은 네트워크 경로 형식이므로 win32에 마운트된 join
메서드는 기본적으로 차단합니다.
백슬래시('')가 일치하면 slashCount
증가합니다. 일치하는 백슬래시('')가 2개 이상인 경우 연결된 경로가 가로채어 수동으로 연결되고 이스케이프됩니다(. '').
needReplace = true로 설정하세요. 슬래시카운트 = 0으로 놔두세요; // StringPrototypeCharCodeAt에 따라 순서대로 첫 번째 문자열의 코드 코드를 추출하고, isPathSeparator 메소드를 통해 정의된 코드 코드와 일치시킵니다. if (isPathSeparator(StringPrototypeCharCodeAt(firstPart, 0))) { ++슬래시카운트; const firstLen = firstPart.length; if (firstLen > 1 && isPathSeparator(StringPrototypeCharCodeAt(firstPart, 1))) { ++슬래시카운트; if (firstLen > 2) { if (isPathSeparator(StringPrototypeCharCodeAt(firstPart, 2))) ++슬래시카운트; 또 다른 { needReplace = 거짓; } } } } if (replace 필요) { while(slashCount < Joined.length && isPathSeparator(StringPrototypeCharCodeAt(joined, slashCount))) { 슬래시카운트++; } if (슬래시 개수 >= 2) Joined = `\${StringPrototypeSlice(joined, slashCount)}`; }
실행 결과 정렬
해결 | 조인에는 | |
---|---|---|
매개변수가 | 없습니다 | . |
매개변수에는 절대 경로가 없습니다 | . 현재 파일의 절대 경로는순서대로 | 이어집니다 | .
. | ||
경로는 현재 파일의 절대 경로를 | 덮어 | 쓰며 |
이후 비절대 경로의 절대 경로에 연결됩니다. | ||
변수는 (./) | 이고 후속 매개변수가 있습니다. 현재 파일의 절대 경로 접합 매개변수에는 후속 매개변수가 없습니다. 경로는 | 후속 매개변수를 가지며, 경로가 접합됩니다. 후속 매개변수에는 후속 매개변수가 없습니다. (./) |
이후 매개변수에는 (./) | 파싱된 절대 경로 스플라이싱 매개변수 | 에는 후속 매개변수가 없으며 스플라이싱(/) | 이 있습니다
. 첫 번째 매개변수는 (../) | 이고 후속 매개변수가 있습니다. 현재 파일의 절대 경로를 포함하는 마지막 레벨 디렉토리 이후에는 후속 매개변수가 없습니다 | . . Splicing 후속 매개변수에는 후속 매개변수가 없습니다. (../) |
(../)가 | 표시된 상위 디렉토리는 이후 매개변수 수만큼 덮어쓰게 됩니다. 상위 디렉터리를 덮어쓰게 됩니다. 반환(/)한 후 후속 매개 변수가 연결되고 | 접미사에 나타나는 상위 디렉터리(../)가 덮어쓰여집니다. 상위 디렉터리를 덮어쓴 후 매개변수 연결 |
읽습니다. 소스 코드 후 resolve
메서드는 매개변수를 처리하고 경로 형식을 고려한 후 마지막에 절대 경로를 삭제합니다. 이를 사용할 때, 파일 등의 작업을 수행하는 경우에는 resolve
메소드를 사용하는 것이 좋습니다. 이에 비해 resolve
메소드는 사용자가 조작할 매개변수가 없어도 경로를 반환하고 해당 경로를 처리합니다. 실행 과정 중. join
방법은 들어오는 매개변수의 표준화된 접합만 수행합니다. 이는 새 경로를 생성하는 데 더 실용적이며 사용자의 희망에 따라 생성될 수 있습니다. 그러나 각 방법에는 장점이 있습니다. 자신의 사용 시나리오와 프로젝트 요구 사항에 따라 적절한 방법을 선택해야 합니다.