投身烈火
Keep Walking

nodejs热更新的方法

嗯,没错,我又来要放水了今天……这个周末停休了,没辙没辙的……

背景

话说上周看了朋友的一篇博客 通过 respawn 加强需要即时生效的开发环境构建系统 ,里面说到了用子进程来更新应用的方法,个人感觉就是像Jetty那样的热部署机制吧。但是归根结底,还是要重新启动整个应用才能完成更新,这点上来说感觉并不完美。那么能不能做到真正的0重启热更新代码呢?本文提一个另类的思路给大家参考一下。

思路

废话不多说了,认真读过nodejs文档的人都应该注意过这个api require.cache 。文档里写的很清楚,如果这个对象中的引用被清除了,下次再调用就会重新加载,我们可以使用这个机制来热加载更新的模块。

下面有个小栗子可以验证这个思路的可行性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// main.js
var fs = require('fs');
function cleanCache (module) {
var path = require.resolve(module);
require.cache[path] && (require.cache[path] = null);
}
function write (text) {
return function () {
return new Promise(function(resolve) {
setTimeout(function(){
fs.writeFileSync("test.js", "module.exports = \"" + text + "\";");
resolve(true)
},1000)
});
}
}
fs.watchFile('test.js',{
interval:100
}, function () {
cleanCache('./test');
console.log(require('./test'));
});
write("hello")().then(write("my name is zxc"))
// test.js
module.exports = '';

问题

当然这种方法看上去挺好,但是存在的问题也挺多的。首先,这个方法没办法更新c\c++模块,另外,原生模块也是没法更新的。然后,由于 node.js 本身缺乏对有效的留存对象的扫描机制,有时会出现老模块的资源无法释放的问题,比如setInterval中引用的模块之类的……╮( ̄▽ ̄)╭。总之,生产环境就别想了,但是用来开发调试些经常修改的简单模块的话,会是个不错的选择,比如router之类的配置文件。

总结

怎么说呢,既然是个另类的思路,所以应用场景远没有线程替换那种方法大的。如果是生产环境,依然是推荐使用使用重启或者 PM2 的 hot reload 功能来保证稳定性。另外,webpack也有热替换的功能,HMR嘛,其实这也可以算是一个思路,具体的可以参考这个系列的文章Backend Apps with Webpack

参考资料