可以卸载、删除或取消需要 Nodejs 模块

Posted

技术标签:

【中文标题】可以卸载、删除或取消需要 Nodejs 模块【英文标题】:Possible to unload, delete, or unrequire a Nodejs module 【发布时间】:2020-05-11 21:34:26 【问题描述】:

我们正在构建一个允许用户提供自己的“模块”来运行的 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、单击该网页上的按钮等。

【问题讨论】:

这能回答你的问题吗? How to remove module after "require" in node.js? 是的,我看到了这个问题并尝试了几乎所有的答案。但是,即使将答案应用于此问题中的示例,计时器仍会记录内容!您还有其他建议吗? 它是基于网络的应用程序吗?我的意思是这个模块会在浏览器端运行吗?顺便说一句,您不能使用delete 运算符删除函数,它只能用于删除对象属性 您是否尝试先将 Mod 分配给某个局部变量,然后在超时清除/设置为 null 该变量?或者你可以使用clearInterval @AhmetZeybek 这是在 Electron 应用程序上,应该提到我的错误。 【参考方案1】:

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

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

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

【讨论】:

这是一个非常有用的解释。我检查了其他问题,但没有一个人解释了这一点,所以我认为我们只是错误地接近它。我想将此标记为已接受的答案,但有没有办法可以使用子进程或虚拟机提供一个示例?我们已经尝试过使用 VM,但在计时器将无限期记录的情况下,似乎仍然存在相同的问题。如果有帮助,它将运行的环境是一个 Electron 应用程序。谢谢你:) @TMack - 我在问用户代码需要做什么,因为它需要访问您的应用程序中的哪些数据很重要,它试图达到什么结果也很重要。当您将事情转移到另一个流程时,您如何做到这一点取决于您必须与它沟通什么以及它必须沟通什么,以及该沟通是否只在一次事情上进行,然后就完成了。 它们的行为类似于插件。该应用程序是一个浏览器,还具有一些自动化功能,因此用户可以编写自己的脚本来自动化诸如单击网站上的按钮等操作。最终,他们需要能够像这样运行多个脚本,并且可以随时启动和停止这些脚本。一般来说,我们希望这些脚本的上下文与常规节点模块非常相似,因为它可以访问大多数节点功能并且能够require 包。我提到这一点是因为我们遇到了一个需要没有在 vm 中工作 @TMack - 这仍然无法解释它与您的主服务器的通信级别。你能给我一些他们做什么的例子吗?你说点击按钮 - 但如何?用户代码是否在您的服务器上运行?或者,插入您的网页并在浏览器中运行?我现在真的很困惑。如果它们在持续运行并可能参与大量服务器事件,则将插件移出进程时需要做更多的工作,但可以做到。 您好,再次感谢您的耐心我在问题中添加了一个示例。很简单,但renderer.js 将加载plugins/modules,它可以访问webview,这是Electron 附带的iframe 的精美版本。然后该模块可以操作 webview 并利用 requireing 与我们的应用程序预安装的其他模块!真的希望这是有道理的,非常感谢您的关注。

以上是关于可以卸载、删除或取消需要 Nodejs 模块的主要内容,如果未能解决你的问题,请参考以下文章

APP功能测试要点

有没有办法取消 node.js 对 require 模块的缓存

NODEJS卸载或安装出现Invalid drive错误

APP测试10点

如何从 nodejs 模块中删除全局上下文?

如何卸载nodejs sqlserver