简介
fast-glob 是一个高性能的 Node.js 文件系统 glob 库,用于快速匹配文件路径模式。它提供了比原生 Node.js glob 更快的性能,支持异步和同步操作,是现代前端工具链中广泛使用的文件匹配库。
特点
- 高性能: 比其他 glob 库快 2-5 倍
- 灵活的 API: 支持同步、异步、流式操作
- 丰富的选项: 提供大量配置选项满足不同需求
- TypeScript 支持: 完整的类型定义
- 零依赖: 不依赖其他第三方库
安装
npm install fast-glob
或者
yarn add fast-glob
基础用法
异步操作(推荐)
import fg from "fast-glob";
// 基础匹配
const files = await fg(["src/**/*.js", "lib/**/*.js"]);
console.log(files);
// ['src/index.js', 'src/utils/helper.js', 'lib/main.js']
// 排除文件
const files = await fg(["src/**/*.js", "!src/**/*.test.js"]);
console.log(files);
// ['src/index.js', 'src/utils/helper.js'] (排除测试文件)
同步操作
import fg from "fast-glob";
const files = fg.sync(["src/**/*.{js,ts}"]);
console.log(files);
// ['src/index.js', 'src/types.ts', 'src/utils/helper.js']
流式操作
import fg from "fast-glob";
const stream = fg.stream(["src/**/*.js"]);
stream.on("data", (entry) => {
console.log(entry); // 每个匹配的文件路径
});
stream.on("end", () => {
console.log("完成");
});
模式语法
基础通配符
// * 匹配任意字符(除了路径分隔符)
await fg("src/*.js"); // src/index.js, src/main.js
// ** 匹配任意目录层级
await fg("src/**/*.js"); // src/utils/helper.js, src/components/Button.js
// ? 匹配单个字符
await fg("src/?.js"); // src/a.js, src/1.js
// [] 字符集匹配
await fg("src/[abc].js"); // src/a.js, src/b.js, src/c.js
await fg("src/[a-z].js"); // src/a.js 到 src/z.js
大括号展开
// 多个扩展名
await fg("src/**/*.{js,ts,vue}");
// 等同于: ['src/**/*.js', 'src/**/*.ts', 'src/**/*.vue']
// 多个目录
await fg("{src,lib}/**/*.js");
// 等同于: ['src/**/*.js', 'lib/**/*.js']
// 复杂组合
await fg("src/**/*.{test,spec}.{js,ts}");
// 匹配: src/utils/helper.test.js, src/components/Button.spec.ts
否定模式
// 排除特定文件
await fg(["src/**/*.js", "!src/**/*.test.js"]);
// 排除目录
await fg(["src/**/*", "!src/node_modules/**"]);
// 多个排除条件
await fg([
"src/**/*.{js,ts}",
"!src/**/*.test.{js,ts}",
"!src/**/*.spec.{js,ts}",
"!src/temp/**",
]);
配置选项
基础选项
const options = {
// 基础目录
cwd: process.cwd(),
// 返回绝对路径
absolute: false,
// 匹配点文件(隐藏文件)
dot: false,
// 区分大小写
caseSensitiveMatch: true,
// 跟随符号链接
followSymbolicLinks: true,
// 忽略错误
suppressErrors: false,
// 抛出错误而不是忽略
throwErrorOnBrokenSymbolicLink: true,
};
const files = await fg(["src/**/*.js"], options);
高级选项
const advancedOptions = {
// 深度限制
deep: 5,
// 只匹配文件
onlyFiles: true,
// 只匹配目录
onlyDirectories: false,
// 标记为目录的模式
markDirectories: false,
// 返回相对路径
objectMode: false,
// 统计信息
stats: false,
// 唯一结果
unique: true,
// 并发限制
concurrency: Infinity,
// 忽略父目录
ignore: ["node_modules/**", ".git/**"],
};
实际应用场景
1. 构建工具中的文件收集
import fg from "fast-glob";
import path from "path";
// 收集所有源文件
async function collectSourceFiles() {
const patterns = [
"src/**/*.{js,ts,jsx,tsx}",
"lib/**/*.{js,ts}",
"!**/*.test.{js,ts}",
"!**/*.spec.{js,ts}",
"!**/node_modules/**",
];
const files = await fg(patterns, {
cwd: process.cwd(),
absolute: true,
onlyFiles: true,
});
return files.map((file) => ({
path: file,
name: path.basename(file),
ext: path.extname(file),
dir: path.dirname(file),
}));
}
// 使用示例
const sourceFiles = await collectSourceFiles();
console.log(`找到 ${sourceFiles.length} 个源文件`);
2. 静态资源处理
// 处理图片资源
async function processImages() {
const imagePatterns = [
"src/assets/**/*.{png,jpg,jpeg,gif,svg,webp}",
"public/images/**/*.{png,jpg,jpeg,gif,svg,webp}",
];
const images = await fg(imagePatterns, {
onlyFiles: true,
stats: true, // 获取文件统计信息
});
return images.map((entry) => ({
path: entry.path,
size: entry.stats.size,
modified: entry.stats.mtime,
}));
}
3. 代码分析工具
// 分析项目结构
async function analyzeProject() {
const analysis = {
components: await fg("src/components/**/*.{vue,jsx,tsx}"),
utils: await fg("src/utils/**/*.{js,ts}"),
styles: await fg("src/**/*.{css,scss,less,stylus}"),
tests: await fg("**/*.{test,spec}.{js,ts,jsx,tsx}"),
configs: await fg("*.config.{js,ts,json}"),
};
// 统计信息
const stats = Object.entries(analysis).map(([type, files]) => ({
type,
count: files.length,
files,
}));
return stats;
}
4. 文件监听和热更新
import fg from "fast-glob";
import chokidar from "chokidar";
// 设置文件监听
async function setupFileWatcher() {
// 获取需要监听的文件
const watchPatterns = [
"src/**/*.{js,ts,vue,jsx,tsx}",
"src/**/*.{css,scss,less}",
];
const filesToWatch = await fg(watchPatterns);
// 创建监听器
const watcher = chokidar.watch(filesToWatch, {
ignored: /node_modules/,
persistent: true,
});
watcher
.on("change", (path) => console.log(`文件变更: ${path}`))
.on("add", (path) => console.log(`文件添加: ${path}`))
.on("unlink", (path) => console.log(`文件删除: ${path}`));
return watcher;
}
性能优化技巧
1. 使用具体的模式
// ❌ 性能较差
await fg("**/*");
// ✅ 性能更好
await fg("src/**/*.{js,ts,vue}");
2. 合理使用排除模式
// ✅ 推荐:在模式中排除
await fg(["src/**/*.js", "!src/node_modules/**"]);
// ❌ 不推荐:在选项中排除
await fg("src/**/*.js", {
ignore: ["src/node_modules/**"],
});
3. 限制搜索深度
// 限制搜索深度提高性能
await fg("src/**/*.js", {
deep: 3, // 最多搜索3层目录
});
4. 使用并发控制
// 控制并发数量避免资源耗尽
await fg("**/*.js", {
concurrency: 10, // 限制并发为10
});
错误处理
import fg from "fast-glob";
try {
const files = await fg(["src/**/*.js"], {
throwErrorOnBrokenSymbolicLink: true,
suppressErrors: false,
});
console.log(`找到 ${files.length} 个文件`);
} catch (error) {
if (error.code === "ENOENT") {
console.error("目录不存在");
} else if (error.code === "EACCES") {
console.error("权限不足");
} else {
console.error("未知错误:", error.message);
}
}
与其他工具集成
Vite 插件中的使用
// vite.config.js
import { defineConfig } from "vite";
import fg from "fast-glob";
export default defineConfig({
plugins: [
{
name: "custom-file-processor",
buildStart: async () => {
// 处理特定文件
const files = await fg("src/assets/icons/*.svg");
console.log(`处理 ${files.length} 个 SVG 图标`);
},
},
],
});
Webpack 配置中的使用
// webpack.config.js
const fg = require("fast-glob");
const path = require("path");
module.exports = async () => {
// 动态生成入口点
const entries = await fg("src/pages/*/index.js");
const entryPoints = {};
entries.forEach((entry) => {
const name = path.basename(path.dirname(entry));
entryPoints[name] = entry;
});
return {
entry: entryPoints,
// ... 其他配置
};
};
总结
fast-glob 是一个功能强大且高性能的文件匹配库,特别适合在构建工具、静态分析、文件处理等场景中使用。通过合理使用其丰富的配置选项和模式语法,可以高效地处理各种文件匹配需求。
最佳实践
- 优先使用异步 API 以获得更好的性能
- 使用具体的模式 而不是过于宽泛的通配符
- 合理使用排除模式 提高匹配效率
- 设置适当的深度限制 避免不必要的深度搜索
- 处理错误情况 确保程序的健壮性
通过掌握这些用法和技巧,您可以在项目中充分发挥 fast-glob 的优势,提高文件处理的效率和性能。
原文链接:https://code.ifrontend.net/archives/882,转载请注明出处。
评论0