如何在V8 JavaScript引擎中启用和使用ES6模块?

9

我在我的(Windows桌面)C++应用程序中使用了嵌入式V8引擎。我知道V8支持ES6模块,那么我该如何在我的应用程序中启用和使用这个功能呢?

我不希望有人提供完整的示例,但是提供一个高层次的答案指引我(以及未来的读者)朝着正确的方向前进就可以满足我的期望。


1
你尝试了一些ES6的特性,但它们没有起作用吗?你是如何嵌入V8引擎的? - Anas
我还没有尝试仅启用功能@Anas,似乎一旦启用,必须有一些配置需要我完成才能实际工作。否则V8怎么知道在我的文件系统上查找文件的位置呢?例如,如果我使用以下JS代码: import {x} from "./y.js"; 我认为V8不会知道在哪里查找y.js - Boinst
@Anas RE "how are you embedding", 我认为我正在以一种非常标准的方式进行嵌入。我所做的一切都与“嵌入指南”(https://github.com/v8/v8/wiki/Embedder%27s-Guide)保持一致。 - Boinst
考虑到缺乏文档,我发现V8对模块的实现非常有帮助,因为它非常贴近规范的文字/语言。https://www.ecma-international.org/ecma-262/#sec-source-text-module-records - Ron Burk
1个回答

14

以下是一个实际例子,由于我曾打算在某个时间点编写一些实际示例,因此没有来得及提供V8的实际示例。如果需要了解使用示例,建议参考Node.js的实现或我的实现,两者的布局非常相似(都是由同一批人员编写)。同时,D8,V8的CLI调试器也有一种实现。

Local<String> source_text = String::NewFromUtf8(
    isolate, "import 'some thing'; 1 + 1");

ScriptOrigin origin(String::NewFromUtf8("main.mjs"),      // specifier
                    Integer::New(isolate, 0),             // line offset
                    Integer::New(isolate, 0),             // column offset
                    False(isolate),                       // is cross origin
                    Local<Integer>(),                     // script id
                    Local<Value>(),                       // source map URL
                    False(isolate),                       // is opaque
                    False(isolate),                       // is WASM
                    True(isolate));                       // is ES6 module
Context::Scope context_scope(context);
ScriptCompiler::Source source(source_text, origin);
Local<Module> module;
if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
  // if you have a v8::TryCatch, you should check it here.
  return;
}

// You can resolve import requests ahead of time (useful for async)
for (int i = 0; i < module->GetModuleRequestsLength(); i++) {
  Local<String> specifier = module->GetModuleRequest(i); // "some thing"
}

// or you can resolve them sync in the InstantiateModule callback
module->InstantiateModule(context, [](Local<Context> context, // "main.mjs"
                                      Local<String> specifier, // "some thing"
                                      Local<Module> referrer) {
  return Local<Module>();
});

// setting this callback enables dynamic import
isolate->SetImportModuleDynamicallyCallback([](Local<Context> context,
                                               Local<ScriptOrModule> referrer,
                                               Local<String> specifier) {
  return MaybeLocal<Promise>();
});

// setting this callback enables import.meta
isolate->SetHostInitializeImportMetaObjectCallback([](Local<Context> context,
                                                      Local<Module> module,
                                                      Local<Object> meta) {
  // meta->Set(key, value); you could set import.meta.url here
});

Local<Value> result;
if (module->Evaluate(context).ToLocal(&result)) {
  String::Utf8Value utf8(isolate, result);
  printf("module eval result: %s\n", *utf8);
} else {
  // once again, if you have a v8::TryCatch, use it here.
}

你如何扩展你的示例以支持从彼此导入和导出的多个模块?当然,还会有一个主入口模块。 - synchronizer
@synchronizer,您可以从InstantiateModule中返回所请求的模块。 - snek
你不是简单地同时加载一个文件数组,或者类似的东西吗?你的意思是我需要自己找出依赖关系吗? - synchronizer
@synchronizer 将特定的映射规范与模块完全留给主机。您需要负责决定如何解析导入请求,源文件从哪里加载等。 - snek
也许我太笨了,您能帮我解决import请求吗?您需要遍历ModuleRequests中的所有specifier,但是我不知道下一步该怎么做。我应该如何告诉V8将此模块链接到那个specifier? - Cisco Ortega
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接