Node.js 不仅可以单独运行,还可以以库的方式被使用,本文介绍下如何把 Node.js 嵌入到自己项目中。
首先第一步下载 Node.js 源码,然后根据 Node.js 的文档进行编译安装。这样我们就可以拿到 Node.js 提供的头文件和库文件了。接下来根据官方的 demo 写一个测试程序。

#include "node.h"
#include "uv.h"
#include <assert.h>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<std::string>& args,const std::vector<std::string>& exec_args);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;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> platform =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<std::string>& args,const std::vector<std::string>& exec_args) {int exit_code = 0;std::vector<std::string> errors;std::unique_ptr<CommonEnvironmentSetup> setup =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<Value> loadenv_ret = node::LoadEnvironment(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。下面来编译一下。

g++ Node.cc src/node_code_cache_stub.cc src/node_snapshot_stub.cc -I/node/v17.9.0/include/node -std=c++14 -L /node/out/Release -l v8_base_without_compiler -l v8_compiler -l v8_init -l v8_initializers -l v8_libbase -l v8_libplatform -l v8_snapshot -l brotli -l cares -l gtest -l gtest_main -l histogram -l icudata -l icui18n -l icutools -l icuucx -l llhttp -l nghttp2 -l nghttp3 -l ngtcp2  -l openssl -l torque_base -l uv -l uvwasi -l v8_zlib -l zlib -l pthread -l node

因为 code cache 和 快照函数的符号找不到的问题,这里先曲线救国一下,从 Node.js 源码里引入这两个文件,后续再去研究具体方案。编译完后就拿到了一个 a.out 文件,执行该文件就可以看到输出 hello world。cool,我们已经实现了把 Node.js 嵌入到我们的项目。下面具体来看一下涉及到的一些逻辑。从 LoadEnvironment 看起。

