React Native JSI 深度解析(第 4 篇):你的第一个 React Native JSI 函数
- 原文链接:heartit.tech/react-nativ…
- 原文作者:Rahul Garg
“简单性是可靠性的先决条件。” — Edsger W. Dijkstra, How Do We Tell Truths That Might Hurt?, 1975
导读: JSI 函数本质上是“伪装成 JavaScript 函数的 C\+\+ lambda”。没有序列化、没有 bridge、没有代码生成,只有一个可由运行时直接调用的 C\+\+ callable。本文会手把手带你从零写出一个:如何注册到运行时、读取参数、校验类型、处理错误,以及从 JavaScript 调用它。读完后,你会得到一个几乎零样板代码的可运行原生模块。
系列:React Native JSI Deep Dive(12 篇)
第 1 篇:React Native 架构——线程、Hermes 与事件循环 | 第 2 篇:React Native Bridge vs JSI——变化与原因 | 第 3 篇:面向 JavaScript 开发者的 C\+\+ | 第 4 篇:你的第一个 React Native JSI 函数(你在这里) | 第 5 篇:HostObjects——将 C\+\+ 类暴露给 JavaScript | 第 6 篇:内存所有权 | 第 7 篇:平台接线 | 第 8 篇:线程与异步 | 第 9 篇:音频管线 | 第 10 篇:存储引擎 | 第 11 篇:模块方案对比 | 第 12 篇:调试与故障定位
快速回顾
在第 2 篇里我们看到,JSI 用直接 C\+\+ 函数调用替代了 JSON bridge——没有序列化、没有异步队列。在第 3 篇里,我们补齐了 C\+\+ 词汇:引用(&)、指针(*)、RAII、智能指针,以及显式 capture 的 lambda。
现在把这些全部用起来。本文就是你写下第一行原生模块代码的地方。
在 JavaScript 运行时里安装原生函数
在 Web 浏览器里,你可以往全局作用域挂 JavaScript 函数(window.myFunc = ...),但你不能安装原生函数——即由 C\+\+ 实现、执行时不需要 JavaScript 引擎解释的函数。浏览器提供的原生 API 面(fetch、setTimeout、DOM)由浏览器厂商固定提供。
在 React Native 里你可以做到。JSI 允许你把 C\+\+ 函数直接安装进 JavaScript 运行时。从 JavaScript 视角看,它们和其他函数没有区别;从 C\+\+ 视角看,它们是接收 runtime 与参数的 lambda——原生执行,而非解释执行。
做这件事的核心 API 只有一个:jsi::Function::createFromHostFunction。(你也可以通过 HostObject 或 evaluateJavaScript 创建可调用函数,但 createFromHostFunction 是专门用于注册 C\+\+ 函数的标准 API。)
第 1 步:最简单的 JSI 函数
先从绝对最小可用版本开始:一个不接收参数、返回数字的函数。
cpp/install.cpp —— 种子版本
#include <jsi/jsi.h>
using namespace facebook;
void install(jsi::Runtime& rt) {
auto fn = jsi::Function::createFromHostFunction(
rt, // 1. runtime
jsi::PropNameID::forAscii(rt, "getFortyTwo"), // 2. 函数名(用于堆栈)
0, // 3. 期望参数个数
[](jsi::Runtime& rt, // 4. lambda
const jsi::Value& thisVal,
const jsi::Value* args,
size_t count) -> jsi::Value {
return jsi::Value(42);
}
);
rt.global().setProperty(rt, "getFortyTwo", std::move(fn));
}
App.js —— 调用
const n = getFortyTwo();
console.log(n); // 42
输出:
42
createFromHostFunction 里发生了四件事:
rt:运行时实例。每次 JSI 调用都要它;它就是通往 JavaScript 世界的句柄。PropNameID::forAscii(rt, "getFortyTwo"):函数名。它会显示在错误堆栈里,但不决定函数安装位置;安装位置由setProperty决定。0:期望参数个数。它只是信息(对应 JS 的.length),运行时不会强制校验。- lambda:真正执行的 C\+\+ 代码。JavaScript 调用函数时,它会接收 runtime、
this值、参数数组指针和参数计数。
最后一行 rt.global().setProperty(...) 会把函数挂到 JavaScript 全局对象上。调用之后,任意 JavaScript 代码都能执行 getFortyTwo()。
关键洞察: 传给
PropNameID的函数名和传给setProperty的属性名是互相独立的。你可以在堆栈里把函数命名为"internalMathOp",但把它安装成global.getFortyTwo。实际工程里建议保持一致,避免混淆。
第 2 步:读取参数
忽略参数的函数没太大用。下面改成两个数相加。
cpp/install.cpp —— 读取参数(⚠️ 先不做校验)
void install(jsi::Runtime& rt) {
auto add = jsi::Function::createFromHostFunction(
rt,
jsi::PropNameID::forAscii(rt, "nativeAdd"),
2, // 期望 2 个参数
[](jsi::Runtime& rt,
const jsi::Value& thisVal,
const jsi::Value* args,
size_t count) -> jsi::Value {
double a = args[0].asNumber(); // ← 第一个参数转 double
double b = args[1].asNumber(); // ← 第二个参数
return jsi::Value(a + b);
}
);
rt.global().setProperty(rt, "nativeAdd", std::move(add));
}
App.js
console.log(nativeAdd(3, 7)); // 10
console.log(nativeAdd(1.5, 2.5)); // 4
输出:
10
4
args 参数是一个 jsi::Value 数组的指针(和第 3 篇讲的一样,是 C 风格数组传递)。args[0] 是第一个参数,args[1] 是第二个。count 表示实际传入了多少个参数。
坑点: 这段代码是为了简化演示,故意没做校验——不要照这个模式上线。 若 JavaScript 只调用
nativeAdd(5),args[1]会越界读取参数数组。这在 C\+\+ 里是未定义行为,可能崩溃、破坏内存,或悄悄产出脏数据。第 3 步会用count校验修复。索引args前必须先检查count。
asNumber() 会把 jsi::Value 转成 C\+\+ double。那如果 JavaScript 传的是字符串呢?
想一想:
nativeAdd("hello", 7)会发生什么?args[0].asNumber()遇到字符串时,会返回NaN?抛异常?还是崩溃?
它会抛出 C\+\+ 异常;JSI 运行时会捕获并转成 JavaScript Error,所以 JS 侧可用 try/catch 捕获。应用不会直接崩,但这次调用会失败,并给出类似 “expected a number” 的通用错误。比静默返回垃圾值好,但仍应显式做参数校验,而不是依赖转换时抛错——既为了更好的报错信息,也为了安全性(见上面的“缺参数”问题)。
第 3 步:校验参数
生产级 JSI 函数必须校验输入。jsi::Value 类型提供了类型判断方法:isNumber()、isString()、isObject()、isUndefined()、isNull()、isBool()、isSymbol()、isBigInt()。
cpp/install.cpp —— 增加参数校验
void install(jsi::Runtime& rt) {
auto add = jsi::Function::createFromHostFunction(
rt,
jsi::PropNameID::forAscii(rt, "nativeAdd"),
2,
[](jsi::Runtime& rt,
const jsi::Value& thisVal,
const jsi::Value* args,
size_t count) -> jsi::Value {
// 校验参数数量
if (count < 2) { // ← NEW
throw jsi::JSError(rt, "nativeAdd requires 2 arguments");
}
// 校验参数类型
if (!args[0].isNumber() || !args[1].isNumber()) { // ← NEW
throw jsi::JSError(rt, "nativeAdd arguments must be numbers");
}
double a = args[0].asNumber();
double b = args[1].asNumber();
return jsi::Value(a + b);
}
);
rt.global().setProperty(rt, "nativeAdd", std::move(add));
}
App.js —— 错误处理
try {
nativeAdd("hello", 7);
} catch (e) {
console.log(e.message); // "nativeAdd arguments must be numbers"
}
try {
nativeAdd(5);
} catch (e) {
console.log(e.message); // "nativeAdd requires 2 arguments"
}
输出:
"nativeAdd arguments must be numbers"
"nativeAdd requires 2 arguments"
模式始终一样:
- 检查
count:JavaScript 是否传够参数? - 检查类型:参数类型是否符合预期?
- 抛出
jsi::JSError:校验失败时,转成可被 JavaScript 捕获的错误。
坑点: 一定要在
asNumber()、asString()等转换前先做校验。类型不匹配时,这些转换会抛 C\+\+ 异常(JSI 会转成 JS 错误),但报错信息很泛(例如 “Value is string, expected a number”)。你自己的错误信息(例如"nativeAdd arguments must be numbers")更利于排查。更重要的是,args索引前必须先校验count;i >= count时访问args[i]属于未定义行为,任何异常处理都兜不住。
第 4 步:错误处理(jsi::JSError)
jsi::JSError 是 C\+\+ 异常和 JavaScript 错误之间的桥梁。在 host function 内抛出 jsi::JSError 后,它会以普通 Error 对象形式回到 JavaScript,可被 try/catch 捕获。
JSI 运行时确实会捕获 host function 抛出的 std::exception 子类,并转成 JavaScript 错误(见 jsi.h 文档:如果抛出了 C++ 异常,系统会创建一个 JS Error 并将其抛到 JS;如果该 C++ 异常继承自 std::exception,则 Error 的 message 就是 what() 的返回值。原文:“If a C\+\+ exception is thrown, a JS Error will be created and thrown into JS; if the C\+\+ exception extends std::exception, the Error's message will be whatever what() returns”)。但不继承 std::exception 的异常,或根本不会抛异常的未定义行为(比如数组越界),仍会导致应用崩溃。只依赖运行时兜底并不稳健——报错泛化,且对非异常型 UB 无效。
稳健做法是:在 native 逻辑外包一层 try/catch,掌控报错并兜住所有异常。
cpp/install.cpp —— 安全错误边界
[](jsi::Runtime& rt,
const jsi::Value& thisVal,
const jsi::Value* args,
size_t count) -> jsi::Value {
try {
// 你的 native 逻辑
auto result = someCppFunction(args[0].asNumber());
return jsi::Value(result);
} catch (const jsi::JSError&) {
throw; // 已经是 JS 错误,直接透传
} catch (const std::exception& e) {
throw jsi::JSError(rt, std::string("Native error: ") + e.what());
} catch (...) {
throw jsi::JSError(rt, "Unknown native error");
}
}
这三级 catch 的意义:
jsi::JSError原样透传(它本来就是 JS 错误)。- 标准 C\+\+ 异常(
std::runtime_error、std::invalid_argument等)用更清晰的消息包装后抛出。 - 未知异常有兜底,不让应用直接崩掉。
关键洞察: 每个 JSI host function 都是两个世界的边界。JSI 能自动处理
std::exception子类,但未定义行为(悬空指针、越界访问)会绕过所有异常处理并直接崩溃。try/catch包装提供的是“纵深防御”:更清晰的报错、对非标准异常的兜底、以及对 JavaScript 可见错误的明确控制。你可以把它看作 native 世界的 React error boundary。
jsi::Value 类型系统
在构建更复杂模块前,先搞清你会接触的类型。jsi::Value 是 tagged union——单个类型可承载任意 JavaScript 值。
读取值(JS → C\+\+)
| JavaScript 类型 | 类型检查 | 转换方法 | C\+\+ 类型 |
|---|---|---|---|
number | val.isNumber() | val.asNumber() | double |
string | val.isString() | val.asString(rt) | jsi::String |
boolean | val.isBool() | val.getBool() | bool |
object | val.isObject() | val.asObject(rt) | jsi::Object |
null | val.isNull() | — | — |
undefined | val.isUndefined() | — | — |
图 1:jsi::Value 的类型检查与转换。转换前务必先检查类型。
注意这个不对称性:asNumber() 不需要 rt,而 asString(rt) 与 asObject(rt) 需要。原因是 number/boolean 是纯 C\+\+ 值(double 和 bool),而 string/object 由 JS 引擎管理,访问时需要 runtime 句柄。
要把 jsi::String 变成 std::string,调用 .utf8(rt):
读取字符串参数:
jsi::String jsStr = args[0].asString(rt); // jsi::String(引擎管理)
std::string cppStr = jsStr.utf8(rt); // std::string(C\\+\\+ 持有拷贝)
创建值(C\+\+ → JS)
| C\+\+ 值 | JSI 构造方式 | JavaScript 结果 |
|---|---|---|
42 或 3.14 | jsi::Value(42) | number |
true / false | jsi::Value(true) | boolean |
"hello" | jsi::String::createFromUtf8(rt, "hello") | string |
| — | jsi::Value::null() | null |
| — | jsi::Value::undefined() | undefined |
| — | jsi::Object(rt) | {}(空对象) |
图 2:从 C\+\+ 构造 JavaScript 值。数字和布尔可直接包裹,字符串和对象需要 runtime。
返回不同类型示例:
// 返回数字
return jsi::Value(42);
// 返回字符串
return jsi::String::createFromUtf8(rt, "hello from C\\+\\+");
// 返回带属性的对象
auto obj = jsi::Object(rt);
obj.setProperty(rt, "name", jsi::String::createFromUtf8(rt, "JSI"));
obj.setProperty(rt, "version", jsi::Value(4));
return obj; // JS 收到:{ name: "JSI", version: 4 }
组合起来:一个 Math 模块
来做个真实一点的例子:一个小型数学模块,包含多个函数,并把它们作为同一个对象的属性安装,而不是污染全局作用域。
cpp/MathModule.cpp —— 完整模块
#include <jsi/jsi.h>
#include <cmath>
#include <string>
using namespace facebook;
void installMathModule(jsi::Runtime& rt) {
// Helper:校验下标 i 的参数是否为 number
auto requireNumber = [](jsi::Runtime& rt,
const jsi::Value* args,
size_t count,
size_t index,
const char* fnName) {
if (index >= count) {
throw jsi::JSError(rt,
std::string(fnName) + ": missing argument at index "
+ std::to_string(index));
}
if (!args[index].isNumber()) {
throw jsi::JSError(rt,
std::string(fnName) + ": argument " + std::to_string(index)
+ " must be a number");
}
};
// --- add(a, b) ---
auto add = jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forAscii(rt, "add"), 2,
[requireNumber](jsi::Runtime& rt, const jsi::Value&,
const jsi::Value* args, size_t count) -> jsi::Value {
requireNumber(rt, args, count, 0, "add");
requireNumber(rt, args, count, 1, "add");
return jsi::Value(args[0].asNumber() + args[1].asNumber());
}
);
// --- multiply(a, b) ---
auto multiply = jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forAscii(rt, "multiply"), 2,
[requireNumber](jsi::Runtime& rt, const jsi::Value&,
const jsi::Value* args, size_t count) -> jsi::Value {
requireNumber(rt, args, count, 0, "multiply");
requireNumber(rt, args, count, 1, "multiply");
return jsi::Value(args[0].asNumber() * args[1].asNumber());
}
);
// --- sqrt(x) ---
auto sqrt = jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forAscii(rt, "sqrt"), 1,
[requireNumber](jsi::Runtime& rt, const jsi::Value&,
const jsi::Value* args, size_t count) -> jsi::Value {
requireNumber(rt, args, count, 0, "sqrt");
double x = args[0].asNumber();
if (x < 0) {
throw jsi::JSError(rt, "sqrt: argument must be non-negative");
}
return jsi::Value(std::sqrt(x));
}
);
// --- describe() — 返回对象 ---
auto describe = jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forAscii(rt, "describe"), 0,
[](jsi::Runtime& rt, const jsi::Value&,
const jsi::Value* args, size_t count) -> jsi::Value {
auto obj = jsi::Object(rt);
obj.setProperty(rt, "name",
jsi::String::createFromUtf8(rt, "NativeMath"));
obj.setProperty(rt, "version", jsi::Value(1));
obj.setProperty(rt, "engine",
jsi::String::createFromUtf8(rt, "JSI"));
return obj;
}
);
// 把函数安装到同一个对象上
auto mathModule = jsi::Object(rt);
mathModule.setProperty(rt, "add", std::move(add));
mathModule.setProperty(rt, "multiply", std::move(multiply));
mathModule.setProperty(rt, "sqrt", std::move(sqrt));
mathModule.setProperty(rt, "describe", std::move(describe));
rt.global().setProperty(rt, "NativeMath", std::move(mathModule));
}
App.js —— 使用模块
console.log(NativeMath.add(3, 7)); // 10
console.log(NativeMath.multiply(6, 7)); // 42
console.log(NativeMath.sqrt(144)); // 12
console.log(NativeMath.describe()); // { name: "NativeMath", version: 1, engine: "JSI" }
try {
NativeMath.sqrt(-1);
} catch (e) {
console.log(e.message); // "sqrt: argument must be non-negative"
}
try {
NativeMath.add("hello", 7);
} catch (e) {
console.log(e.message); // "add: argument 0 must be a number"
}
输出:
10
42
12
{ "name": "NativeMath", "version": 1, "engine": "JSI" }
"sqrt: argument must be non-negative"
"add: argument 0 must be a number"
本文和第 3 篇的所有核心概念都在这个例子里了:
| 模式 | 实际发生了什么 |
|---|---|
jsi::Runtime& rt | 引用:借用 runtime |
const jsi::Value* args | 指针:C 风格参数数组 |
requireNumber lambda | 通过值捕获进入每个 host function |
jsi::JSError | C\+\+ 异常 → JavaScript Error |
std::move(add) | move 语义:把所有权转移到模块对象 |
jsi::Object(rt) | 栈上 JSI 对象:句柄由 RAII 管理 |
在 mathModule 上 setProperty | 挂载到对象而非全局:命名空间更干净 |
全局安装 vs 对象安装
函数安装位置有两种选择:
全局安装:函数在所有地方可见。
全局方式(裸函数可直接调用):
rt.global().setProperty(rt, "nativeAdd", std::move(fn));
// JS: nativeAdd(3, 7)
对象安装:函数放在命名空间下。
对象方式(挂在模块名下):
auto module = jsi::Object(rt);
module.setProperty(rt, "add", std::move(fn));
rt.global().setProperty(rt, "NativeMath", std::move(module));
// JS: NativeMath.add(3, 7)
优先使用对象安装。它能避免污染全局命名空间,把相关函数自然分组,也更符合 JavaScript 模块习惯。只有非常简单的单函数模块才值得考虑全局安装。
取舍:这种方式做不到什么
纯 JSI 函数(即本文展示方式)很强,但也有边界:
| 能力 | JSI Host Functions | 你需要的替代方案 |
|---|---|---|
| 同步调用 | 可以(运行在 JS 线程) | — |
| 返回值 | 可以(任意 jsi::Value) | — |
| 有状态模块 | 可通过 shared_ptr capture 实现,但冗长(无属性、无 this) | HostObjects(第 5 篇):以更清晰接口暴露 C\+\+ 类 |
| 异步操作 | 不行(必须同步返回) | CallInvoker(第 8 篇):后台线程 + Promise |
| 平台 API 接入 | 不行(仅纯 C\+\+) | 平台接线(第 7 篇):Obj-C\+\+/JNI 桥接 |
| 从 JS 获得类型安全 | 不行(手写校验) | TurboModules(第 11 篇):由 Flow/TS 规范代码生成 |
图 3:JSI host function 的能力与边界。第 5-11 篇会逐一补上这些能力。
最大的人体工学限制是:没有干净的有状态接口。你可以在 lambda 里 capture shared_ptr 来共享状态(第 3 篇 key-value store 示例就是这样),但很快会变得啰嗦——没有属性访问、没有 this,也无法把一组方法组织成 JavaScript 可检查的对象。当你需要数据库连接、缓存或流式音频会话时,你要的是一个可被 JavaScript 当成一等对象交互的 C\+\+ 对象。这正是 HostObjects 的作用,也是第 5 篇要讲的内容。
关键结论
createFromHostFunction是核心 API。 它接收 runtime、函数名(用于堆栈)、参数个数和一个 C\+\+ lambda。lambda 就是 JavaScript 实际调用的逻辑。这就是全部机制。- 参数校验必须做。 访问
args[index]前先检查count;调用asNumber()/asString(rt)/asObject(rt)前先检查isNumber()/isString()/isObject()。永远不要假设 JavaScript 一定按你想的传参。 - 原生错误用
jsi::JSError包装。 JSI 会自动捕获std::exception子类,但报错通常太泛。加try/catch能得到更清晰报错,也可兜住非标准异常。jsi::JSError直接透传即可。未定义行为(越界、悬空指针)会绕过异常系统,所以仍需先做输入校验。 - 优先安装到对象,不要挂全局。 把相关函数放在命名空间对象下(
NativeMath.add)而非污染全局(nativeAdd),更干净、更符合 JS 习惯。 - host function 是同步的。 它们运行在 JS 线程并立即返回。若操作耗时超过 16ms 帧预算中的可用份额,就会明显阻塞事件循环。异步模式(后台线程 + Promise)在第 8 篇展开。
回看崩溃栈
再看一次第 1 篇里的崩溃栈。在 mqt_js 线程上,最后一帧是:
facebook::jsi::Runtime::PointerValue::invalidate()
这就是 JSI 层。PointerValue 是 JSI 用来在 C\+\+ 侧引用 JavaScript 对象的内部类型——每个 jsi::Object、jsi::Function、jsi::String 都包装了一个 PointerValue。当 invalidate() 运行时,运行时正在释放一个 JSI 引用。这正是你在第 3 篇旁注中看到的 HostObject 析构链触发机制。JSI 运行时判定该对象不再被 JavaScript 可达,于是开始清理 native 侧。本文你学会创建的 host function 与 HostObject,正是那段 trace 中被销毁对象的类型。
系统目前长这样:
JS Thread UI Thread Native Thread
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Hermes │ │ Platform │ │ C\\+\\+ code │
│ │ │ │ │ │
│ nativeAdd()──┼───┼──── JSI ─────┼──▶│ C\\+\\+ lambda │ ← NEW
│ │ │ │ │ stack / heap │
│ │ │ │ │ shared_ptr │
└───────────────┘ └───────────────┘ └───────────────┘
常见问题(FAQ)
在 React Native 里如何创建 JSI 函数?
使用 jsi::Function::createFromHostFunction(),把一个 C\+\+ lambda 注册成 JavaScript 函数。该 lambda 会接收 runtime、以 jsi::Value 表示的参数,并返回 jsi::Value。
JSI 函数可以是同步的吗?
可以。JSI 函数在 JS 线程上同步执行,立即返回结果,不需要 Promise 或回调。通常只适合在约 ~1ms 内完成的操作。
JSI 函数里抛异常会怎样?
继承 std::exception 的 C\+\+ 异常会被 JSI 运行时捕获,并转换成 JavaScript Error 对象,可在 JS 侧通过 try/catch 捕获。
下一篇
你现在已经能把 C\+\+ 函数安装到 JavaScript 里了。但这些函数是无状态的——每次调用彼此独立。如果你想暴露的是一个 C\+\+ 对象呢?比如能记住状态的数据库连接、可读写的缓存、可启动/暂停/停止的音频会话。
在第 5 篇:HostObjects——将 C\+\+ 类暴露给 JavaScript中,你会学到如何把 C\+\+ 类以一等对象形式暴露给 JavaScript(有属性也有方法)。HostObjects 是 JSI 从“有趣机制”走向“完整原生模块框架”的关键节点。你将构建一个 key-value store,让 storage.get('key') 同步调用 C\+\+——无需 await、无需 bridge、无需序列化。
第 4 篇给你函数,第 5 篇给你对象。
参考资料与延伸阅读
- JSI Header — jsi.h(完整 API 面,facebook/react-native)
- React Native — The New Architecture(官方文档)
- react-native-mmkv — 生产级 JSI 模块(源码参考)
- react-native-vision-camera — 生产级 JSI + HostObject 模块(源码参考)
- cppreference — Lambda Expressions
- cppreference — std::exception
快速参考
createFromHostFunction
auto fn = jsi::Function::createFromHostFunction(
rt, // jsi::Runtime&
jsi::PropNameID::forAscii(rt, "name"), // 调试名(用于堆栈)
2, // 参数个数(JS .length,不强制)
[](jsi::Runtime& rt, const jsi::Value& thisVal,
const jsi::Value* args, size_t count) -> jsi::Value {
// your code here
return jsi::Value::undefined();
}
);
读取参数
| 类型检查 | 读取值 | C\+\+ 类型 |
|---|---|---|
args[i].isNumber() | args[i].asNumber() | double |
args[i].isString() | args[i].asString(rt).utf8(rt) | std::string |
args[i].isBool() | args[i].getBool() | bool |
args[i].isObject() | args[i].asObject(rt) | jsi::Object |
args[i].isUndefined() | — | — |
args[i].isNull() | — | — |
创建返回值
return jsi::Value(42); // number
return jsi::Value(true); // boolean
return jsi::String::createFromUtf8(rt, "hello");// string
return jsi::Value::undefined(); // undefined
return jsi::Value::null(); // null
挂载到全局
rt.global().setProperty(rt, "fnName", std::move(fn));
向 JavaScript 抛错
throw jsi::JSError(rt, "error message");
系列:React Native JSI Deep Dive(12 篇)
第 1 篇:React Native 架构——线程、Hermes 与事件循环 | 第 2 篇:React Native Bridge vs JSI——变化与原因 | 第 3 篇:面向 JavaScript 开发者的 C\+\+ | 第 4 篇:你的第一个 React Native JSI 函数(你在这里) | 第 5 篇:HostObjects——将 C\+\+ 类暴露给 JavaScript | 第 6 篇:内存所有权 | 第 7 篇:平台接线 | 第 8 篇:线程与异步 | 第 9 篇:音频管线 | 第 10 篇:存储引擎 | 第 11 篇:模块方案对比 | 第 12 篇:调试与故障定位
术语表(本篇命中)
| 英文术语 | 中文译法 | 说明 |
|---|---|---|
| JSI | JSI | React Native 的 JavaScript Interface |
| Host Function | Host Function | 由 C\+\+ 提供、可被 JS 调用的函数 |
| HostObject | HostObject | 向 JS 暴露 C\+\+ 对象的机制 |
| runtime | 运行时 | jsi::Runtime 实例 |
| bridge | bridge | RN 旧架构的跨端通信层 |
| serialization | 序列化 | 跨边界数据编码过程 |
| lambda | lambda | C\+\+ 匿名函数对象 |
| RAII | RAII | 资源获取即初始化 |
| smart pointer | 智能指针 | 如 shared_ptr |
| undefined behavior | 未定义行为 | C\+\+ 标准未规定的行为结果 |
| event loop | 事件循环 | JS 线程任务调度循环 |
| frame budget | 帧预算 | 每帧可用计算时间窗口 |
| TurboModules | TurboModules | RN 新架构模块方案 |
| CallInvoker | CallInvoker | 跨线程调度调用工具 |