Electron开发应用中想要监听某个文件,在文件被修改后能自动检测到并立即更新内容。要实现这一功能,需要用到 chokidar 文件监视库。实现步骤:

1. 安装依赖

npm install chokidar --save

2. 在electron主进程(main.js)中添加文件监视功能

electron/main.js

const fs = require('fs-extra');
const chokidar = require('chokidar');

let watchedFiles = new Map();  //存储正在被监视的文件

/ 添加文件监视IPC处理函数-----
ipcMain.handle('watch-file', async(event, filePath) => {
  // 如果文件已经在监视中,直接返回成功
  if (watchedFiles.has(filePath)) {
    return { success: true, message: '文件已在监视中' };
  }

  try {
    // 创建文件监视器
    const watcher = chokidar.watch(filePath, {
      persistent: true,
      awaitWriteFinish: {
        stabilityThreshold: 1000, // 等待文件写入完成的稳定时间(毫秒)
        pollInterval: 1000       // 检查文件变化的间隔(毫秒)
      }
    });

    // 监听文件变化事件
    watcher.on('change', (path) => {
      try {
        // 读取文件的新内容
        const content = fs.readFileSync(path, 'utf-8');
        // 发送文件变化通知到渲染进程
        event.sender.send('file-changed', { path, content });
      } catch (readErr) {
        console.error('读取变更文件失败:', readErr);
      }
    });
    // 监听文件新增事件
    watcher.on('add', (path) => {
      try {
        const content = fs.readFileSync(path, 'utf-8');
        event.sender.send('file-changed', { path, content });
      } catch (readErr) {
        console.error('读取新增文件失败:', readErr);
      }
    });
    // 监听文件删除事件
    watcher.on('unlink', (path) => {
      try {
        event.sender.send('file-changed', { path, content });
      } catch (readErr) {
        console.error('读取删除文件失败:', readErr);
      }
    });

    // 存储监视器引用,以便之后可以停止监视
    watchedFiles.set(filePath, watcher);
    
    return { success: true };
  } catch (error) {
    return { success: false, error: error.message };
  }
});

// 停止监视文件的IPC处理函数
ipcMain.handle('unwatch-file', async (event, filePath) => {
  const watcher = watchedFiles.get(filePath);
  if (watcher) {
    await watcher.close();
    watchedFiles.delete(filePath);
    return { success: true };
  }
  return { success: false, message: '文件未被监视' };
});

3. 在预加载脚本中暴露API

electron/preload.js中,添加文件监视相关的API:

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  // 开始监视文件
  watchFile: (filePath) => ipcRenderer.invoke('watch-file', filePath),
  // 停止监视文件
  unwatchFile: (filePath) => ipcRenderer.invoke('unwatch-file', filePath),
  // 注册文件变化的事件监听
  onFileChanged: (callback) => {
    const subscription = (_, data) => callback(data);
    ipcRenderer.on('file-changed', subscription);
    // 返回取消订阅的函数
    return () => {
      ipcRenderer.removeListener('file-changed', subscription);
    };
  }
})

4. 添加文件监视方法

我拆了一个js文件,把文件监视相关的都写在了 /electron/file-watcher.js 文件里

const watchingFiles = new Set(); // 当前正在监视的文件集合

// 开始监视文件的函数--暴露
export const startWatchingFile = async (filePath, ascFileName, projectId) => {
  // 确保Electron API可用
  if (!window.electronAPI || watchingFiles.size > 0) return;
  try {
    // 调用Electron API开始监视文件
    const result = await window.electronAPI.watchFile(filePath);
    if (result.success) {
      // 记录已监视的文件
      watchingFiles.add(filePath); 
      console.log(`开始监视文件: `, watchingFiles);

      window.electronAPI.onFileChanged(async(data) => {
        // 监测到文件修改,处理逻辑
        //...
      })
    }
  } catch (error) {
    console.error('启动文件监视失败:', error);
  }
};

// 停止所有文件监视--暴露
export const stopWatchingFileListeners = () => {
  for (const filePath of watchingFiles) {
    stopWatchingFile(filePath);
  }
}

