文件监控 #
一、文件监控概述 #
Deno提供了 Deno.watchFs API用于监控文件系统的变化,常用于开发工具、热重载等场景。
二、基本用法 #
2.1 监控目录 #
typescript
const watcher = Deno.watchFs("./src");
for await (const event of watcher) {
console.log("Event:", event.kind);
console.log("Paths:", event.paths);
}
2.2 监控多个路径 #
typescript
const watcher = Deno.watchFs(["./src", "./tests"]);
for await (const event of watcher) {
console.log(event);
}
2.3 监控选项 #
typescript
const watcher = Deno.watchFs("./src", {
recursive: true // 递归监控子目录
});
for await (const event of watcher) {
console.log(event);
}
三、事件类型 #
3.1 事件种类 #
typescript
type FsEventKind =
| "any" // 任何变化
| "access" // 文件访问
| "create" // 文件创建
| "modify" // 文件修改
| "remove" // 文件删除
| "other"; // 其他事件
const watcher = Deno.watchFs("./src");
for await (const event of watcher) {
switch (event.kind) {
case "create":
console.log("文件创建:", event.paths);
break;
case "modify":
console.log("文件修改:", event.paths);
break;
case "remove":
console.log("文件删除:", event.paths);
break;
}
}
3.2 事件结构 #
typescript
interface FsEvent {
kind: FsEventKind;
paths: string[];
flag?: FsEventFlag;
}
四、实际应用 #
4.1 热重载服务器 #
typescript
async function startServer() {
let process: Deno.ChildProcess | null = null;
async function restart() {
if (process) {
process.kill();
await process.status;
}
process = new Deno.Command("deno", {
args: ["run", "--allow-net", "./server.ts"]
}).spawn();
console.log("Server started");
}
await restart();
const watcher = Deno.watchFs("./src", { recursive: true });
for await (const event of watcher) {
if (event.kind === "modify" && event.paths.some(p => p.endsWith(".ts"))) {
console.log("File changed, restarting...");
await restart();
}
}
}
4.2 日志文件监控 #
typescript
async function watchLogFile(path: string) {
let lastSize = 0;
try {
const info = await Deno.stat(path);
lastSize = info.size;
} catch {
// 文件不存在
}
const watcher = Deno.watchFs(path);
for await (const event of watcher) {
if (event.kind === "modify") {
try {
const info = await Deno.stat(path);
const newSize = info.size;
if (newSize > lastSize) {
const file = await Deno.open(path);
await file.seek(lastSize, Deno.SeekMode.Start);
const buffer = new Uint8Array(newSize - lastSize);
await file.read(buffer);
console.log(new TextDecoder().decode(buffer));
lastSize = newSize;
file.close();
}
} catch (error) {
console.error("Error:", error);
}
}
}
}
4.3 配置文件监控 #
typescript
interface Config {
host: string;
port: number;
}
async function watchConfig(path: string, onChange: (config: Config) => void) {
let config: Config = { host: "localhost", port: 3000 };
async function loadConfig() {
try {
const content = await Deno.readTextFile(path);
config = JSON.parse(content);
onChange(config);
} catch (error) {
console.error("Failed to load config:", error);
}
}
await loadConfig();
const watcher = Deno.watchFs(path);
for await (const event of watcher) {
if (event.kind === "modify") {
// 防抖处理
await new Promise(resolve => setTimeout(resolve, 100));
await loadConfig();
}
}
}
await watchConfig("./config.json", (config) => {
console.log("Config updated:", config);
});
4.4 文件同步工具 #
typescript
async function syncDirectories(src: string, dest: string) {
// 初始同步
await copyDir(src, dest);
const watcher = Deno.watchFs(src, { recursive: true });
for await (const event of watcher) {
for (const path of event.paths) {
const relativePath = path.replace(src, "");
const destPath = `${dest}${relativePath}`;
switch (event.kind) {
case "create":
case "modify":
try {
const info = await Deno.stat(path);
if (info.isFile) {
await Deno.copyFile(path, destPath);
console.log(`Copied: ${relativePath}`);
}
} catch {
// 忽略错误
}
break;
case "remove":
try {
await Deno.remove(destPath);
console.log(`Removed: ${relativePath}`);
} catch {
// 忽略错误
}
break;
}
}
}
}
async function copyDir(src: string, dest: string) {
await Deno.mkdir(dest, { recursive: true });
for await (const entry of Deno.readDir(src)) {
const srcPath = `${src}/${entry.name}`;
const destPath = `${dest}/${entry.name}`;
if (entry.isDirectory) {
await copyDir(srcPath, destPath);
} else {
await Deno.copyFile(srcPath, destPath);
}
}
}
五、注意事项 #
5.1 事件去重 #
typescript
function debounce<T extends unknown[]>(
fn: (...args: T) => void,
delay: number
): (...args: T) => void {
let timeoutId: number | undefined;
return (...args: T) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
const handleChange = debounce((paths: string[]) => {
console.log("Changed:", paths);
}, 100);
const watcher = Deno.watchFs("./src", { recursive: true });
for await (const event of watcher) {
handleChange(event.paths);
}
5.2 关闭监控 #
typescript
const watcher = Deno.watchFs("./src");
// 在某个条件下关闭
setTimeout(() => {
watcher.close();
console.log("Watcher closed");
}, 10000);
for await (const event of watcher) {
console.log(event);
}
5.3 错误处理 #
typescript
try {
const watcher = Deno.watchFs("./nonexistent");
for await (const event of watcher) {
console.log(event);
}
} catch (error) {
console.error("Watch error:", error);
}
六、总结 #
本章学习了:
- 文件监控的基本用法
- 事件类型
- 实际应用场景
- 注意事项
下一章,我们将学习流处理。
最后更新:2026-03-28