当 importModuleDynamically 为 vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER 时
【When importModuleDynamically is vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER】
该选项目前不支持 vm.SourceTextModule。
【This option is currently not supported for vm.SourceTextModule.】
使用此选项时,当在已编译的代码中发起 import() 时,Node.js 会使用主上下文的默认 ESM 加载器来加载所请求的模块,并将其返回给正在执行的代码。
【With this option, when an import() is initiated in the compiled code, Node.js
would use the default ESM loader from the main context to load the requested
module and return it to the code being executed.】
这使得被编译的代码可以访问 Node.js 内置模块,例如 fs 或 http。如果代码在不同的上下文中执行,需要注意的是,从主上下文加载的模块创建的对象仍然来自主上下文,并且在新上下文中并不是内置类的 instanceof 实例。
【This gives access to Node.js built-in modules such as fs or http
to the code being compiled. If the code is executed in a different context,
be aware that the objects created by modules loaded from the main context
are still from the main context and not instanceof built-in classes in the
new context.】
const { Script, constants } = require('node:vm');
const script = new Script(
'import("node:fs").then(({readFile}) => readFile instanceof Function)',
{ importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER });
// false: URL loaded from the main context is not an instance of the Function
// class in the new context.
script.runInNewContext().then(console.log);import { Script, constants } from 'node:vm';
const script = new Script(
'import("node:fs").then(({readFile}) => readFile instanceof Function)',
{ importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER });
// false: URL loaded from the main context is not an instance of the Function
// class in the new context.
script.runInNewContext().then(console.log);此选项还允许脚本或函数加载用户模块:
【This option also allows the script or function to load user modules:】
import { Script, constants } from 'node:vm';
import { resolve } from 'node:path';
import { writeFileSync } from 'node:fs';
// Write test.js and test.txt to the directory where the current script
// being run is located.
writeFileSync(resolve(import.meta.dirname, 'test.mjs'),
'export const filename = "./test.json";');
writeFileSync(resolve(import.meta.dirname, 'test.json'),
'{"hello": "world"}');
// Compile a script that loads test.mjs and then test.json
// as if the script is placed in the same directory.
const script = new Script(
`(async function() {
const { filename } = await import('./test.mjs');
return import(filename, { with: { type: 'json' } })
})();`,
{
filename: resolve(import.meta.dirname, 'test-with-default.js'),
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
});
// { default: { hello: 'world' } }
script.runInThisContext().then(console.log);const { Script, constants } = require('node:vm');
const { resolve } = require('node:path');
const { writeFileSync } = require('node:fs');
// Write test.js and test.txt to the directory where the current script
// being run is located.
writeFileSync(resolve(__dirname, 'test.mjs'),
'export const filename = "./test.json";');
writeFileSync(resolve(__dirname, 'test.json'),
'{"hello": "world"}');
// Compile a script that loads test.mjs and then test.json
// as if the script is placed in the same directory.
const script = new Script(
`(async function() {
const { filename } = await import('./test.mjs');
return import(filename, { with: { type: 'json' } })
})();`,
{
filename: resolve(__dirname, 'test-with-default.js'),
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
});
// { default: { hello: 'world' } }
script.runInThisContext().then(console.log);使用主上下文中的默认加载器加载用户模块有一些注意事项:
【There are a few caveats with loading user modules using the default loader from the main context:】
- 要解析的模块将相对于传递给
vm.Script或vm.compileFunction()的filename选项。解析可以使用绝对路径或 URL 字符串作为filename。如果filename是既不是绝对路径也不是 URL 的字符串,或者未定义,则解析将相对于进程的当前工作目录。在vm.createContext()的情况下,解析始终相对于当前工作目录,因为该选项仅在没有引用脚本或模块时使用。 - 对于任何解析为特定路径的
filename,一旦进程成功从该路径加载了特定模块,结果可能会被缓存,并且随后从同一路径加载相同模块时会返回相同的内容。如果filename是 URL 字符串,且包含不同的查询参数,则缓存不会被命中。对于非 URL 字符串的filename,目前没有办法绕过缓存行为。