概览
此项目主函数入口在src\engine\engine.cpp中,主流程大概如下:
- 初始化 txiki.js 运行时(TJS_Initialize)
- 创建 JS 虚拟机实例(JS 上下文 + libuv 事件循环)
- 向 JS 全局对象注入 lvgljs 命名空间
- 初始化 LVGL 硬件抽象层(HAL)和窗口
- 启动 30ms 定时器,循环调用
lv_timer_handler() - 进入 txiki.js 事件循环,执行用户 JS 代码
详细流程
整个注册流程大概如下:
cpp
NativeRenderInit(ctx, ns) render_bootstrap.cpp 总入口,注册所有原生绑定
NativeComponentInit(ctx, ns) component.cpp 注册所有组件
NativeComponentXxxInit(ctx, ns) *_wrap.cpp 每个组件各自的注册函数
WRAPPED_JS_* 系列宏 component.hpp 通过宏自动生成通用 JS 绑定函数
registerComponent(config) react/components/config.ts JS 端注册 React 组件
以 View 为例:注册全流程如下:
第 1 层:C++ 总入口
cpp
engine.cpp
└─ NativeRenderInit(ctx, ns) // render_bootstrap.cpp
└─ NativeComponentInit(ctx, obj) // component.cpp
└─ NativeComponentViewInit(ctx, component_obj) // view_wrap.cpp
注册完成后,就可以在JS端利用以下路径进行访问:
globalThis['lvgljs'].NativeRender.NativeComponents.View
第 2 层:View 的 wrap 文件(view_wrap.cpp)
① 这里定义了一些里宏,用来生成所有组件通用方法
cpp
WRAPPED_JS_SETSTYLE(View, "View") // → NativeCompSetStyle
WRAPPED_JS_AddEventListener(View, "View") // → NativeCompAddEventListener
WRAPPED_APPEND_CHILD(View, "View") // → NativeCompAppendChild
WRAPPED_REMOVE_CHILD(View, "View") // → NativeCompRemoveChild
WRAPPED_INSERT_CHILD(View, "View") // → NativeCompInsertChildBefore
WRAPPED_JS_Align(View, "View") // → NativeCompSetAlign
STYLE_INFO(View, "View") // → style_funcs (width/height/left/top)
WRAPPED_JS_CLOSE_COMPONENT(View, "View") // → NativeCompCloseComponent
每个宏会自动展开为一个 C 函数,C端调用时,通过 JS_GetOpaque 取出 C++ 实例指针,然后调用对应方法。
② 定义 JS 方法列表
cpp
static const JSCFunctionListEntry ComponentProtoFuncs[] = {
TJS_CFUNC_DEF("nativeSetStyle", 0, NativeCompSetStyle),
TJS_CFUNC_DEF("addEventListener", 0, NativeCompAddEventListener),
TJS_CFUNC_DEF("appendChild", 0, NativeCompAppendChild),
TJS_CFUNC_DEF("removeChild", 0, NativeCompRemoveChild),
TJS_CFUNC_DEF("align", 0, NativeCompSetAlign),
TJS_CFUNC_DEF("close", 0, NativeCompCloseComponent),
JS_OBJECT_DEF("style", style_funcs, countof(style_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE),
};
③ 定义构造函数
cpp
static JSValue ViewConstructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) {
// 1. 从 JS 参数中取出 uid
jsUid = JS_GetPropertyStr(ctx, arg, "uid");
uid = JS_ToCString(ctx, jsUid);
// 2. 创建 JS 对象,绑定到 ViewClassID
obj = JS_NewObjectProtoClass(ctx, proto, ViewClassID);
// 3. 创建 C++ View 实例
s = (COMP_REF*)js_mallocz(ctx, sizeof(*s));
s->uid = uid;
s->comp = new View(uid, NULL); // ← 关键:创建 C++ 组件
// 4. 将 C++ 实例指针绑定到 JS 对象
JS_SetOpaque(obj, s);
return obj;
}
④ 定义析构函数
cpp
static void ViewFinalizer(JSRuntime *rt, JSValue val) {
COMP_REF *th = (COMP_REF *)JS_GetOpaque(val, ViewClassID);
delete static_cast<View*>(th->comp); // 释放 C++ 实例
js_free_rt(rt, th);
}
⑤ 注册函数(核心)
cpp
void NativeComponentViewInit(JSContext* ctx, JSValue ns) {
// 1. 分配唯一的 ClassID
JS_NewClassID(JS_GetRuntime(ctx), &ViewClassID);
// 2. 注册 JS 类(含 finalizer)
JS_NewClass(JS_GetRuntime(ctx), ViewClassID, &ViewClass);
// 3. 创建 prototype 并绑定方法列表
JSValue proto = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, proto, ComponentProtoFuncs, countof(ComponentProtoFuncs));
JS_SetClassProto(ctx, ViewClassID, proto);
// 4. 创建构造函数并关联 prototype
JSValue obj = JS_NewCFunction2(ctx, ViewConstructor, "View", 1, JS_CFUNC_constructor, 0);
JS_SetConstructor(ctx, obj, proto);
// 5. 将 "View" 挂到命名空间上
JS_DefinePropertyValueStr(ctx, ns, "View", obj, JS_PROP_C_W_E);
}
第 3 层:JS 端注册(React 侧)
bash
// src/render/react/components/view/index.ts
// 1. 获取原生类
const bridge = globalThis[Symbol.for('lvgljs')];
const NativeView = bridge.NativeRender.NativeComponents.View;
// 2. JS 端继承原生类
export class ViewComp extends NativeView {
// 封装 React 兼容的方法
}
// 3. 注册 React 组件
export const View = registerComponent<ViewProps, ViewComp>(new ViewConfig());
当在JS调用时,整体数据流如下:
JS: new NativeView({ uid: "1" })
→ ViewConstructor()
→ new View(uid, NULL) // C++ 对象创建
→ JS_SetOpaque(obj, ref) // 绑定到 JS 对象
JS: view.appendChild(child)
→ NativeCompAppendChild() // 注册的 C 函数
→ JS_GetOpaque(obj) → ref
→ static_cast<View*>(ref->comp)->appendChild(child)
JS 对象被 GC 回收
→ ViewFinalizer()
→ delete View* // C++ 对象释放
总结
如果要新增一个组件应该怎么办呢?
创建 xxx_wrap.cpp,模仿 view_wrap.cpp 的模式
定义宏展开 + 方法列表 + Constructor + Finalizer + Init 函数
在 component.cpp 的 NativeComponentInit() 中添加调用
在 JS 端创建 XxxComp extends NativeXxx 并调用 registerComponent()