Node 里的模块是什么?
node 中,每个文件模块都是一个对象,它定义如下:
function Module(id, parent) {
this.id = id;
this.exports = {};
this.parent = parent;
this.filename = null;
this.loaded = false;
this.children = [];
}
module.exports = Module;
var module = new Module(filename, parent);
require的模块加载机制
- 计算模块的绝对路径
- 如果缓存中有该模块,则从缓存中取出该模块
- 按优先级依次寻找并编译执行模块,将模块推入缓存(require.cache)中
- 输出模块的exports 属性
Node中内存泄漏问题和解决方案
内存泄漏原因
- 全局变量:全局变量挂载在root对象上,不会被清除掉
- 闭包:如果闭包未释放,就会导致内存泄漏
- 事件监听: 对同一个事件重复监听,忘记移除(removeListener),将造成内存泄漏
解决方案
- 最容易出现也是最难排查的就是事件监听造成的内存泄漏,所以事件监听这块需要格外注意小心使用。
- 如果出现了内存泄漏问题,需要检测内存使用情况,对内存泄漏的位置进行定位,然后对对应的内存泄漏代码进行修复
- 使用浏览器的快照功能来测试快速定位问题
两个模块相互引用会发生什么问题?
假设 A 和 B 模块相互引用,此时运行 A 模块
- A 模块将会被缓存,但是此时缓存的是一个未执行完毕的 A 模块
- A 模块引入 B 模块将会被完整地加载并且正常的使用
- B 模块中调用 A 模块将会是默认的空对象(module.exports的默认值),A 模块不具有任何功能
如何实现热更新
node 中有一个Api 是 require.cache,如果这个对象中的引用被清除后,下次再调用就会重新加载,这个机制可以用来热加载更新的模块
function clearCache(modulePath) {
const path = require.resolve(modulePath)
if (require.cache[path]) {
require.cache[path] = null
}
}
然后使用 fs.watchFile 监听文件的更改,文件更改后调用 clearCache 传入对应的模块名
使用 pm2 reload 也可以实现暴力更新,它会保证在新的实例重启成功后才会把旧的进程杀死,可以保证服务一直能够响应
Node 适合处理 I/O 密集型任务
- node 在处理 I/O 密集型任务的时候可以异步调用,利用事件循环的处理能力,资源占用极少,事件循环可以避开多线程调用,在调用方面是单线程,内部处理其实是多线程
- javascript 是单线程的原因,node 不合适处理 CPU 密集型的任务, CPU 密集型的任务会导致CPU 时间片不能释放,使得后续 I/O 无法发起,从而会造成堵塞,这时候可以使用多线程来处理,但是js不支持多线程