極簡 Node.js 入門 - 3.5 監視檔案變化
阿新 • • 發佈:2020-09-01
> 極簡 Node.js 入門系列教程:[https://www.yuque.com/sunluyong/node](https://www.yuque.com/sunluyong/node)
>
> 本文更佳閱讀體驗:[https://www.yuque.com/sunluyong/node/fs-watch](https://www.yuque.com/sunluyong/node/fs-watch)
## fs.FSWatcher
fs.FSWatcher類 繼承了 EventEmitter,用於監視檔案變化,呼叫 fs.watch 後返回一個 fs.FSWatcher 例項,每當指定監視的檔案被修改時,例項會觸發事件呼叫回撥函式
```javascript
fs.watch('./tmp', (eventType, filename) => {
if (filename) {
console.log(filename);
}
});
```
## fs.watch()
`fs.watch(filename[, options][, listener])` 監視檔案變化,返回 fs.FSWatcher 例項
1. filename:檔案或資料夾路徑
1. options
- encoding
- recursive:預設值 false,應該監視所有子目錄,還是僅監視當前目錄,僅在 macOS 和 Windows 上支援
- persistent:預設值 true,指示如果檔案已正被監視,程序是否應繼續執行
- listener(eventType, filename):檔案變化回撥函式
eventType 主要是 `rename` 和 `change` ,在大多數平臺上,檔案在目錄中出現或消失時觸發 'rename' 事件,在 Windows 上,如果監視的目錄被移動或重新命名,則不會觸發任何事件,當監視的目錄被刪除時,則報告 `EPERM` 錯誤
```javascript
fs.watch('./', { recursive: true }, (eventType, filename) => {
console.log(eventType, filename);
});
```
## fs.watchFile()
`fs.watchFile(filename[, options], listener)` 用於監視檔案變化
1. filename
1. options
- biginit:預設值 false,指定回撥 stat 中的數值是否為 biginit 型別
- persistent:預設值 true,當檔案正在被監視時,程序是否應該繼續執行
- interval:預設值 5007,用來指定輪詢頻率(ms)
3. listener(currentStats, previousStats):listener 有兩個引數,當前的 stat 物件和之前的 stat 物件
要在修改檔案時收到通知,則需要比較 `curr.mtime` 和 `prev.mtime`
```javascript
const fs = require('fs');
fs.watchFile('./test.txt', { interval: 100 }, (curr, prev) => {
console.log('當前的最近修改時間是: ' + curr.mtime);
console.log('之前的最近修改時間是: ' + prev.mtime);
});
const tid = setInterval(() => {
fs.appendFile('./test.txt', 'Hello, world!\n', err => {
if (err) throw err;
console.log('檔案修改完成');
});
}, 300);
setTimeout(() => {
clearInterval(tid);
fs.unwatchFile('./test.txt');
}, 2000);
```
## fs.watch() 與 fs.watchFile()
因為 fs.watchFile() 使用輪訓方式檢測檔案變化,如果不設定 `interval` 或者設定較高的值會發現檔案變化的監視有延遲
而 fs.watch() 監聽作業系統提供的事件,而且可以監視目錄變化,使用 fs.watch() 比 fs.watchFile() 更高效,平常應儘可能使用 fs.watch() 代替 fs.watchFile()
當然 fs.watch() 依賴作業系統的實現,在不同平臺上表現會有差異 1. Linux 作業系統使用 inotify 1. 在 macOS 系統使用 FSEvents 1. 在 windows 系統使用 ReadDirectoryChangesW ## fs.unwatchFile `fs.unwatchFile(filename[, listener])` 停止監視 filename 的變化,如果指定了 listener,則僅移除此特定監聽器,否則將移除所有監聽器,從而停止監視 filename ```javascript fs.unwatchFile('./test.txt'); ``` ## 社群選擇 fs.watchFile() 效能問題,fs.watch() 平臺不一致等兩個方法都有不盡如人意的地方 > Node.js `fs.watch`:
> - MacOS 有時候不提供 `filename`
> - 在部分場景不觸發修改事件(MacOS Sublime)
> - 經常一次修改兩次觸發事件
> - 大部分檔案變化 eventType 都是 `rename`.
> - 未提供簡單的監視檔案樹方式
>
Node.js `fs.watchFile`:
> - 事件處理問題和 fs.watch 一樣爛
> - 沒有巢狀監聽
> - CPU 消耗大
>
[https://www.npmjs.com/package/chokidar](https://www.npmjs.com/package/chokidar)
日常在監視檔案變化可以選擇社群的優秀方案
1. [node-watch](https://www.npmjs.com/package/node-watch)
1. [chokidar](https://www.npmjs.com/package/chokidar)
```javascript
const chokidar = require('chokidar');
// One-liner for current directory
chokidar.watch('.').on('all', (event, path) => {
console.log(event, path);
});
```
```javascript
// Initialize watcher.
const watcher = chokidar.watch('file, dir, glob, or array', {
ignored: /(^|[\/\\])\../, // ignore dotfiles
persistent: true
});
// Something to use when events are received.
const log = console.log.bind(console);
// Add event listeners.
watcher
.on('add', path => log(`File ${path} has been added`))
.on('change', path => log(`File ${path} has been changed`))
.on('unlink', path => log(`File ${path} has been removed`));
// More possible events.
watcher
.on('addDir', path => log(`Directory ${path} has been added`))
.on('unlinkDir', path => log(`Directory ${path} has been removed`))
.on('error', error => log(`Watcher error: ${error}`))
.on('ready', () => log('Initial scan complete. Ready for changes'))
.on('raw', (event, path, details) => { // internal
log('Raw event info:', event, path, details);
當然 fs.watch() 依賴作業系統的實現,在不同平臺上表現會有差異 1. Linux 作業系統使用 inotify 1. 在 macOS 系統使用 FSEvents 1. 在 windows 系統使用 ReadDirectoryChangesW ## fs.unwatchFile `fs.unwatchFile(filename[, listener])` 停止監視 filename 的變化,如果指定了 listener,則僅移除此特定監聽器,否則將移除所有監聽器,從而停止監視 filename ```javascript fs.unwatchFile('./test.txt'); ``` ## 社群選擇 fs.watchFile() 效能問題,fs.watch() 平臺不一致等兩個方法都有不盡如人意的地方 >