Node.js 不仅可以单独运行,何把还可以以库的嵌入方式被使用,本文介绍下如何把 Node.js 嵌入到自己项目中。自己首先第一步下载 Node.js 源码,目中然后根据 Node.js 的何把文档进行编译安装。这样我们就可以拿到 Node.js 提供的嵌入头文件和库文件了。接下来根据官方的自己 demo 写一个测试程序。 #include "node.h" #include "uv.h" #include using node::CommonEnvironmentSetup; using node::Environment; using node::MultiIsolatePlatform; using v8::Context; using v8::HandleScope; using v8::Isolate; using v8::Locker; using v8::MaybeLocal; using v8::V8; using v8::Value; static int RunNodeInstance(MultiIsolatePlatform* platform,目中 const std::vector const std::vector int main(int argc, char** argv) { argv = uv_setup_args(argc, argv); std::vector std::vector std::vector 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; } std::unique_ptr MultiIsolatePlatform::Create(4); V8::InitializePlatform(platform.get()); V8::Initialize(); int ret = RunNodeInstance(platform.get(), args, exec_args); V8::Dispose(); // V8::DisposePlatform(); return ret; } int RunNodeInstance(MultiIsolatePlatform* platform, const std::vector const std::vector int exit_code = 0; std::vector std::unique_ptr CommonEnvironmentSetup::Create(platform, &errors, args, exec_args); if (!setup) { for (const std::string& err : errors) fprintf(stderr, "%s: %s\n", args[0].c_str(), err.c_str()); return 1; } Isolate* isolate = setup->isolate(); Environment* env = setup->env(); { Locker locker(isolate); Isolate::Scope isolate_scope(isolate); HandleScope handle_scope(isolate); Context::Scope context_scope(setup->context()); MaybeLocal env, "const publicRequire =" " require(module).createRequire(process.cwd() + /);" "globalThis.require = publicRequire;" "publicRequire(./test)"); if (loadenv_ret.IsEmpty()) // There has been a JS exception. return 1; exit_code = node::SpinEventLoop(env).FromMaybe(1); node::Stop(env); } return exit_code; 大部分都是默认的流程,我们可以先不用关注,何把重点是嵌入 LoadEnvironment 函数的逻辑。LoadEnvironment 最后会执行我们传入的自己字符串代码。这段代码中,目中前面是服务器租用何把 Node.js 提供的 demo,后面一句是嵌入我加的,test.js 里简单输出 hello world。自己下面来编译一下。 因为 code cache 和 快照函数的符号找不到的问题,这里先曲线救国一下,从 Node.js 源码里引入这两个文件,后续再去研究具体方案。编译完后就拿到了一个 a.out 文件,执行该文件就可以看到输出 hello world。cool,我们已经实现了把 Node.js 嵌入到我们的项目。下面具体来看一下涉及到的一些逻辑。从 LoadEnvironment 看起。站群服务器 MaybeLocal Environment* env, const char* main_script_source_utf8) { Isolate* isolate = env->isolate(); return LoadEnvironment( env, [&](const StartExecutionCallbackInfo& info) -> MaybeLocal // 一会分析 }); LoadEnvironment 进一步调了另一个 LoadEnvironment。 MaybeLocal Environment* env, StartExecutionCallback cb) { env->InitializeLibuv(); env->InitializeDiagnostics(); return StartExecution(env, cb); LoadEnvironment 进行了一些初始化,接着调 StartExecution。 MaybeLocal InternalCallbackScope callback_scope( env, Object::New(env->isolate()), { 1, 0 }, InternalCallbackScope::kSkipAsyncHooks); if (cb != nullptr) { EscapableHandleScope scope(env->isolate()); if (StartExecution(env, "internal/bootstrap/environment").IsEmpty()) return { }; StartExecutionCallbackInfo info = { env->process_object(), env->native_module_require(), }; return scope.EscapeMaybe(cb(info)); } StartExecution 最终执行了 第一个 LoadEnvironment 传入到回调,并传入了 process 和原生 JS 模块加载器。接着看回调函数的逻辑。 std::string name = "embedder_main_" + std::to_string(env->thread_id()); // 插入原生 JS 模块代码中 native_module::NativeModuleEnv::Add( name.c_str(), UnionBytes(**main_utf16, main_utf16->length())); env->set_main_utf16(std::move(main_utf16)); std::vector env->process_string(), env->require_string()}; std::vector env->process_object(), env->native_module_require()}; // 执行我们的代码 return ExecuteBootstrapper(env, name.c_str(), ¶ms, &args); 回调函数通过 ExecuteBootstrapper 执行我们传入的代码。 MaybeLocal const char* id, std::vector std::vector EscapableHandleScope scope(env->isolate()); // 从原生 JS 模块代码中找到我们的代码 MaybeLocal NativeModuleEnv::LookupAndCompile(env->context(), id, parameters, env); Local if (!maybe_fn.ToLocal(&fn)) { return MaybeLocal } // 执行我们的代码 MaybeLocal Undefined(env->isolate()), arguments->size(), arguments->data()); 再回过头来看看我们的代码。 const publicRequire = require(module).createRequire(process.cwd() + /); globalThis.require = publicRequire; require 函数是原生 JS 模块加载器,可以用来加载 Node.js 原生 JS 模块。通过 module 模块可以创建一个用户 JS 模块加载器。通过用户 JS 模块加载器,我们就可以把我们的代码串起来了。亿华云计算