关于我如何在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()语句中得到一个错误,说'require'没有定义。Process.compile正在计算app.js,但没有关于node.js全局变量的线索。
这是一篇关于Node热重载的博文。它提供了一个github节点分支,您可以使用它来替换节点的安装,以启用热重新加载。
来自博客:
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时,上面的代码将不再生效,并用新代码重新放置本地的re-questHandler。任何已存在的请求将继续使用旧代码,而任何新的请求将使用新代码。所有这些都不需要关闭服务器,不需要反弹任何请求,不需要提前终止任何请求,甚至不需要依赖内置的负载均衡器。
我正在制作一个相当小的节点“东西”,能够加载/卸载模块(所以,也就是说,你可以重新启动你的应用程序的一部分,而不会使整个应用程序关闭)。
我加入了一个(非常愚蠢的)依赖管理,所以如果你想停止一个模块,所有依赖于它的模块也会停止。
到目前为止一切顺利,但是我遇到了如何重新加载模块的问题。显然,只需要从“require”缓存中删除模块就可以完成工作。因为我不热衷于直接改变节点源代码,我想出了一个非常hack -hack的方法:在堆栈中搜索跟踪最后一次调用“require”函数,抓取对它的“cache”字段的引用和..那么,删除对节点的引用:
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 ] )
}
显然,这工作得很好。我完全不知道参数["1"]是什么意思,但它在做它的工作。我相信节点总有一天会实现重载功能,所以我想现在这个解决方案也是可以接受的。
(顺便说一句。我的“东西”将在这里:https://github.com/cheng81/wirez,几周后去那里,你应该看到我在说什么)
如果有人仍然遇到这个问题,并希望仅使用标准模块来解决它,我做了一个简单的例子:
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制作的,它不适合一些特定的需求,但可以在一些简单的应用程序中工作。
更新:
这个函数是在我的模块simple, GitHub repo中实现的
一个好的、最新的替代supervisor的方法是nodemon:
监视node.js应用程序中的任何更改并自动重新启动服务器-非常适合开发
在没有npx的Node版本下使用nodemon(不建议使用v8.1及以下版本):
$ npm install nodemon -g
$ nodemon app.js
或者对捆绑了npx的Node版本使用nodemon (v8.2+):
$ npm install nodemon
$ npx nodemon app.js
或者在package.json中作为一个npm脚本中的devDependency:
"scripts": {
"start": "nodemon app.js"
},
"devDependencies": {
"nodemon": "..."
}
您可以使用自动重新加载来重新加载模块,而无需关闭服务器。
安装
npm install auto-reload
例子
data.json
{ "name" : "Alan" }
. 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' }
没有一个恶魔是伟大的。我只是为调试和监视选项添加了更多参数。
package.json
"scripts": {
"dev": "cross-env NODE_ENV=development nodemon --watch server --inspect ./server/server.js"
}
执行命令:nodemon——watch server——inspect ./server/server.js
而:
当更改server文件夹(包括子文件夹)中的.js, .mjs, .coffee, .litcoffee和.json文件时重新启动应用程序。
——inspect开启远程调试。
./server/server.js入口。
然后添加以下配置以启动。json (VS Code)和开始调试随时。
{
"type": "node",
"request": "attach",
"name": "Attach",
"protocol": "inspector",
"port": 9229
}
注意,最好将nodemon安装为项目的开发依赖项。所以你的团队成员不需要安装它或记住命令参数,他们只需要npm运行dev并开始黑客。
查看更多关于nodemon文档的信息:https://github.com/remy/nodemon#monitoring-multiple-directories
这里有一个在Windows中使用的低技术方法。把它放在一个名为server .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”。
我的应用结构:
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"
}
现在你必须在你的服务器文件中使用reload:
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
你可以通过浏览器刷新来实现。您的节点应用程序自动重启,您的结果页面在浏览器也自动刷新。缺点是你必须把js片段生成的页面。下面是工作示例的回购。
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}/` })
}
});
这是相当简单的,只是做这个自己没有任何依赖…内置的文件监视器已经足够成熟,它不像以前那么糟糕
你不需要任何复杂的子进程产卵/杀死和管道性病进出…你只需要一个简单的网络工作者,这就是全部!web Worker也是我在浏览器中使用的工具…所以坚持使用网络技术吧!Worker也将登录到控制台
import { watch } from 'node:fs/promises'
import { Worker } from 'node:worker_threads'
let worker = new Worker('./app.js')
async function reloadOnChange (dir) {
const watcher = watch(dir, { recursive: true })
for await (const change of watcher) {
if (change.filename.endsWith('.js')) {
worker.terminate()
worker = new Worker('./app.js')
}
}
}
// All the folder to watch for
['./src', './lib', './test'].map(reloadOnChange)
这可能不是最好的解决方案,如果你使用javascript以外的任何东西,不依赖于一些构建过程。