- assert断言
- async_hooks异步钩子
- buffer缓冲区
- C++插件
- C/C++插件(使用Node-API)
- C++嵌入器
- child_process子进程
- cluster集群
- CLI命令行
- console控制台
- crypto加密
- debugger调试器
- deprecation弃用
- dgram数据报
- dns域名服务器
- domain域
- Error错误
- events事件触发器
- fs文件系统
- global全局变量
- http超文本传输协议
- http2超文本传输协议2.0
- https安全超文本传输协议
- inspector检查器
- Intl国际化
- module模块
- module/cjsCommonJS模块
- module/esmECMAScript模块
- module/package包模块
- net网络
- os操作系统
- path路径
- perf_hooks性能钩子
- policy安全策略
- process进程
- punycode域名代码
- querystring查询字符串
- readline逐行读取
- repl交互式解释器
- report诊断报告
- stream流
- string_decoder字符串解码器
- timers定时器
- tls安全传输层
- trace_events跟踪事件
- tty终端
- url网址
- util实用工具
- v8引擎
- vm虚拟机
- wasi网络汇编系统接口
- worker_threads工作线程
- zlib压缩
Node.js v12.22.12 文档
- Node.js 12.22.12
- ► 目录
-
►
索引
- assert 断言
- async_hooks 异步钩子
- buffer 缓冲区
- C++插件
- C/C++插件(使用Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- crypto 加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- dns 域名服务器
- domain 域
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS模块
- module/esm ECMAScript模块
- module/package 包模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- policy 安全策略
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- stream 流
- string_decoder 字符串解码器
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
- ► 其他版本
- 文档搜索
C++ 嵌入器#
Node.js 提供了许多 C++ API,可用于在 Node.js 环境中从其他 C++ 软件执行 JavaScript。
这些 API 的文档可以在 Node.js 源代码树的 src/node.h 中找到。 除了 Node.js 暴露的 API,V8 嵌入器 API 还提供了一些必需的概念。
因为使用 Node.js 作为嵌入式库与编写由 Node.js 执行的代码不同,破坏性更改不遵循典型的 Node.js 弃用策略,并且可能在每个语义化主版本上发生,而没有事先警告.
嵌入式应用程序的示例#
以下章节将概述如何使用这些 API 从头开始创建应用程序,该应用程序将执行相当于 node -e <code>
的操作,也就是将使用一段 JavaScript 并在特定于 Node.js 的环境中运行。
可以在 Node.js 源代码树中找到完整的代码。
设置每个进程的状态#
Node.js 需要一些每个进程的状态管理才能运行:
- 为 Node.js 命令行选项解析的参数,
- V8 每个进程要求,例如
v8::Platform
实例。
下面的示例展示了如何设置这些。
一些类名分别来自 node
和 v8
C++ 命名空间。
int main(int argc, char** argv) {
argv = uv_setup_args(argc, argv);
std::vector<std::string> args(argv, argv + argc);
std::vector<std::string> exec_args;
std::vector<std::string> errors;
// 解析 Node.js 命令行选项,
// 并打印尝试解析它们时发生的任何错误。
int exit_code = node::InitializeNodeWithArgs(&args, &exec_args, &errors);
for (const std::string& error : errors)
fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str());
if (exit_code != 0) {
return exit_code;
}
// 创建 v8::Platform 实例。
// `MultiIsolatePlatform::Create()` 是一种创建 v8::Platform 实例的方法,Node.js 在创建时可以使用它
// 工作线程。当没有 `MultiIsolatePlatform` 实例时,
// 工作线程被禁用。
std::unique_ptr<MultiIsolatePlatform> platform =
MultiIsolatePlatform::Create(4);
V8::InitializePlatform(platform.get());
V8::Initialize();
// 此函数的内容见下文。
int ret = RunNodeInstance(platform.get(), args, exec_args);
V8::Dispose();
V8::ShutdownPlatform();
return ret;
}
每个实例的状态#
Node.js 有“Node.js 实例”的概念,通常被称为 node::Environment
。
每个 node::Environment
都与:
- 正好是
v8::Isolate
,即 JS 引擎实例, - 正好是
uv_loop_t
,即事件循环,并且 - 许多
v8::Context
,但只有一个主要的v8::Context
。 node::IsolateData
实例包含的信息可以由使用相同v8::Isolate
的多个node::Environment
共享。 目前,没有针对此场景执行测试。
为了设置 v8::Isolate
,需要提供 v8::ArrayBuffer::Allocator
。
一种可能的选择是默认的 Node.js 分配器,它可以通过 node::ArrayBufferAllocator::Create()
创建。
当插件使用 Node.js C++ Buffer
API 时,使用 Node.js 分配器可以实现较小的性能优化,并且需要在 process.memoryUsage()
中跟踪 ArrayBuffer
内存。
此外,每个用于 Node.js 实例的 v8::Isolate
都需要在 MultiIsolatePlatform
实例中注册和注销(如果正在使用),以便平台知道对于 v8::Isolate
调度的任务使用哪个事件循环。
node::NewIsolate()
辅助函数创建 v8::Isolate
,使用一些 Node.js 特定的钩子(例如 Node.js 错误句柄)设置,并自动将其注册到平台。
int RunNodeInstance(MultiIsolatePlatform* platform,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args) {
int exit_code = 0;
// 设置 libuv 事件循环。
uv_loop_t loop;
int ret = uv_loop_init(&loop);
if (ret != 0) {
fprintf(stderr, "%s: Failed to initialize loop: %s\n",
args[0].c_str(),
uv_err_name(ret));
return 1;
}
std::shared_ptr<ArrayBufferAllocator> allocator =
ArrayBufferAllocator::Create();
Isolate* isolate = NewIsolate(allocator, &loop, platform);
if (isolate == nullptr) {
fprintf(stderr, "%s: Failed to initialize V8 Isolate\n", args[0].c_str());
return 1;
}
{
Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
// 创建 node::IsolateData 实例,
// 稍后将使用 node::FreeIsolateData() 释放该实例。
std::unique_ptr<IsolateData, decltype(&node::FreeIsolateData)> isolate_data(
node::CreateIsolateData(isolate, &loop, platform, allocator.get()),
node::FreeIsolateData);
// 设置新的 v8::Context。
HandleScope handle_scope(isolate);
Local<Context> context = node::NewContext(isolate);
if (context.IsEmpty()) {
fprintf(stderr, "%s: Failed to initialize V8 Context\n", args[0].c_str());
return 1;
}
// 当调用 node::CreateEnvironment() 和 node::LoadEnvironment() 时,
// 需要输入 v8::Context。
Context::Scope context_scope(context);
// 创建 node::Environment 实例,
// 稍后将使用 node::FreeEnvironment() 释放该实例。
std::unique_ptr<Environment, decltype(&node::FreeEnvironment)> env(
node::CreateEnvironment(isolate_data.get(), context, args, exec_args),
node::FreeEnvironment);
// 设置要执行的 Node.js 实例,并在其中运行代码。
// 还有一个变体接受回调
// 并为其提供 `require` 和 `process` 对象,
// 以便它可以根据需要手动编译和运行脚本。
// 此脚本中的 `require` 函数不访问文件系统,
// 并且只能加载 Node.js 内置模块。
// `module.createRequire()` 被用来创建能够从磁盘加载文件的文件,
// 并使用标准的 CommonJS 文件加载器
// 而不是内部的 `require` 函数。
MaybeLocal<Value> loadenv_ret = node::LoadEnvironment(
env.get(),
"const publicRequire ="
" require('module').createRequire(process.cwd() + '/');"
"globalThis.require = publicRequire;"
"require('vm').runInThisContext(process.argv[1]);");
if (loadenv_ret.IsEmpty()) // 出现了 JS 异常。
return 1;
{
// SealHandleScope 防止来自回调的句柄泄漏。
SealHandleScope seal(isolate);
bool more;
do {
uv_run(&loop, UV_RUN_DEFAULT);
// 后台线程上的 V8 任务可能最终会在前台调度新任务,从而使事件循环继续进行。
// 例如,
// WebAssembly.compile() 可能会这样做。
platform->DrainTasks(isolate);
// 如果有新任务,则继续。
more = uv_loop_alive(&loop);
if (more) continue;
//
node::EmitBeforeExit(env.get());
// 'beforeExit' 还可以调度新的新工作,
// 以使事件循环保持运行。
more = uv_loop_alive(&loop);
} while (more == true);
}
//
exit_code = node::EmitExit(env.get());
// node::Stop() 可用于显式停止事件循环并阻止进一步的 JavaScript 运行。
// 它可以从任何线程调用,
// 如果从另一个线程调用,则像 worker.terminate() 一样。
node::Stop(env.get());
}
// 取消向平台注册 Isolate 并添加监听器,
// 当平台完成清理它与 Isolate 关联的任何状态时,
// 则调用该监听器。
bool platform_finished = false;
platform->AddIsolateFinishedCallback(isolate, [](void* data) {
*static_cast<bool*>(data) = true;
}, &platform_finished);
platform->UnregisterIsolate(isolate);
isolate->Dispose();
// 等到平台清理完所有相关资源。
while (!platform_finished)
uv_run(&loop, UV_RUN_ONCE);
int err = uv_loop_close(&loop);
assert(err == 0);
return exit_code;
}