只需几行代码,手写实现【文件监听和自动重启】

前端阳光

共 4953字,需浏览 10分钟

 ·

2021-09-15 22:55

前言

写这篇文章的原因是因为 好奇于平时 在vscode或者其他编译器写代码的时候,会在我修改代码后重新打包,当然我知道是webpack的功能,那它是怎么知道我修改了代码的,我觉得肯定有个东西在监听,于是就研究了亿下下

手写实现

稍微写过一点 node 应用的同学应该都有类似的经历,编写一段代码,运行一个 http server,保存为 js 文件,例如 app.js ,

然后执行命令

node app.js

此时 node 就会执行我们刚才书写的代码。此时一切看起来都很美好,但是随着开发继续,一些问题也会逐渐显现出来。

每过一段时间,我们都想看下刚才书写都代码是否能够正常运行,那么每次保存代码以后,我们都不得不 ctrl + C 结束掉刚才的 server,然后重新运行这段脚本。于是乎一些能够监听文件变化并支持自动重启的工具就出现在了我们的视线中。比如 hotnode,supervisor 等等。这些工具能够很好的节约开发者在重复操作上花费的时间,能够把经历更加专注在其他方面。

下面说一下如何实现一个极简的工具。

文件监听

fs.watch()

fs,File System 的缩写,利用这个 API,我们可以对文件或者目录进行监听。当监听到文件变化后,执行预设的回调函数。

fs.watch("app.js", (event, filename) => {
  // 文件变化时触发的回调函、
});

细心的小伙伴可能已经想到了,文件发生变化,我再去执行一个什么函数,把app.js再重新执行一下,是不是就可以了?

你说的没错。

自动重启

API: child_process.spawn

我们使用 spawn 开启一个新的进程,然后在在这个进程中执行 js 脚本即可,话不多说,上栗子:

const { spawn } = require("child_process");
let process = null;

function startProcess({
  // 执行js脚本
  process = spawn("node", ["app.js"]);

  // 打印输出到控制台
  process.stdout.on("data", data => {
    console.log(data.toString());
  });
}

// 有了start,还要有个restart
function restartProcess({
  if (process !== null) {
    try {
      // 清除进程,
      process.kill("SIGKILL");
    } catch (error) {
      console.log("Exception: " + error.message, "bad");
    }
  }

  // 然后这里再次启动,可以说很像清除定时器的操作了~
  startProcess();
}

最后,我们把restartProcess放到fs.watch的回调函数里,再让startProcess和fs.watch顺序执行就好了。像这样:

fs.watch("app.js", (event, filename) => {
  restartProcess();
});

startProcess();

如果你想写一个放心能用的工具,还是有很多东西要解决的,比如即使没有修改文件内容,单纯的按下保存,也会触发 watch 函数的回调。这需要采取一定的策略去判断保存前后文件内容是否有变动。

有兴趣的同学也可以去看下另一个库的代码,watch,链接:https://link.juejin.cn/?target=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fwatch

相关优化与思考

这样修改并保存的时候回发现一样有点问题。直接保存,显示两次更新

修改文件以后,一样显示两次更新(mac系统上是两次,其余系统可能有所差异)  这样多是于操做系统对文件修改的事件支持有关,在保存的时候出发了不止一次。

下面聚焦于回调事件的参数,event对应事件类型,是否能够判断事件类型为change呢,才执行呢,忽略空保存。

fs.watch("app.js", (event, filename) => {
  if (filename && event == 'change') {
     restartProcess();
  }
  
});

startProcess();

不过实际上,空的保存event也是change,另外不一样平台event的实现可能也有所不一样。这种方式要pass掉。

显然从上面的例子能够看到,变动时间依然不可控。由于每次保存,node对应stat对象依然会修改。

对比文件内容

只能选择这种方式来判断是不是否更新。例如md5:

const fs = require('fs'),
    md5 = require('md5');
const filePath = './'    
let preveMd5 = null

fs.watch("app.js", (event, filename) => {
    var currentMd5 = md5(fs.readFileSync(filePath + filename))
    if (currentMd5 == preveMd5) {
        return
    }
    preveMd5 = currentMd5
    restartProcess();
});

startProcess();

没错,也可以说是判断文件的hash值。

先保存当前文件md5值,每次文件变化时(即保存操做响应以后),每次都获取文件的md5而后进行对比,看是否发生变化。

不过这样能够看到,当初次保存时,都会执行一次,由于初始值为null的缘故。这样能够加个兼容,根据是否第一次保存来判断好了。

对于不一样的操做系统,可能保存时触发的回调不止一个(mac上到没出现)。为了不这种实时响应对应的频繁触发,能够引入debounce函数来保证性能。

const fs = require('fs'),
    md5 = require('md5');
let preveMd5 = null,
    fsWait = false
const filePath = './'    
fs.watch(filePath,(event,filename)=>{
    if (filename){
        if (fsWait) return;
        fsWait = setTimeout(() => {
            fsWait = false;
        }, 100)
        var currentMd5 = md5(fs.readFileSync(filePath + filename))
        if (currentMd5 == preveMd5){
            return 
        }
        preveMd5 = currentMd5
        restartProcess();
    }
})

startProcess();

结束

谢谢大家观看。是不是忽然明白了文件是怎么被监听的了呢

 欢迎关注《前端阳光》,加入技术交流群,加入内推群


相关文献

https://juejin.cn/post/6844903893785116680


https://www.shangmayuan.com/a/07ce3c507d7a4c478e37f1f0.html




浏览 47
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报