javascript - 在同一进程中创建 "fresh and empty"Node.js JavaScript 上下文的最佳方法?

标签 javascript node.js v8

Node.js 和 v8 专家的问题。
我正在开发 Siesta 的新版本测试工具。
默认情况下,Siesta 会在新创建的 Node.js 进程中运行每个测试。但是,我想避免产生新进程的开销,而是提供在空 JavaScript 上下文中运行测试的能力。
可以使用内置的 vm 创建这样的上下文。模块。但是,以这种方式创建的上下文是一个空的 JavaScript 上下文,而不是一个空的 Node.js 上下文。例如,它没有全局变量 process :

> require('vm').runInNewContext('process')
evalmachine.<anonymous>:1
process
^

Uncaught ReferenceError: process is not defined
    at evalmachine.<anonymous>:1:1
    at Script.runInContext (vm.js:143:18)
    at Script.runInNewContext (vm.js:148:17)
    at Object.runInNewContext (vm.js:303:38)
    at REPL30:1:15
    at Script.runInThisContext (vm.js:133:18)
    at REPLServer.defaultEval (repl.js:484:29)
    at bound (domain.js:413:15)
    at REPLServer.runBound [as eval] (domain.js:424:12)
    at REPLServer.onLine (repl.js:817:10)
> 
所以问题是——在同一进程中创建一个新鲜的空 Node.js 上下文的最佳方法是什么? 我希望这样的上下文具有所有常规全局变量,例如 process , require另外,我希望这样的上下文有一个单独的并且最初是空的模块缓存,这样即使在主上下文中加载了一些模块,它也会在新的上下文中再次加载。
当然,我可以将全局变量从主上下文映射到新上下文,但这意味着这些全局变量在上下文之间共享,并且我的目标是上下文隔离。此外,模块缓存也将被共享。
我相信 JavaScript 和 Node.js 上下文之间的区别在于后者是用某个脚本初始化的。是否有可能以某种方式获取该脚本的来源并在新的上下文中执行它?
谢谢!

最佳答案

这就是 NodeJS loads a new module 时发生的情况。 :

(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
他们使用他们所谓的 模块包装
所以你必须使用 VM 做类似的事情
require('vm').runInNewContext('the code you are running', {
  module:  // your empty module or a wrapper
  exports: // a reference to the module.exports
  require: // your empty require or a wapper
  __filename: // your __filename
  __dirname: // your __dirname
  process
})
我成功创建了有效的新空 require调试模块类/函数内部后的函数
如果您创建以下 2 个文件并运行 isolated.js,您会发现它确实每次都在重新加载,同时原始缓存保持不变(尽管我相信异步执行可能会产生意想不到的结果)
// isolated.js
const Module = require('module')
const vm = require('vm')
const path = require('path')

// this is just to show it wont load it again
const print = require('./print')
print('main file')

const getNewContext = () => {
  const mod = new Module()
  const filename = path.join(__dirname, `test-filename-${Math.random().toString().substr(-6)}.js`)
  const req = Module.createRequire(filename)
  req.cache = Object.create(null)

  return {
    module: mod, // your empty module or a wrapper
    exports: mod.exports, // a reference to the module.exports
    require: req, // your empty require or a wapper
    // require: mod.require, // your empty require or a wapper
    __filename: filename,
    __dirname: path.dirname(filename),
    console,
    process
  }
}

// print('runningInNewContext')
const jsCode = `
// What?
const log = console.log.bind(null, Date.now())
log({from:'print', cache: require.cache})
const print = require('./print.js')
log(print.toString())
print('evaluated code')
log({from:'print', cache: require.cache})
`

function customRunInNewContext (jsCode, context) {
  const { _cache } = Module

  Module._cache = Object.create(null)
  vm.runInNewContext(jsCode, context)
  Module._cache = _cache
}

customRunInNewContext(jsCode, getNewContext())
customRunInNewContext(jsCode, getNewContext())
// print.js
const log = console.log.bind(null, 'PRINT loaded at', new Date().toISOString(), module.parent.filename)
process.stdout.write('\n ====> loading print module\n\n')

module.exports = function print (...args) {
  log(...args)
}

当你跑
node ./isolated.js
您应该会看到消息 ====> loading print module多次

关于javascript - 在同一进程中创建 "fresh and empty"Node.js JavaScript 上下文的最佳方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66139963/

相关文章:

匿名函数中的javascript奇怪行为

javascript - Selenium - 使用 javascript 启用元素在 Jenkins 中不起作用

javascript - 请求的资源 : Mean stack 上不存在 'Access-Control-Allow-Origin' header

SQL 查询为 SQLite 返回 JSON 中的嵌套对象数组

javascript - 在 Nodejs/V8 扩展中通过 Javascript 将 C++ 对象传递给 C++ 函数

javascript - 如何将单个字符串变成多个字符串?

node.js - 如何发现我的机器人出了什么问题

node.js - 无法连接到服务器 - Migration Parse Server iOS

c++ - 在 Node.js 插件的 void * 中检索和存储 V8 对象

garbage-collection - Node.js 和 V8 垃圾回收