開発プロセスでは、V8 が提供する機能を使用して JS の機能を拡張する Node.js がよく使用されます。 Node.js では、JS には存在しないパス モジュールを使用できます。アプリケーションをよりよく理解するために、それを見てみましょう~
この記事の Node.js バージョンは 16.14.0 です。 、この記事のソースコードはここから来ています バージョン。この記事を読んだ後、皆さんがソースコードを読むのに役立つことを願っています。
パスは、ファイルとディレクトリのパスを処理するために使用されます。このモジュールは、開発者が複雑なパスの判断を支援し、開発効率を向上させるために便利なツール機能をいくつか提供します。例:
プロジェクト内でエイリアスを構成します。エイリアスを構成すると、ファイルの参照が容易になり、段階的に上方へ検索する必要がなくなります。
レスラブ: { エイリアス: { // 現在のファイルが配置されている __dirname ディレクトリ パス 'src': path.resolve(__dirname, './src'), // process.cwd の現在の作業ディレクトリ '@': path.join(process.cwd(), 'src'), }、webpack では
、
独自の設定を通じて、指定された場所にファイルの出力パスを生成することもできます。
module.exports = { エントリ: './path/to/my/entry/file.js', 出力: { パス: path.resolve(__dirname, 'dist'), ファイル名: 'my-first-webpack.bundle.js', }、または
、
フォルダー操作の場合は、
fs = require("fs"); とします。 let path = require("パス"); // フォルダーを削除 let deleDir = (src) => { // フォルダーを読み取ります let Children = fs.readdirSync(src); Children.forEach(item => { let childpath = path.join(src, item); // ファイルが存在するかどうかを確認します let file = fs.statSync(childpath).isFile(); if (ファイル) { // ファイルが存在する場合は削除 fs.unlinkSync(childpath) } それ以外 { // フォルダーの検出を続行します delDir(childpath) } }) // 空のフォルダーを削除 fs.rmdirSync(src) } deleDir("../floor") は、
path の使用シナリオを簡単に理解します。次に、その実行メカニズムと、その使用方法に基づいて実装される方法を検討します。
pathモジュールを導入し、pathのツール関数を呼び出すと、ネイティブモジュールの処理ロジックに入ります。
_load
関数を使用して、IDとして導入したモジュール名を使用して、ロードされるモジュールがネイティブJSモジュールであると判断すると、 loadNativeModule
関数はIDを使用して対応するASCIIコード( _source
)を見つけます。ネイティブ JS モジュールを保存するソース コード文字列)、データがネイティブ JS モジュールにロードされます。
lib/path.jsファイルを実行して、オペレーティングシステムに応じて、ファイル処理に格差がありますが、メソッドはほぼ同じです電話をかけてきた人。
解像度電流パスの絶対パスは、
新しい絶対パスを生成するために順番に複数のパラメーターを解決します。
solve(...args) { 解決済みデバイス = ''; 解決済みテール = ''; 解決済み絶対 = 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)
、要件が満たされている場合、パスの長さが判断されます。値がある場合は、次のステップで += パスが使用されます。
パスを許可します。 if (i >= 0) { パス = args[i]; // 内部/バリデーター validateString(パス, 'パス'); // path 长度为0 的话,会直接跳出上述代码块的for 循环if (path.length === 0) { 続く; } else if (resolvedDevice.length === 0) { //resolvedDevice の長さは 0 なので、その値を現在の作業ディレクトリとして path に割り当てます。 path = process.cwd(); } それ以外 { // 値を環境オブジェクトまたは現在の作業ディレクトリのパスに割り当てます = process.env[`=${resolvedDevice}`] process.cwd(); if (パス === 未定義 || (StringPrototypeToLowerCase(StringPrototypeSlice(path, 0, 2)) !== StringPrototypeToLowerCase(resolvedDevice) && StringPrototypeCharCodeAt(path, 2) === CHAR_BACKWARD_SLASH)) { // 对path 进行非空与绝对路径判断得出path 路径path = `${resolvedDevice}\`; } }
ルート パスとの一致を試み、パス区切り文字 ('') が 1 つだけであるか、パスが絶対パスであるかを判断し、絶対パスをマークしてrootEnd
インターセプト フラグを 1 (添え字) に設定します。 2 番目の項目がまだパス区切り文字 ('') である場合は、インターセプト値を 2 (添え字) として定義し、後続の判断のためにインターセプト値を保存するためにlast
を使用します。
Continue to determine whether the third item is a path separator (''). If so, it is an absolute path, and the rootEnd
interception identifier is 1 (subscript), but it may also be a UNC path (servernamesharename 、servername サーバー名)。 If there are other values, the intercepted value will continue to increment and read the following values, and use firstPart
to save the value of the third bit so that the value can be obtained when splicing the directory, and keep the last and intercepted values 判決を終わらせるために一貫して。
const len = path.length; let rootEnd = 0; // パスインターセプト終了サブスクリプト let device = '' // ディスクルート D:, C:; let isAbsolute = false; // ディスクルートパスかどうか const code = StringPrototypeCharCodeAt(path, 0); // パスの長さは 1 if (レン === 1) { // 絶対パスにはパス区切り文字 が 1 つだけあります if (isPathSeparator(code)) { ルートエンド = 1; 絶対 = true; } else if (isPathSeparator(code)) { // 区切り文字 で始まる UNC ルートの可能性があり、少なくとも 1 つは何らかの絶対パス (UNC またはその他) です 絶対 = true; // 二重パス区切り文字の照合を開始 if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) { j = 2 とします。 最後に = j; // 1 つ以上の非パス区切り文字と一致します while (j < len && !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { j++; } if (j < len && j !== last) { const firstPart = StringPrototypeSlice(path, last, j); 最後 = j; // 1 つ以上のパス区切り文字と一致します while (j < len && isPathSeparator(StringPrototypeCharCodeAt(path, j))) { j++; } if (j < len && j !== last) { 最後 = j; while (j < len && !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { j++; } if (j === len || j !== last) { デバイス= `\ $ {firstPart} \ $ {StringProtopeSlice(Path、last、j)}`; rootEnd = j; } } } } それ以外 { ルートエンド = 1; } // 检测磁盘根目录匹配例:D:,C: } else if (isWindowsDeviceRoot(code) && StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) { デバイス = StringPrototypeSlice(パス, 0, 2); ルートエンド = 2; if (len > 2 && isPathSeparator(StringPrototypeCharCodeAt(path, 2))) { 絶対 = true; ルートエンド = 3; }
パスを検出して生成し、ディスクのルート ディレクトリが存在するかどうかを確認するか、 resolvedAbsolute
が絶対パスであるかどうかを解決します
。
//ディスクのルートディレクトリを検出 if (device.length > 0) { //resolvedDevice に値がある if (resolvedDevice.length > 0) { if (StringPrototypeToLowerCase(デバイス) !== StringPrototypeToLowerCase(resolvedDevice)) 続く; } それ以外 { //resolvedDevice には値がなく、ディスク ルート ディレクトリの値が割り当てられます。 } } // 絶対パス if (resolvedAbsolute) { // ディスクのルート ディレクトリが存在する場合は終了ループが発生します (resolvedDevice.length > 0) 壊す; } それ以外 { // スプライシング用のパスのプレフィックスを取得しますresolvedTail = `${StringPrototypeSlice(path, rootEnd)}\${resolvedTail}`; 解決済み絶対 = isAbsolute; if (isAbsolute &&solvedDevice.length > 0) { // ディスクルートが存在するとループは終了します Break; }join は
、
受信パス フラグメントに基づいてパス スプライシングを実行します。
接收多个参数,利用特定分隔符作为定界符将所有的path 参数连接在一起,生成新的规范化路径。
パラメーターを受信した後、パラメーターがない場合throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
それ以外の場合は、違反がある場合は各パラメーターを直接validateString
します。 throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
エスケープ文字のjoin
は、単独で使用すると、スラッシュの後の文字列をエスケープするとみなされるため、バックスラッシュ ('') をエスケープするために 2 つのバックスラッシュが使用されます。
最后进行拼接后的字符串校验并格式化返回。
if (引数の長さ === 0) 戻る '。'; 参加しましょう。 最初のパートをみましょう。 // 从左到右检测参数for (let i = 0; i < args.length; ++i) { const arg = args[i]; // 内部/バリデーター validateString(arg, 'パス'); if (引数の長さ > 0) { if (結合 === 未定義) // 最初の文字列を join に割り当て、firstPart 変数を使用して最初の文字列を保存し、後で使用できるようにします join = firstPart = arg; それ以外 // join には値があるので、+= スプライシング操作を実行します join += `\${arg}`; } } if (結合 === 未定義) return '。';
ウィンドウシステムの下では、バックスラッシュ( '')とunc(主にLANのリソースの名前を参照)の使用が必要です。 represents is a network path format, so the join
method mounted under win32 will intercept by default.
バックスラッシュ ('') が一致すると、 slashCount
が増加します。 2 つ以上のバックスラッシュ ('') が一致する限り、結合されたパスはインターセプトされ、手動で結合されてエスケープされます。 '')。
let needsReplace = 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 = false; } } } } if (replace が必要) { while (slashCount < join.length && isPathSeparator(StringPrototypeCharCodeAt(joined,lashCount))) { スラッシュカウント++; } if (スラッシュカウント >= 2) 結合 = `\${StringPrototypeSlice(結合, スラッシュカウント)}`;実行
結果
のソート
replace | join に | ||
---|---|---|---|
パラメータがありませ | ん | 。 | |
パラメータには絶対パスがありませ | ん | 。 | |
最初のパラメータは絶対パスを上書きします。 | 現在のファイルの絶対パスに結合され、 | 後続の非絶対パスの絶対パスに結合されます。 | この|
絶対 | パスを上書きし、そのパスで結合されたパス | を上書きします。 | 事前パラメータ。|
最初のパラメータは (./) であり | 、後続のパラメータがあります。現在のファイルの絶対パス スプライシング パラメータには後続のパラメータがありません。パスには | 後続のパラメータがあります | 。後続のパラメータには後続のパラメータがありません (./) |
後続のパラメータには (./) があり | 、解析された絶対パスのスプライシング パラメータ | には後続のパラメータがありません | |
。 first parameter is (../) | and there are subsequent parameters. The splicing parameters after the last level directory covering the absolute path of the current file have no subsequent parameters. The last level directory covering the absolute path of the current file | has subsequent parameters . Splicing There are no subsequent parameters for subsequent parameters. (../) | |
The post-parameter has (../ | ). The upper-level directory where (../) appears will be overwritten. The number of subsequent parameters will be上書きされます。リターン(/)後、後続のパラメータが結合され、サフィックスに | 表示される上位ディレクトリ(../)が上書きされます。上位ディレクトリが上書きされた後、 |
コードの後に、 resolve
メソッドがパラメータを処理し、パスの形式を考慮して、最後に絶対パスをスローします。使用する場合、ファイルなどの操作を実行する場合は、 resolve
メソッドを使用することをお勧めします。これに対して、 resolve
メソッドは、ユーザーが操作するパラメーターがなくてもパスを返し、そのパスを処理します。実行プロセス中。 join
メソッドは、入力パラメータの標準化されたスプライシングのみを実行します。これは、新しいパスを生成する場合により実用的であり、ユーザーの希望に応じて作成できます。ただし、それぞれの方法に利点があります。独自の使用シナリオとプロジェクトのニーズに応じて、適切な方法を選択する必要があります。