// 停止监视文件的函数
const stopWatchingFile = async (filePath) => {
  if (!window.electronAPI) return;
  try {
    // 调用Electron API停止监视文件
    await window.electronAPI.unwatchFile(filePath);
    // 从集合中移除
    watchingFiles.delete(filePath);
    console.log(`停止监视文件: ${filePath}`); 
  } catch (error) {
    console.error('停止文件监视失败:', error);
  }
}

5. 在Vue组件中调用监听方法

// 引入必要的Vue API
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { startWatchingFile, stopWatchingFileListeners } from '../../electron/file-watcher.js'


// 在组件挂载时初始化监视
onMounted(async()=>{
  startWatchingFile();
})

// 在组件卸载前清理监视
onUnmounted(() => {
  stopWatchingFileListeners()
})

说明:

我们使用了chokidar库而不是Node.js内置的fs.watch,因为它提供了更稳定、跨平台的文件监视功能,并且处理了一些常见的问题,如:

  • 避免重复触发事件
  • 提供 awaitWriteFinish 选项,确保文件写入完成后再触发事件
  • 处理不同操作系统间的差异

注意:chokidar报错问题解决

在 Electron 项目中引入 chokidar 时,打包后的项目运行时会报错,说找不到chokidar模块。

因为chokidar在打包时需要特殊处理,以下是完整的解决方案

方案一:electron-builder 配置(推荐)

在 package.json 中配置

{
  "name": "your-electron-app",
  "version": "1.0.0",
  "main": "electron/main.js",
  "scripts": {
    "electron:build": "build && electron-builder",
    "electron:dev": "electron ./electron/main.js"
  },
  "devDependencies": {
    "electron": "^28.0.0",
    "electron-builder": "^26.0.12",
  },
  "dependencies": {
    "chokidar": "^4.0.3",
  },
  "build": {
    "appId": "com.yourcompany.yourapp",
    "productName": "YourApp",
    "directories": {
      "output": "dist-electron"
    },
    "files": [
      "./dist",
      "./dist_electron",
      "./electron",
      "node_modules/**/*"
    ],
    "asar": true,
    //!!重点(打包排除 chokidar:)
    "asarUnpack": [
      "**/node_modules/chokidar/**/*",
      "**/node_modules/braces/**/*",
      "**/node_modules/readdirp/**/*",
      "**/node_modules/fsevents/**/*"
    ]
  }
}

方案二:主进程中安全引入 chokidar

//  electron/main.js
const { app, BrowserWindow } = require('electron');
const path = require('path');

let chokidar;
let watcher;

function setupFileWatcher() {
  try {
    // 开发环境直接引入
    if (process.env.NODE_ENV === 'development') {
      chokidar = require('chokidar');
    } else {
      // 生产环境使用解压后的模块
      const chokidarPath = path.join(process.resourcesPath, 'app.asar.unpacked', 'node_modules', 'chokidar');
      chokidar = require(chokidarPath);
    }

    // 初始化文件监视
    watcher = chokidar.watch('/path/to/watch', {
      ignored: /(^|[\/\\])\../, // 忽略隐藏文件
      persistent: true,
      ignoreInitial: true
    });

    watcher
      .on('add', path => console.log(`File ${path} has been added`))
      .on('change', path => console.log(`File ${path} has been changed`))
      .on('unlink', path => console.log(`File ${path} has been removed`));

  } catch (error) {
    console.error('Failed to load chokidar:', error);
  }
}

app.whenReady().then(() => {
  createWindow();
  setupFileWatcher();
});

// 应用退出时关闭监视器
app.on('before-quit', () => {
  if (watcher) {
    watcher.close();
  }
});

项目结构示例

your-electron-app/
├── package.json
├── electron/
│   ├── main.js        # 主进程入口
│   ├── preload.js        # 渲染进程入口
├── dist/                     # 构建输出
└── dist-electron/           # electron-builder 输出

我采用了第一种方式,更简单,打包后成功运行

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