MaybeLocal<Value> LoadEnvironment(Environment* env,const char* main_script_source_utf8) {Isolate* isolate = env->isolate();return LoadEnvironment(env,[&](const StartExecutionCallbackInfo& info) -> MaybeLocal<Value> {// 一会分析});
}

LoadEnvironment 进一步调了另一个 LoadEnvironment。

MaybeLocal<Value> LoadEnvironment(Environment* env,StartExecutionCallback cb) {env->InitializeLibuv();env->InitializeDiagnostics();return StartExecution(env, cb);
}

LoadEnvironment 进行了一些初始化,接着调 StartExecution。

MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {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 模块加载器。接着看回调函数的逻辑。

    [&](const StartExecutionCallbackInfo& info) -> MaybeLocal<Value> {// main_script_source_utf8 为我们传入到字符串代码Local<String> str =String::NewFromUtf8(isolate,main_script_source_utf8).ToLocalChecked();auto main_utf16 = std::make_unique<String::Value>(isolate, str);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<Local<String>> params = {env->process_string(),env->require_string()};std::vector<Local<Value>> args = {env->process_object(),env->native_module_require()};// 执行我们的代码return ExecuteBootstrapper(env, name.c_str(), &params, &args);}

回调函数通过 ExecuteBootstrapper 执行我们传入的代码。

MaybeLocal<Value> ExecuteBootstrapper(Environment* env,const char* id,std::vector<Local<String>>* parameters,std::vector<Local<Value>>* arguments) {EscapableHandleScope scope(env->isolate());// 从原生 JS 模块代码中找到我们的代码MaybeLocal<Function> maybe_fn =NativeModuleEnv::LookupAndCompile(env->context(), id, parameters, env);Local<Function> fn;if (!maybe_fn.ToLocal(&fn)) {return MaybeLocal<Value>();}// 执行我们的代码MaybeLocal<Value> result = fn->Call(env->context(),Undefined(env->isolate()),arguments->size(),arguments->data());
}

再回过头来看看我们的代码。

const publicRequire = require('module').createRequire(process.cwd() + '/');
globalThis.require = publicRequire;
publicRequire('./test');

require 函数是原生 JS 模块加载器,可以用来加载 Node.js 原生 JS 模块。通过 module 模块可以创建一个用户 JS 模块加载器。通过用户 JS 模块加载器,我们就可以把我们的代码串起来了。

如何把 Node.js 嵌入自己的项目中相关推荐

  1. Node.js期中爬虫实验项目

    Node.js期中爬虫实验项目 期中作业要求 基础概念引入 前期准备工作 安装node.js 安装数据库 安装Navicat Premium 15 正则表达式学习网站 课堂示例演示 示例一(显示在终端 ...

  2. helmet是一个保护Node.JS应用的安全项目

    helmet是一个保护Node.JS应用的安全项目  14-11-15  banq #NodeJS      #express      #安全      Helmet是一系列帮助增强Node.JS之 ...

  3. 体验使用node.js创建vue+Element-UI项目

    1.首先去node.js官网下载系统对应的node.js版本. 2.安装淘宝镜像 npm install -g cnpm --registry=https://registry.npm.taobao. ...

  4. Node.js结合Express框架项目搭建

    一.简述 本次项目是用node.js写后台接口,前端使用vue.js分离的方法实现一个在线点咖啡的项目.本节教程只是一个简单的入门,关于实际用法后期继续更新. 二.搭建应用 1. 通过应用生成器工具 ...

  5. 使用node.js 脚手架搭建Vue项目

    1.安装node.js https://nodejs.org/zh-cn/ 下载安装node.js在命令行测试 node -v 输出版本号说明安装成功 2.使用npm更新安装cpnm npm inst ...

  6. node.js+uni计算机毕设项目计算机配件价格查询微信小程序(程序+小程序+LW)

    该项目含有源码.文档.程序.数据库.配套开发软件.软件安装教程.欢迎交流 项目运行 环境配置: Node.js+ Vscode + Mysql5.7 + HBuilderX+Navicat11+Vue ...

  7. node.js+uni计算机毕设项目基于微信小程序在线抽签系统(程序+小程序+LW)

    该项目含有源码.文档.程序.数据库.配套开发软件.软件安装教程.欢迎交流 项目运行 环境配置: Node.js+ Vscode + Mysql5.7 + HBuilderX+Navicat11+Vue ...

  8. Vue2+Node.js前后端分离项目部署到云服务器

    本文参考教程: NodeJS项目部署到阿里云ECS服务器全程详解 - 知乎本文详细介绍如何部署NodeJS项目到阿里云ECS上,以及本人在部署过程中所遇到的问题.坑点和解决办法,可以说是全网最全最详细 ...

  9. node.js+uni计算机毕设项目基于微信小程序的车位共享系统LWPPT(程序+小程序+LW)

    该项目含有源码.文档.程序.数据库.配套开发软件.软件安装教程.欢迎交流 项目运行 环境配置: Node.js+ Vscode + Mysql5.7 + HBuilderX+Navicat11+Vue ...

最新文章

  1. 气泡图在开源监控工具中的应用效果
  2. 全球与中国人脸语音生物识别市场”十四“五规模状况与前景趋势分析报告2021-2027年版
  3. 若依微服务版新建业务模块后提示找不到mapper的解决方法
  4. 深度学习-Tensorflow2.2-深度学习基础和tf.keras{1}-tf.keras函数式API-08
  5. xshell连接虚拟机里的linux系统
  6. 使用 ABAP 开发的一个基于 Web Socket 的小工具,能提高程序员日常工作效率
  7. Netweaver和SAP云平台的quota管理
  8. HDU - 4497 GCD and LCM
  9. 第一阶段:前端开发_Mysql——表与表之间的关系
  10. [Leetcode][第32题][JAVA][最长有效括号][动态规划][栈][正向逆向结合]
  11. c常用算法程序集_10万赞的AI算法集:含python,java,C,C++多种语言
  12. C++---异常处理
  13. Predictably Irractional - 期望的效应
  14. python通过类名创建对象_如何在Python中为自动创建的类对象分配名称
  15. cd40系列芯片_CD40系列芯片大全
  16. 东北四省赛E-Minimum Spanning Tree-贡献求和
  17. 百度地图自定义图标动画
  18. 色即是空 空即是色_huadingjin_新浪博客
  19. 直击文印痛点 中小企业需要这样一台复合机
  20. printf 打印结构体成员函数出错原因分析

热门文章

  1. 汇顶科技外包java_【汇顶科技有限公司Java面试】2020春季校招后端Java-看准网
  2. 招人:没钱靠谱的我们想和你一起探索人生的可能性
  3. 分析Nginx是如何实现反向代理和负载均衡
  4. android app根目录下cache,Android 缓存目录 Context.getExternalFilesDir()和Context.getExternalCacheDir()方法...
  5. python入门基础002
  6. mysql中depart_mysql实训
  7. 优质开源:共享图书小程序3.0 全新UI 免费下载
  8. Prior Posterior和Likelihood的理解与几种表达方式
  9. RPG 游戏数值系统—1
  10. 云和恩墨大讲堂电子期刊第四期