前言
在前端开发中,我们经常需要进行文件路径的匹配和筛选,以实现特定功能的需求。然而,正则表达式虽然是文件路径匹配的常用工具,但其语法复杂、难以维护,同时还容易出现匹配错误的情况。因此,Node.js 生态圈中就出现了一系列轻量级的文件路径匹配工具,如 minimatch、micro-match 等。本文介绍其中之一的 picomatch,并详细演示其用法。
picomatch 简介
picomatch 是一个高速、精确且易用的路径匹配工具。它支持大部分 glob 语法(即通配符语法),并且消除了通配符之间的歧义,从而使得使用者能够更轻松地理解和编写匹配规则。同时,picomatch 使用快速的字符串分析算法,并且对于较大的匹配集合,它的性能表现也非常出色。
安装
picomatch 可以通过 npm 安装:
npm install picomatch --save-dev
使用
基本用法
使用 picomatch 进行路径匹配可以采用如下基本的语法:
const picomatch = require('picomatch');
// 使用 picomatch 进行单个路径匹配
picomatch('*.txt', 'hello.txt'); // -> true
picomatch('*.txt', 'world.md'); // -> false上述代码中,第一个参数是匹配规则,第二个参数是待匹配的路径。在这里,我们使用通配符 * 表示任意字符(包括空字符串),并结合 .txt 后缀限制,从而匹配以 .txt 为后缀的文件。
glob 语法
除了支持基本的通配符语法,picomatch 还支持 glob 语法的大部分特性,如:
?:匹配任意单个字符(不包括空字符串);*:匹配任意数量的字符,包括空字符串;**:匹配任意数量的字符和目录(含子目录);[]:匹配字符集中的任意一个字符;{}:匹配其中任意一个选择项。
下面我们逐个实例介绍这些用法。
匹配单个字符
// 使用 ? 匹配任意单个字符(不含空字符串)
picomatch('a?c', 'abc'); // -> true
picomatch('a?c', 'ab'); // -> false
picomatch('a?c', 'acc'); // -> true在上述代码中,我们使用 a?c 匹配一切形如 axc 的字符串(其中 ? 只能代表单个字符,不包括空字符串)。因此,对于 abc,第二个字符 b 和 c 不匹配,所以返回 false;对于 acc,第二个字符 c 和 c 匹配成功,所以返回 true。
匹配任意数量字符
// 使用 * 匹配任意数量的字符(包括空字符串)
picomatch('a*c', 'ac'); // -> true
picomatch('a*c', 'abc'); // -> true
picomatch('a*c', 'axxxxxc'); // -> true在上述代码中,我们使用 a*c 匹配一切形如 ac 或以 a 开头、以 c 结尾的字符串(其中 * 包括 0 个或多个任意字符,包括空字符串)。因此,对于 ac 和 abc,匹配条件均成立;对于 axxxxxc,匹配条件也成立,因为第二个 x 向前扩展为零个字符。
匹配目录
// 使用 ** 匹配任意数量的字符和目录(含子目录)
picomatch('src/**/index.js', 'src/views/index.js'); // -> true
picomatch('src/**/index.js', 'src/index.js'); // -> true
picomatch('src/**/index.js', 'src/components/main/index.js'); // -> true在上述代码中,我们使用 src/**/*.js 匹配一切以 .js 为后缀的文件,并且可能在 src 目录中出现的任意子目录下。因此,对于 src/views/index.js、src/index.js 和 src/components/main/index.js,匹配规则均成立。
在 picomatch 中,使用 ** 在匹配规则中表示一个或多个目录或子目录(用 / 分隔)。这样做可以实现相对灵活的匹配策略,同时也减小了模式匹配的复杂度。
匹配字符集
// 使用 [] 匹配字符集中的任意一个字符
picomatch('foo[a-z]bar', 'fooiubar'); // -> false
picomatch('foo[a-z]bar', 'foozbar'); // -> true
picomatch('foo[!a-z]bar', 'fooiubar'); // -> true在上述代码中,我们使用 [a-z] 的字符集,限制了匹配条件必须包含小写字母 a 到 z 中的一个。因此,第一个匹配条件不成立,第二个和第三个匹配条件则均成立。
除了基本的字符集,还可以使用补集,即在字符集前加一个 !,表示不匹配该字符集中的任何一个字符。
匹配选择项
// 使用 {} 匹配其中任意一个选择项
picomatch('foo/{alpha,beta,gamma}/bar', 'foo/beta/bar'); // -> true
picomatch('foo/{alpha,beta,gamma}/bar', 'foo/omega/bar'); // -> false在上述代码中,我们使用 {} 匹配三个中括号内的任意一个选择项。因此,第一个匹配条件成立,第二个匹配条件不成立。
值得注意的是,在使用选择项时,选择项不允许嵌套,如下述语法是错误的:
// 错误示例
picomatch('foo/{alpha,beta,{gamma,delta}}/bar', 'foo/delta/bar'); // -> Error高级用法
返回匹配的文件路径列表
除了单次匹配,picomatch 还支持返回匹配的文件路径列表,可以使用 picomatch.scan() 方法:
const picomatch = require('picomatch');
const matches = picomatch.scan('src/**/*.js', {
cwd: process.cwd(),
nodir: true,
});
console.log(matches);上述代码中,我们使用 src/**/*.js 匹配一切以 .js 为后缀的文件,并且可能在 src 目录中出现的任意子目录下。参数 nodir: true 表示只匹配文件,而不匹配目录。
通过 picomatch.scan() 实现的匹配结果是一个字符串数组,包含了所有匹配成功的文件路径。
结合 minimatch 支持功能更加强大的 glob 语法
picomatch 支持的 glob 语法非常丰富,但还有一些特殊场景需要使用更加强大的 glob 语法。在这种情况下,我们可以通过结合 minimatch 和 picomatch,实现复杂的路径匹配:
// www.javascriptcn.com code example
const picomatch = require('picomatch');
const minimatch = require('minimatch');
const match = (path, patterns) => {
const isMatch = patterns.some(pattern => minimatch(path, pattern));
if (isMatch) {
return picomatch(path, patterns);
}
return false;
};
console.log(match('src/index.js', ['!src/node_modules/**', 'src/**/*.js']));在上述代码中,我们使用被否定的 glob 模式 !src/node_modules/** 和匹配模式 src/**/*.js 来筛选出所有的以 .js 为后缀的非 node_modules 目录下的文件。通过结合 minimatch 和 picomatch,我们实现了一个更加强大的路径匹配功能。
总结
在本文中,我们介绍了一个高速、精确且易用的路径匹配工具 picomatch,并且演示了其详细的使用方法。通过本文的学习,你已经掌握了 picomatch 的基本用法和 glob 语法,可以在实际的前端开发过程中应用该工具,提高文件路径匹配的效率和准确性。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/5eedad26b5cbfe1ea0610bea