加入收藏 | 设为首页 | 会员中心 | 我要投稿 银川站长网 (https://www.0951zz.com/)- 云通信、基础存储、云上网络、机器学习、视觉智能!
当前位置: 首页 > 综合聚焦 > 编程要点 > 语言 > 正文

path模块运用场景有哪些 执行机制是怎样

发布时间:2023-09-15 12:42:28 所属栏目:语言 来源:
导读:这篇主要是介绍“path模块使用场景有哪些,执行机制是怎样”的内容了,下文有实例供大家参考,对大家了解操作过程或相关知识有一定的帮助,而且实用性强,希望这篇文章能帮助大家解决path模块使用场景有哪

这篇主要是介绍“path模块使用场景有哪些,执行机制是怎样”的内容了,下文有实例供大家参考,对大家了解操作过程或相关知识有一定的帮助,而且实用性强,希望这篇文章能帮助大家解决path模块使用场景有哪些,执行机制是怎样的问题,下面我们一起来了解看看吧。

path 的常见使用场景

Path 用于处理文件和目录的路径,这个模块中提供了一些便于开发者开发的工具函数,来协助我们进行复杂的路径判断,提高开发效率。例如:

在项目中配置别名,别名的配置方便我们对文件更简便的引用,避免深层级逐级向上查找。

reslove: {

alias: {

// __dirname 当前文件所在的目录路径

'src': path.resolve(__dirname, './src'),

// process.cwd 当前工作目录

'@': path.join(process.cwd(), 'src'),

},

}

在 webpack 中,文件的输出路径也可以通过我们自行配置生成到指定的位置。

module.exports = {

entry: './path/to/my/entry/file.js',

output: {

path: path.resolve(__dirname, 'dist'),

filename: 'my-first-webpack.bundle.js',

},

};

又或者对于文件夹的操作

let fs = require("fs");

let path = require("path");

// 删除文件夹

let deleDir = (src) => {

// 读取文件夹

let children = fs.readdirSync(src);

children.forEach(item => {

let childpath = path.join(src, item);

// 检查文件是否存在

let file = fs.statSync(childpath).isFile();

if (file) {

// 文件存在就删除

fs.unlinkSync(childpath)

} else {

// 继续检测文件夹

deleDir(childpath)

}

})

// 删除空文件夹

fs.rmdirSync(src)

}

deleDir("../floor")

简单的了解了一下 path 的使用场景,接下来我们根据使用来研究一下它的执行机制,以及是怎么实现的。

path 的执行机制

引入 path 模块,调用 path 的工具函数的时候,会进入原生模块的处理逻辑。

使用 _load 函数根据你引入的模块名作为 ID,判断要加载的模块是原生 JS 模块后,会通过 loadNativeModule 函数,利用 id 从 _source (保存原生JS模块的源码字符串转成的 ASCII 码)中找到对应的数据加载原生 JS 模块。

执行 lib/path.js 文件,利用 process 判断操作系统,根据操作系统的不同,在其文件处理上可能会存在操作字符的差异化处理,但方法大致一样,处理完后返回给调用方。

常用工具函数简析

resolve 返回当前路径的绝对路径

resolve 将多个参数,依次进行拼接,生成新的绝对路径。

resolve(...args) {

let resolvedDevice = '';

let resolvedTail = '';

let resolvedAbsolute = false;

// 从右到左检测参数

for (let i = args.length - 1; i >= -1; i--) {

......

}

// 规范化路径

resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\', isPathSeparator);

return resolvedAbsolute ?

`${resolvedDevice}\\${resolvedTail}` :

`${resolvedDevice}${resolvedTail}` || '.';

}

根据参数获取路径,对接收到的参数进行遍历,参数的长度大于等于 0 时都会开始进行拼接,对拼接好的 path 进行非字符串校验,有不符合的参数则抛出 throw new ERR_INVALID_ARG_TYPE(name, 'string', value), 符合要求则会对 path 进行长度判断,有值则 +=path 做下一步操作。

let path;

if (i >= 0) {

path = args[i];

// internal/validators

validateString(path, 'path');

// path 长度为 0 的话,会直接跳出上述代码块的 for 循环

if (path.length === 0) {

continue;

}

} else if (resolvedDevice.length === 0) {

// resolvedDevice 的长度为 0,给 path 赋值为当前工作目录

path = process.cwd();

} else {

// 赋值为环境对象或者当前工作目录

path = process.env[`=${resolvedDevice}`] || process.cwd();

if (path === undefined ||

(StringPrototypeToLowerCase(StringPrototypeSlice(path, 0, 2)) !==

StringPrototypeToLowerCase(resolvedDevice) &&

StringPrototypeCharCodeAt(path, 2) === CHAR_BACKWARD_SLASH)) {

// 对 path 进行非空与绝对路径判断得出 path 路径

path = `${resolvedDevice}\\`;

}

}

尝试匹配根路径,判断是否是只有一个路径分隔符 ('\') 或者 path 为绝对路径,然后给绝对路径打标,并把 rootEnd 截取标识设为 1 (下标)。第二项若还是路径分隔符 ('\') ,就定义截取值为 2 (下标),并用 last 保存截取值,以便后续判断使用。

继续判断第三项是否是路径分隔符 ('\'),如果是,那么为绝对路径,rootEnd 截取标识为 1 (下标),但也有可能是 UNC 路径 ( \servername\sharename,servername 服务器名。sharename 共享资源名称)。如果有其他值,截取值会继续进行自增读取后面的值,并用 firstPart 保存第三位的值,以便拼接目录时取值,并把 last 和截取值保持一致,以便结束判断。

const len = path.length;

let rootEnd = 0; // 路径截取结束下标

let device = ''; // 磁盘根 D:\、C:\

let isAbsolute = false; // 是否是磁盘根路径

const code = StringPrototypeCharCodeAt(path, 0);

// path 长度为 1

if (len === 1) {

// 只有一个路径分隔符 \ 为绝对路径

if (isPathSeparator(code)) {

rootEnd = 1;

isAbsolute = true;

}

} else if (isPathSeparator(code)) {

// 可能是 UNC 根,从一个分隔符 \ 开始,至少有一个它就是某种绝对路径(UNC或其他)

isAbsolute = true;

// 开始匹配双路径分隔符

if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) {

let j = 2;

let last = j;

// 匹配一个或多个非路径分隔符

while (j < len &&

!isPathSeparator(StringPrototypeCharCodeAt(path, j))) {

j++;

}

if (j < len && j !== last) {

const firstPart = StringPrototypeSlice(path, last, j);

last = j;

// 匹配一个或多个路径分隔符

while (j < len &&

isPathSeparator(StringPrototypeCharCodeAt(path, j))) {

j++;

}

if (j < len && j !== last) {

last = j;

while (j < len &&

!isPathSeparator(StringPrototypeCharCodeAt(path, j))) {

j++;

}

if (j === len || j !== last) {

device =

`\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`;

rootEnd = j;

}

}

}

} else {

rootEnd = 1;

}

// 检测磁盘根目录匹配 例:D:,C:\

} else if (isWindowsDeviceRoot(code) && StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) {

device = StringPrototypeSlice(path, 0, 2);

rootEnd = 2;

if (len > 2 && isPathSeparator(StringPrototypeCharCodeAt(path, 2))) {

isAbsolute = true;

rootEnd = 3;

}

}

检测路径并生成,检测磁盘根目录是否存在或解析 resolvedAbsolute 是否为绝对路径。

// 检测磁盘根目录

if (device.length > 0) {

// resolvedDevice 有值

if (resolvedDevice.length > 0) {

if (StringPrototypeToLowerCase(device) !==

StringPrototypeToLowerCase(resolvedDevice))

continue;

} else {

// resolvedDevice 无值并赋值为磁盘根目录

resolvedDevice = device;

}

}

// 绝对路径

if (resolvedAbsolute) {

// 磁盘根目录存在结束循环

if (resolvedDevice.length > 0)

break;

} else {

// 获取路径前缀进行拼接

resolvedTail =

`${StringPrototypeSlice(path, rootEnd)}\\${resolvedTail}`;

resolvedAbsolute = isAbsolute;

if (isAbsolute && resolvedDevice.length > 0) {

// 磁盘根存在便结束循环

break;

}

}

join 根据传入的 path 片段进行路径拼接

接收多个参数,利用特定分隔符作为定界符将所有的 path 参数连接在一起,生成新的规范化路径。

接收参数后进行校验,如果没有参数的话,会直接返回 '.' ,反之进行遍历,通过内置 validateString 方法校验每个参数,如有一项不合规则直接 throw new ERR_INVALID_ARG_TYPE(name, 'string', value);

window 下为反斜杠 ('\') , 而 linux 下为正斜杠 ('/'),这里是 join 方法区分操作系统的一个不同点,而反斜杠 ('\') 有转义符的作用,单独使用会被认为是要转义斜杠后面的字符串,故此使用双反斜杠转义出反斜杠 ('\') 使用。

最后进行拼接后的字符串校验并格式化返回。

if (args.length === 0)

return '.';

let joined;

let firstPart;

// 从左到右检测参数

for (let i = 0; i < args.length; ++i) {

const arg = args[i];

// internal/validators

validateString(arg, 'path');

if (arg.length > 0) {

if (joined === undefined)

// 把第一个字符串赋值给 joined,并用 firstPart 变量保存第一个字符串以待后面使用

joined = firstPart = arg;

else

// joined 有值,进行 += 拼接操作

joined += `\\${arg}`;

}

}

if (joined === undefined)

return '.';

在 window 系统下,因为使用反斜杠 ('\') 和 UNC (主要指局域网上资源的完整 Windows 2000 名称)路径的缘故,需要进行网络路径处理,('\') 代表的是网络路径格式,因此在 win32 下挂载的join 方法默认会进行截取操作。

如果匹配得到反斜杠 ('\'),slashCount 就会进行自增操作,只要匹配反斜杠 ('\') 大于两个就会对拼接好的路径进行截取操作,并手动拼接转义后的反斜杠 ('\')。

let needsReplace = true;

let slashCount = 0;

// 根据 StringPrototypeCharCodeAt 对首个字符串依次进行 code 码提取,并通过 isPathSeparator 方法与定义好的 code 码进行匹配

if (isPathSeparator(StringPrototypeCharCodeAt(firstPart, 0))) {

++slashCount;

const firstLen = firstPart.length;

if (firstLen > 1 &&

isPathSeparator(StringPrototypeCharCodeAt(firstPart, 1))) {

++slashCount;

if (firstLen > 2) {

if (isPathSeparator(StringPrototypeCharCodeAt(firstPart, 2)))

++slashCount;

else {

needsReplace = false;

}

}

}

}

if (needsReplace) {

while (slashCount < joined.length &&

isPathSeparator(StringPrototypeCharCodeAt(joined, slashCount))) {

slashCount++;

}

if (slashCount >= 2)

joined = `\\${StringPrototypeSlice(joined, slashCount)}`;

}

阅读了源码之后,resolve 方法会对参数进行处理,考虑路径的形式,在最后抛出绝对路径。在使用的时候,如果是进行文件之类的操作,推荐使用 resolve 方法,相比来看, resolve 方法就算没有参数也会返回一个路径,供使用者操作,在执行过程中会进行路径的处理。而 join 方法只是对传入的参数进行规范化拼接,对于生成一个新的路径比较实用,可以按照使用者意愿创建。不过每个方法都有优点,要根据自己的使用场景以及项目需求,去选择合适的方法。

(编辑:银川站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章