【问题标题】:How to auto-reload files in Node.js?如何在 Node.js 中自动重新加载文件?
【发布时间】:2021-05-14 13:33:05
【问题描述】:

关于如何在 Node.js 中实现文件自动重新加载的任何想法?我厌倦了每次更改文件时都重新启动服务器。 显然 Node.js 的 require() 函数不会重新加载已经需要的文件,所以我需要做这样的事情:

var sys     = require('sys'), 
    http    = require('http'),
    posix   = require('posix'),
    json    = require('./json');

var script_name = '/some/path/to/app.js';
this.app = require('./app').app;

process.watchFile(script_name, function(curr, prev){
    posix.cat(script_name).addCallback(function(content){
        process.compile( content, script_name );
    });
});

http.createServer(this.app).listen( 8080 );

app.js 文件中我有:

var file = require('./file');
this.app = function(req, res) { 
    file.serveFile( req, res, 'file.js');  
}

但这也不起作用 - 我在 process.compile() 语句中收到一个错误,指出未定义“要求”。 process.compile 正在评估 app.js,但对 node.js 全局变量一无所知。

【问题讨论】:

  • 你知道你可以在每个请求上运行这个代码:Object.keys(require.cache).forEach(function(key) { delete require.cache[key]; });
  • 为什么会有两个js文件,app.js作为服务器文件应该够了吧?

标签: javascript node.js


【解决方案1】:

最近(2009 年)thread 在 node.js 邮件列表中有关于此主题的消息。简短的回答是否定的,目前无法自动重新加载所需的文件,但有几个人开发了添加此功能的补丁。

【讨论】:

  • +1 是的。我参加了讨论。我承认我的解决方案太简单了。它仅在热模块本身不需要其他模块时才有效。 Felix 的解决方案经过深思熟虑,但 auto-reload 是否真的属于核心存在争议。
【解决方案2】:

有可以安装的Node-Supervisor

npm install supervisor

http://github.com/isaacs/node-supervisor

【讨论】:

  • 更多的是在服务器崩溃时重启服务器。当监视的文件发生变化时,node-supervisor 也会重新启动整个过程。它不是严格意义上的热重载。
  • 虽然不是真正的热加载,但如果您只想在开发时自动重新加载代码,那么这个工具非常有用,这样您就不必在每次更改后在命令行中重新启动节点。
【解决方案3】:

Here 是一篇关于 Node.js 热重载的博文。它提供了一个github Node branch,您可以使用它来替换您安装的 Node 以启用热重载。

来自博客:

var requestHandler = require('./myRequestHandler');

