首页 > 解决方案 > 可以卸载、删除或取消需要 Nodejs 模块

问题描述

我们正在构建一个 Electron 应用程序,允许用户提供他们自己的“模块”来运行。我们正在寻找一种方法来要求模块,然后在需要时删除或终止模块。我们查看了一些似乎讨论这个主题的教程,但我们似乎无法让模块完全终止。我们通过在模块中使用定时器来探索这一点,并且可以观察到即使在模块引用被删除后定时器仍在运行。

https://repl.it/repls/QuerulousSorrowfulQuery

index.js

// Load module
let Mod = require('./mod.js'); 

// Call the module function (which starts a setInterval)
Mod();

// Delete the module after 3 seconds
setTimeout(function () {
  Mod = null;
  delete Mod;
  console.log('Deleted!')
}, 3000);

./mod.js

function Mod() {
  setInterval(function () {
    console.log('Mod log');
  }, 1000);
}

module.exports = Mod;

预期产出

Mod log
Mod log
Deleted!

实际输出

Mod log
Mod log
Deleted!
Mod log 
...
(continues to log 'Mod log' indefinitely)

也许我们想多了,也许模块不会占用内存,但是我们加载的模块将有非常密集的工作负载,并且能够随意停止它们似乎很重要。

使用真实用例进行编辑

这就是我们目前使用这种技术的方式。这两个问题是以正确的方式加载模块并在完成后卸载模块。

renderer.js(在具有访问权限的浏览器上下文中运行document等)

const webview = document.getElementById('webview'); // A webview object essentially gives us control over a webpage similar to how one can control an iframe in a regular browser.
const url = 'https://ourserver.com/module.js';
let mod;
request({
  method: 'get',
  url: url,
}, function (err, httpResponse, body) {
  if (!err) {
    mod = requireFromString(body, url); // Module is loaded
    mod(webview); // Module is run
    // ...
    // Some time later, the module needs to be 'unloaded'. 
    // We are currently 'unloading' it by dereferencing the 'mod' variable, but as mentioned above, this doesn't really work. So we would like to have a way to wipe the module and timers and etc and free up any memory or resources it was using!
    mod = null;
    delete mod;
  } 
})

function requireFromString(src, filename) {
  var Module = module.constructor;
  var m = new Module();
  m._compile(src, filename);
  return m.exports;
}

https://ourserver.com/module.js

// This code module will only have access to node modules that are packaged with our app but that is OK for now!
let _ = require('lodash'); 
let obj = {
  key: 'value'
}
async function main(webview) {
  console.log(_.get(obj, 'key')) // prints 'value'
  webview.loadURL('https://google.com') // loads Google in the web browser
}

module.exports = main;

以防万一阅读者不熟悉 Electron,renderer.js可以访问与 iframe 几乎相同的“webview”元素。这就是为什么将它传递给“module.js”将允许模块访问操作网页,例如更改 URL、单击该网页上的按钮等。

标签: javascriptnode.jsmoduleelectronrequire

解决方案


没有办法杀死一个模块并停止或关闭它正在使用的任何资源。这不是 node.js 的一个特性。这样的模块可以有计时器、打开的文件、打开的套接字、正在运行的服务器等……此外,node.js 不提供“卸载”曾经加载过的代码的方法。

您可以从模块缓存中删除模块,但这不会影响现有的、已加载的代码或其资源。

我知道的唯一万无一失的方法是将用户的模块加载到作为子进程加载的单独 node.js 应用程序中,然后您可以退出该进程或终止该进程,然后操作系统将回收它正在使用的任何资源并卸载一切都来自记忆。这种子进程方案还有一个优点,就是用户的代码与你的主服务器代码更加隔离。如果您愿意,您甚至可以通过在 VM 中运行其他进程来进一步隔离它。


推荐阅读