process.watchFile('./myRequestHandler', function () {
  module.unCacheModule('./myRequestHandler');
  requestHandler = require('./myRequestHandler');
}

var reqHandlerClosure = function (req, res) {
  requestHandler.handle(req, res);
}

http.createServer(reqHandlerClosure).listen(8000);

现在,每当您修改 myRequestHandler.js 时,上述代码都会注意到并用新代码替换本地 requestHandler。任何现有请求将继续使用旧代码,而任何新传入请求将使用新代码。所有这些都无需关闭服务器、退回任何请求、过早终止任何请求,甚至无需依赖智能负载平衡器。

【讨论】:

  • 这个解决方案唯一的问题是它是旧版本 Node 的一个分支,因此在使用之前必须对其进行调整并与最新版本合并(除非你不介意使用旧版本的节点)。
【解决方案4】:

解决方案: http://github.com/shimondoodkin/node-hot-reload

请注意,您必须自行处理所使用的引用。

这意味着如果你这样做了: var x=require('foo'); y=x;z=x.bar;并热重载 它。

这意味着您必须替换存储在 x、y 和 z 中的引用。在热重载回调函数中。

有些人将热重载与自动重启混淆了 我的 nodejs-autorestart 模块还具有新贵集成,可在启动时启用自动启动。 如果你有一个小应用程序自动重启很好,但是当你有一个大应用程序时热重载更合适。仅仅是因为热重载更快。

我也喜欢我的节点流入模块。

【讨论】:

  • 这个不错!
  • 这是旧的对不起
【解决方案5】:

node-supervisor 太棒了

用于在保存旧节点版本时重新启动(不建议):

npm install supervisor -g
supervisor app.js

使用 npx 附带的 Node 版本在保存时重新启动:

npm install supervisor
npx supervisor app.js

或者直接在 npm 脚本中调用supervisor

"scripts": {
  "start": "supervisor app.js"
}

【讨论】:

  • npm install -g 主管。它应该全局安装。
  • 在 OSx 10.2.8 上我必须使用 sudo 运行它
  • 不得不在 Windows 下这样运行:"C:\Program Files\nodejs\node.exe" C:\Users\Mark\AppData\Roaming\npm\node_modules\supervisor\lib\cli-wrapper.js app.js
  • 在应用程序根目录中没有 -g 或 sudo:npm install supervisornode node_modules/supervisor/lib/cli-wrapper.js app.js(我有一个非根节点安装)
  • @Mark 这意味着节点不在你的PATH
【解决方案6】:

我正在制作一个相当小的节点“东西”,它能够随意加载/卸载模块(也就是说,您可以重新启动应用程序的一部分而不会关闭整个应用程序)。 我正在合并一个(非常愚蠢的)依赖管理,所以如果你想停止一个模块,所有依赖它的模块也会停止。

到目前为止一切顺利,但后来我偶然发现了如何重新加载模块的问题。显然,可以从“require”缓存中删除模块并完成工作。由于我不热衷于直接更改节点源代码,所以我想出了一个 very hacky-hack,即:在堆栈跟踪中搜索对“require”函数的最后调用,获取引用它的“缓存”字段和..好吧,删除对节点的引用: 罢工>

    var args = arguments
    while(!args['1'] || !args['1'].cache) {
        args = args.callee.caller.arguments
    }
    var cache = args['1'].cache
    util.log('remove cache ' + moduleFullpathAndExt)
    delete( cache[ moduleFullpathAndExt ] )

实际上更容易:

var deleteCache = function(moduleFullpathAndExt) {
  delete( require.cache[ moduleFullpathAndExt ] )
}

显然,这很好用。我完全不知道那个 arguments["1"] 是什么意思,但它正在做它的工作。我相信节点人员有一天会实施重新加载工具,所以我想现在这个解决方案也是可以接受的。 (顺便说一句。我的“东西”会在这里:https://github.com/cheng81/wirez,几周后去那里,你应该明白我在说什么)

【讨论】:

  • ..当然没那么简单。这仅在调用堆栈中存在 对 require 的调用时才有效。哦,好吧,在 hack 之上轻松 hack:在临时脚本中编写这些东西,并在运行时需要它。做到了,它起作用了..它甚至从缓存中清除了自己
  • 其实更简单:delete(require.cache[moduleFullpathAndExt])
  • Node.js 模块实际上包装在一个匿名函数中,这就是模块封装的完成方式。每个模块实际上看起来像function (module, require) { /* your code */ }。考虑到这一点,arguments[1] 指向require。 while 循环适用于从模块中的另一个函数中调用它的情况(它只是向上函数层次结构并检查传递给每个函数的参数值)。
【解决方案7】:

我找到了一个简单的方法:

delete require.cache['/home/shimin/test2.js']

【讨论】:

  • 如果您想在不重新启动应用程序的情况下重新加载外部库(在我的例子中是一个 IRC 机器人),这非常棒。
  • 这太棒了!如此简单,效果很好。每当收到请求时,我都会取消缓存一堆没有状态的文件。
  • delete require.cache[require.resolve('./mymodule.js')];解析处理真实路径
  • 这样做是否安全或被视为“不良做法”或“仅用于开发”?
  • @jocull 我不认为它是安全的,因为它可能会重新创建类和函数或任何导出,与===比较时会导致不同的引用
【解决方案8】:

nodemon 在谷歌搜索中最先出现,它似乎可以解决问题:

npm install nodemon -g
cd whatever_dir_holds_my_app
nodemon app.js

【讨论】:

    【解决方案9】:

    如果有人仍然提出这个问题并想只使用标准模块来解决它,我做了一个简单的例子:

    var process = require('process');
    var cp = require('child_process');
    var fs = require('fs');
    
    var server = cp.fork('server.js');
    console.log('Server started');
    
    fs.watchFile('server.js', function (event, filename) {
        server.kill();
        console.log('Server stopped');
        server = cp.fork('server.js');
        console.log('Server started');
    });
    
    process.on('SIGINT', function () {
        server.kill();
        fs.unwatchFile('server.js');
        process.exit();
    });
    

    此示例仅适用于一个文件(server.js),但可以使用文件数组、for 循环获取所有文件名或通过查看目录来适应多个文件:

    fs.watch('./', function (event, filename) { // sub directory changes are not seen
        console.log(`restart server`);
        server.kill();
        server = cp.fork('server.js');    
    })
    

    此代码是为 Node.js 0.8 API 编写的,它不适合某些特定需求,但适用于一些简单的应用程序。

    更新: 这个功能在我的模块simpleRGitHub repo中实现了

    【讨论】:

    • 这是一个伟大而简单的解决方案。我只是将它用于一个机器人,当主持人告诉它时,它应该从 git 进行自我更新。问题是,一旦您进入应用程序,您就无法重新启动自己。但是,我可以使用您的方法生成机器人实例并观察点文件。然后机器人会自我更新,接触点文件,并由启动器自动重新启动。太棒了!
    • @Fred 我很高兴听到这个消息 :) 我将在一个模块中实现这个解决方案,很快我想,我对如何扩展它的功能有了更多的想法
    • 如果不需要watch文件,可以不使用fs重新加载,通过监听不同的信号。
    【解决方案10】:

    supervisor 的一个很好的最新替代方案是 nodemon

    监控 node.js 应用程序中的任何更改并自动重启服务器 - 非常适合开发

    nodemon 与不带npx 的Node 版本一起使用(v8.1 及以下版本,不建议使用):

    $ npm install nodemon -g
    $ nodemon app.js
    

    或者将nodemon 与带有npx 的Node 版本一起使用(v8.2+):

    $ npm install nodemon
    $ npx nodemon app.js
    

    或者在 package.json 中使用 npm 脚本作为 devDependency:

    "scripts": {
      "start": "nodemon app.js"
    },
    "devDependencies": {
      "nodemon": "..."
    }
    

    【讨论】:

    • 如果你想在Nitrous.io中使用它——$ nodemon -L yourfile.js(完整的解释在coderwall.com/p/aqxl_q
    • 但是在这种情况下,它也会重新启动服务器进程。
    • automatically restart the server - perfect for development 太夸张了。重新加载服务器可能意味着登录后端服务,这在我的情况下确实需要很长时间。 “完美的开发”类似于热重载类,而进程在内存中运行而不会丢失状态,就像 android studio 在您更改源代码时所做的那样。
    • 使用npm install [--save-dev | -D] nodemon 将安装限制在项目范围内。
    • 这只是重新启动服务器,仍然需要手动重新加载网络客户端。
    【解决方案11】:

    使用这个:

    function reload_config(file) {
      if (!(this instanceof reload_config))
        return new reload_config(file);
      var self = this;
    
      self.path = path.resolve(file);
    
      fs.watchFile(file, function(curr, prev) {
        delete require.cache[self.path];
        _.extend(self, require(file));
      });
    
      _.extend(self, require(file));
    }
    

    你现在要做的就是:

    var config = reload_config("./config");
    

    配置会自动重新加载:)

    【讨论】:

    • 获得了一个不依赖非 Node 框架的版本?
    【解决方案12】:

    loaddir 是我递归快速加载目录的解决方案。

    可以返回

    { 'path/to/file': 'fileContents...' } 要么 { path: { to: { file: 'fileContents'} } }

    它有callback,当文件改变时会被调用。

    它处理文件足够大以至于在完成写入之前调用watch 的情况。

    我已经在项目中使用它一年左右,最近才向它添加了 Promise。

    帮我进行战斗测试!

    https://github.com/danschumann/loaddir

    【讨论】:

      【解决方案13】:

      您可以使用自动重新加载来重新加载模块而无需关闭服务器。

      安装

      npm install auto-reload
      

      示例

      数据.json

      { "name" : "Alan" }
      

      test.js

      var fs = require('fs');
      var reload = require('auto-reload');
      var data = reload('./data', 3000); // reload every 3 secs
      
      // print data every sec
      setInterval(function() {
          console.log(data);
      }, 1000);
      
      // update data.json every 3 secs
      setInterval(function() {
          var data = '{ "name":"' + Math.random() + '" }';
          fs.writeFile('./data.json', data);
      }, 3000);
      

      结果:

      { name: 'Alan' }
      { name: 'Alan' }
      { name: 'Alan' }
      { name: 'Alan' }
      { name: 'Alan' }
      { name: '0.8272748321760446' }
      { name: '0.8272748321760446' }
      { name: '0.8272748321760446' }
      { name: '0.07935990858823061' }
      { name: '0.07935990858823061' }
      { name: '0.07935990858823061' }
      { name: '0.20851597073487937' }
      { name: '0.20851597073487937' }
      { name: '0.20851597073487937' }
      

      【讨论】:

        【解决方案14】:

        另一个简单的解决方案是使用 fs.readFile 而不是 require 您可以保存一个包含 json 对象的文本文件,并在服务器上创建一个间隔以重新加载该对象。

        优点:

        • 无需使用外部库
        • 与生产相关(更改时重新加载配置文件)
        • 易于实施

        缺点:

        • 你不能重新加载一个模块 - 只是一个包含键值数据的 json

        【讨论】:

          【解决方案15】:

          对于使用 Vagrant 和 PHPStorm 的人来说,file watcher 是一种更快的方法

          • 禁用文件的立即同步,以便您仅在保存时运行命令,然后为 *.js 文件和工作目录创建范围并添加此命令

            vagrant ssh -c "/var/www/gadelkareem.com/forever.sh 重启"

          forever.sh 的样子

          #!/bin/bash
          
          cd /var/www/gadelkareem.com/ && forever $1 -l /var/www/gadelkareem.com/.tmp/log/forever.log -a app.js
          

          【讨论】:

            【解决方案16】:

            我最近提出了这个问题,因为通常的嫌疑人没有使用链接包。如果您像我一样在开发过程中利用npm link 有效地处理由许多包组成的项目,那么依赖项中发生的更改也会触发重新加载,这一点很重要。

            在尝试了 node-mon 和 pm2 之后,即使按照他们的指示额外查看 node_modules 文件夹,他们仍然没有接受更改。尽管这里的答案中有一些自定义解决方案,但对于这样的事情,单独的包更清洁。我今天遇到了node-dev,它无需任何选项或配置即可完美运行。

            来自自述文件:

            与 supervisor 或 nodemon 等工具相比,它不会扫描文件系统以查找要监视的文件。相反,它会挂接到 Node 的 require() 函数中,以仅查看实际需要的文件。

            【讨论】:

              【解决方案17】:

              这个问题的另一个解决方案是使用forever

              Forever 的另一个有用功能是它可以选择重启 当任何源文件发生更改时,您的应用程序。这让你解放了 不必在每次添加功能或修复问题时手动重新启动 漏洞。要在此模式下启动 Forever,请使用 -w 标志:

              forever -w start server.js
              

              【讨论】:

              • 奇怪的是 -w 标志我的 express.js 应用程序不使用 CSS。
              • 'forever' 不是内部或外部命令、可运行程序或批处理文件。
              【解决方案18】:

              不需要使用 nodemon 或其他类似的工具。只需使用 IDE 的功能。

              可能最好的一个是 IntelliJ WebStorm,具有 node.js 的热重载功能(自动服务器和浏览器重载)。

              【讨论】:

                【解决方案19】:

                node-dev 效果很好。 npm install node-dev

                它甚至会在服务器重新加载时提供桌面通知,并会在消息上给出成功或错误。

                在命令行上启动您的应用程序:

                node-dev app.js

                【讨论】:

                  【解决方案20】:

                  nodemon 很棒。我只是为调试和观看选项添加了更多参数。

                  package.json

                    "scripts": {
                      "dev": "cross-env NODE_ENV=development nodemon --watch server --inspect ./server/server.js"
                    }
                  

                  命令:nodemon --watch server --inspect ./server/server.js

                  鉴于:

                  --watch server 更改server 文件夹(包括子文件夹)中的.js.mjs.coffee.litcoffee.json 文件时重新启动应用程序。

                  --inspect启用远程调试。

                  ./server/server.js入口点。

                  然后将以下配置添加到launch.json(VS Code)并随时开始调试。

                  {
                      "type": "node",
                      "request": "attach",
                      "name": "Attach",
                      "protocol": "inspector",
                      "port": 9229
                  }
                  

                  请注意,最好将nodemon 安装为项目的开发依赖项。因此,您的团队成员无需安装它或记住命令参数,他们只需 npm run dev 并开始破解。

                  nodemon 文档中查看更多信息:https://github.com/remy/nodemon#monitoring-multiple-directories

                  【讨论】:

                  • nodemon 的最新版本(至少 1.19.0)不支持通配符。只需使用 nodemon --watch server --inspect ./server/server.js 代替。
                  • 感谢@Alex 提供信息。更新了答案。
                  • 有没有办法通过rebuild来重建应用程序,以便对react应用程序中的任何客户端文件进行任何更改?
                  【解决方案21】:

                  现在使用带有热选项的 WebPack 开发服务器。 您可以在 package.json 中添加这样的脚本:"hot": "cross-env NODE_ENV=development webpack-dev-server --hot --inline --watch-poll",

                  文件中的每一次更改都会自动触发重新编译

                  【讨论】:

                  • 这个答案是错误的。 Webpack 用于前端应用程序,而开发服务器本身就是一个网络服务器。问题是指在 Node.js 中实现的服务器应用程序。它不需要网络服务器。已经是一个了。
                  【解决方案22】:

                  您可以使用 NPM 中的 nodemon。 如果您使用的是 Express 生成器,那么您可以在项目文件夹中使用此命令:

                  nodemon npm start
                  

                  或使用调试模式

                  DEBUG=yourapp:* nodemon npm start
                  

                  也可以直接运行

                  nodemon your-app-file.js
                  

                  希望对您有所帮助。

                  【讨论】:

                  • 如果你输入set DEBUG=myapp:* & nodemon npm start,这适用于Windows
                  • 我还必须按照这些说明全局安装 nodemon stackoverflow.com/a/40359656/74585
                  • 我使用了 express 生成器,nodemon npm start 确实有效,谢谢
                  【解决方案23】:

                  这是一种在 Windows 中使用的低技术方法。将其放入名为serve.bat 的批处理文件中:

                  @echo off
                  
                  :serve
                  start /wait node.exe %*
                  goto :serve
                  

                  现在不要从 cmd shell 运行 node app.js,而是运行 serve app.js

                  这将打开一个运行服务器的新 shell 窗口。批处理文件将阻塞(因为/wait),直到您关闭 shell 窗口,此时原始 cmd shell 将询问“终止批处理作业(Y/N)?”如果您回答“N”,则服务器将重新启动。

                  每次要重启服务器时,关闭服务器窗口并在 cmd shell 中回答“N”。

                  【讨论】:

                    【解决方案24】:

                    我的应用结构:

                    NodeAPP (folder)
                       |-- app (folder)
                          |-- all other file is here
                       |-- node_modules (folder)
                       |-- package.json
                       |-- server.js (my server file)
                    

                    首先使用以下命令安装 reload

                    npm install [-g] [--save-dev] reload
                    

                    然后更改package.json

                    "scripts": {
                        "start": "nodemon -e css,ejs,js,json --watch app"
                    }
                    

                    现在您必须在您的服务器文件中使用重新加载:

                    var express = require('express');
                    var reload = require('reload');
                    var app = express();
                    
                    app.set('port', process.env.PORT || 3000);
                    
                    var server = app.listen(app.get('port'), function() {
                        console.log( 'server is running on port ' + app.get('port'));
                    });
                    
                    reload(server, app);
                    

                    对于最后的更改,您的回复结束发送此脚本

                    <script src="/reload/reload.js"></script>
                    

                    现在使用以下代码启动您的应用程序:

                    npm start
                    

                    【讨论】:

                    【解决方案25】:
                    const cleanCache = (moduleId) => {
                        const module = require.cache[moduleId];
                        if (!module) {
                            return;
                        }
                        // 1. clean parent
                        if (module.parent) {
                            module.parent.children.splice(module.parent.children.indexOf(module), 1);
                        }
                        // 2. clean self
                        require.cache[moduleId] = null;
                    };
                    

                    【讨论】:

                      【解决方案26】:

                      您可以使用browser-refresh 完成此操作。您的节点应用程序会自动重新启动,浏览器中的结果页面也会自动刷新。缺点是您必须将 js sn-p 放在生成的页面上。这是工作示例的repo

                      const http = require('http');
                      const hostname = 'localhost';
                      const port = 3000;
                      
                      const server = http.createServer((req, res) => {
                          res.statusCode = 200;
                          res.setHeader('Content-Type', 'text/html; charset=UTF-8');
                          res.write('Simple refresh!');
                          res.write(`<script src=${process.env.BROWSER_REFRESH_URL}></script>`);
                          res.end();
                      })
                      
                      server.listen(port, hostname, () => {
                          console.log(`Server running at http://${hostname}:${port}/`);
                      
                          if (process.send) {
                              process.send({ event: 'online', url: `http://${hostname}:${port}/` })
                          }
                      
                      });
                      

                      【讨论】:

                        【解决方案27】:

                        我试过pm2:安装简单,使用也方便;结果令人满意。但是,我们必须处理我们想要的 pm2 版本。 pm 2 runtime 是免费版,而 pm2 plus 和 pm2 enterprise 不是免费的。

                        至于Strongloop,我的安装失败或未完成,无法使用。

                        【讨论】:

                          【解决方案28】:

                          如果您谈论服务器端 NodeJS 热重载,假设您希望在服务器上有一个 Javascript 文件,该文件具有描述的快速路由,并且您希望此 Javascript 文件热重载而不是服务器重新启动文件更改然后razzle 可以做到这一点。

                          基本服务器就是一个例子

                          https://github.com/jaredpalmer/razzle/tree/master/examples/basic-server

                          文件https://github.com/jaredpalmer/razzle/blob/master/examples/basic-server/src/server.js如果被修改保存会热重载,服务器不会重启。

                          这意味着您可以编写一个 REST 服务器,该服务器可以使用这个 razzle 进行热重载。

                          【讨论】:

                            猜你喜欢
                            • 2018-08-04
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2015-06-28
                            • 2013-10-06
                            • 2020-11-29
                            • 2016-06-12
                            相关资源
                            最近更新 更多