开发指南:连接 React 与浏览器 Runtime)
前端3D渲染【免费下载链接】react-360Create amazing 360 and VR content using React项目地址https://gitcode.com/gh_mirrors/re/react-360点击查看免费下载React 360 的应用逻辑默认运行在 Web Worker 中而 Native Modules 正是 React 代码与主浏览器窗口Runtime之间的通信桥梁用于调用仅在浏览器环境中可用的能力例如读写本地存储、查询手柄/控制器信息、操作渲染环境等。本文以docs/native-modules.md为核心结合仓库内 NativeModules 示例工程 与 Module 基类源码系统讲解从模块创建、注册到四种通信模式常量、简单方法、回调、Promise的完整实战方案。一、Native Modules 是什么解决什么问题React 360 的 React 应用代码运行在独立的 Web Worker 线程中这意味着它无法直接访问document、window、navigator等浏览器全局对象。Native Modules 提供了让 React 代码反向调用你的 Runtime的能力把只能在主浏览器环境完成的功能暴露给 React 侧使用典型场景包括在多次页面加载之间持久化保存数值获取已连接控制器的信息操作渲染出来的环境Environment读写浏览器窗口标题、弹窗、电池电量等浏览器 API。从仓库源码看Native Modules 正是 React 360 自身内置模块体系的同构机制ReactNativeContext 初始化时 会注册UIManager、AsyncLocalStorage、ControllerInfo、Networking、AudioModule、VideoModule等一批核心模块ReactInstance 构造时 又额外注册EnvironmentModule、AudioModule、VideoModule随后把options.nativeModules中的自定义模块拼接进来。因此你自定义的模块与框架内置模块走的是同一条桥接通道行为完全一致。二、创建并注册自定义 Native ModuleNative Modules 创建在 Runtime 代码通常是client.js中必须在 React 应用启动之前注册完毕之后 React 侧才能使用。2.1 基础创建流程要创建一个 Native Module只需继承Module基类并在初始化时注册import {Module} from react-360-web; class MyModule extends Module { constructor() { super(MyModule); // Makes this module available at NativeModules.MyModule } // This method will be exposed to the React app doSomething() { } } const r360 new ReactInstance( MyApp.bundle?platformvr, document.getElementById(container), { // Register custom modules at init time nativeModules: [ new MyModule(), ] } );super(MyModule)中传入的字符串决定了模块在 React 侧的挂载名称上例中模块会以NativeModules.MyModule暴露给 React 应用。2.2 两种注册形式nativeModules数组既可以接收 Native Module 实例也可以接收一个返回 Native Module 实例的函数。函数形式在需要于构造时注入参数时特别有用例如把 ReactNativeContext 传入模块后面的回调与 Promise 示例都会用到。源码证实了这两种形式在 Runtime.js 中框架遍历options.nativeModules时会先判断元素类型——若是函数则调用m(context)并把返回值注册否则直接注册实例而在 ReactInstance.js 中自定义模块被合并到内置模块数组之后一并交给 Runtime 处理。// 函数形式可以在构造时拿到 ctxReactNativeContext nativeModules: [ ctx new MyModule(ctx), ]三、暴露常量给 React 侧最简单的用法是把主浏览器窗口中初始化时已知、之后不变的值传给 React例如浏览器语言或 User Agent 字符串。规则是在构造函数中挂到 Native Module 实例上的、不以_开头的属性都会作为常量暴露给 React。// in client.js class MyModule extends Module { constructor() { super(MyModule); someConstant 42; } }// in index.js import {NativeModules} from react-360; const {MyModule} NativeModules; assert(MyModule.someConstant 42);常量在模块注册时被一次性拷贝到 React 侧之后 Runtime 侧的任何修改都不会再同步过去。这一点在 example-native-modules 示例文档 中也有明确说明示例工程的 BrowserInfoModule.js 正是这样把navigator.userAgent作为userAgent常量暴露的React 侧组件则通过BrowserInfo.userAgent直接渲染该字符串。四、简单方法触发无反馈的浏览器操作如果只需要在浏览器窗口触发一个效果、不需要返回值直接给 Native Module 类添加一个方法即可。方法调用时参数会被整体拷贝到 Runtime 侧执行。以下代码来自示例工程的 BrowserInfoModule.js它把传入的字符串设置为浏览器窗口标题Web Worker 中做不到的事setTitle(title) { document.title title; }React 侧调用BrowserInfo.setTitle(Count: this.titleCount);注意如果某个方法不想暴露给 React 侧给它加_前缀即可详见下文命名规则。五、带回调的方法异步取回数据由于 React 代码与 Runtime 之间的通信是异步的桥接通道本身基于消息传递Native Module 的方法无法直接返回值。标准做法是把值传给一个回调函数用经典的 JavaScript 异步模式完成通信。模块取到数据后需要借助ReactNativeContext调用回调。这个 context 引用可以在初始化时通过函数注册形式注入// in client.js import {Module} from react-360-web; class MyModule extends Module { constructor(ctx) { super(MyModule); this._ctx ctx; } getValueWithCallback(id) { ctx.invokeCallback( id, // callback id, passed to the method [42, hello] // array of arguments passed to callback ); } } const r360 new ReactInstance( MyApp.bundle?platformvr, document.getElementById(container), { // Register custom modules at init time nativeModules: [ ctx new MyModule(ctx), // Use the function initializer // to get the context ] } );// in index.js import {NativeModules} from react-360; const {MyModule} NativeModules; MyModule.getValueWithCallback((a, b) { assert(a 42); assert(b hello); });invokeCallback的底层实现在 ReactNativeContext.js 中它接受回调 ID 与参数数组通过executor.invoke(id, args)把数据送回 React 侧桥接层如 WebWorkerBridge.js收到invoke命令后调用invokeCallbackAndReturnFlushedQueue触发 React 侧对应的回调函数。示例工程的 getBatteryLevel 方法 是一个更完整的实战样本它调用navigator.getBattery()读取电量成功后把battery.level通过this._rnctx.invokeCallback(cb, [level])传回 React 侧失败时传回null。回调参数数组可以容纳任意数量的参数React 侧会按顺序解构。六、返回 Promise 的方法现代异步首选现代 JavaScript 往往更偏爱 Promise 形式的异步处理。Native Modules 支持把方法自动包装成 Promise方法名以$结尾原文为以$作为前缀$会在 React 侧被忽略同时 Runtime 侧会自动收到两个回调 ID分别对应 Promise 的resolve与reject。// in client.js import {Module} from react-360-web; class MyModule extends Module { constructor(ctx) { super(MyModule); this._ctx ctx; } $getValueWithPromise(resolveID, rejectID) { // Resolve the Promise ctx.invokeCallback( resolveID, [42] // array of arguments passed to resolve method ); } } const r360 new ReactInstance( MyApp.bundle?platformvr, document.getElementById(container), { // Register custom modules at init time nativeModules: [ ctx new MyModule(ctx), ] } );// in index.js import {NativeModules} from react-360; const {MyModule} NativeModules; MyModule.getValueWithPromise().then(value { assert(value 42); });示例工程中的 $getConfirmation 方法 展示了 reject 的用法它弹出浏览器确认对话框用户点击确定则invokeCallback(resolve, [])点击取消则invokeCallback(reject, [{message: Canceled the dialog}])——reject 时需要传入一个对象用来在 React 侧填充 Error 对象的信息。React 侧调用时方法名不带$即BrowserInfo.getConfirmation(Resolve the Promise?)返回的 Promise 可以被.then()链式处理见 index.js。七、深入原理Module 基类与命名规则所有 Native Module 的行为都由 Module 基类 定义其__describe()方法在模块注册时负责提取模块对外暴露的成员并生成桥接协议描述模块名、常量表、函数名表、Promise 函数 ID 表等。根据 Module.js 的源码注释与实现成员的可见性规则可归纳为命名特征处理方式用途_前缀如_ctx、_rnctx不暴露私有成员仅 Runtime 侧可用$前缀的函数如$getValueWithPromise注册为 Promise 方法$被去除React 侧调用返回 Promise普通原型方法如setTitle注册为可远程调用的函数简单方法或回调方法其他非函数属性如userAgent注册为常量一次性拷贝给 Reactconstructor跳过不暴露具体实现中Module.js会遍历原型方法跳过_前缀与构造函数把$前缀的方法记录进promiseFunctions列表并分配递增的 methodID随后Module.js遍历实例属性把非_前缀、非函数的属性收进常量表。返回的描述数组格式为[模块名, 常量表, 函数名数组, promise函数ID数组, sync函数数组]其中 sync 函数当前版本暂不支持。值得一提的是构造函数中对RCT前缀的处理如果模块名以RCT开头会被自动去掉Module.js这与 React Native 的命名惯例保持一致。__describe()生成的描述在 ReactNativeContext.js 的 describe 函数 中被汇总为remoteModuleConfig并随 bundle 初始化一起发送到 Worker 侧的 React 运行时从而建立完整的调用协议。八、完整实战NativeModules 示例工程解读仓库中的 Samples/NativeModules 是一个完整的可运行示例把上述四种通信模式集成在一个页面面板中Battery电量、Title Bar标题栏、Get Confirmation确认框以及底部的 User Agent 文本由三个文件组成BrowserInfoModule.js自定义模块本体同时演示常量userAgent、简单方法setTitle、回调方法getBatteryLevel与 Promise 方法$getConfirmation四种模式client.jsRuntime 入口使用函数注册形式ctx new BrowserInfoModule(ctx)注入模块并把示例渲染到默认 Surface、设置 360 背景图index.jsReact 侧消费代码通过const BrowserInfo NativeModules.BrowserInfo;拿到模块在componentDidMount中回调取电量、点击按钮修改标题与触发确认框。你可以用 React 360 CLI 新建项目后把该目录下的文件复制到项目中使用启动服务并访问http://localhost:8081/index.html即可看到面板效果。更详细的分步说明见 示例文档。九、常见问题与注意事项为什么方法不能同步返回数值因为 React 代码运行在 Web Worker与 Runtime 的通信是异步消息传递不存在同步返回值通道必须借助回调或 Promise。回调与 Promise 如何选择需要复杂链式异步逻辑时优先 Promise$前缀简单取数场景回调callback 参数同样适用且代码更直接。不想暴露的成员怎么办一律加_前缀包括内部 context 引用如_rnctx这既是为了安全也是框架的约定规则。模块必须在什么时机注册必须在 React 应用启动context.init/ReactInstance初始化之前通过nativeModules数组注册注册完成后桥接协议即被固化。常量会更新吗不会。常量在注册时一次性拷贝到 React 侧之后 Runtime 侧再改动实例属性不会同步。掌握了 Native Modules 之后你就可以把浏览器端的一切能力安全、异步地注入 React 360 应用与框架内置的AudioModule、VideoModule、EnvironmentModule一样构建出功能完整的 360/VR 体验。赞分享前端3D渲染【免费下载链接】react-360Create amazing 360 and VR content using React项目地址https://gitcode.com/gh_mirrors/re/react-360点击查看免费下载相关推荐React Native Turbo Modules实战指南构建高性能原生模块React Native Turbo Modules实战指南构建高性能原生模块 本文深入探讨React Native Turbo Modules的核心概念、架移动开发原生移动跨平台前端OptiScaler实战指南3步解锁跨GPU超分辨率技术OptiScaler实战指南3步解锁跨GPU超分辨率技术 OptiScaler是一款革命性的图形增强工具它通过拦截游戏的原生超分辨率调用并重定向到不同的后端图形学游戏开发React Native Firebase 模块化架构与工程实践连接原生 Firebase SDK 的 React Native 全家桶指南React Native Firebase 模块化架构与工程实践连接原生 Firebase SDK 的 React Native 全家桶指南 React Na移动开发后端上一篇Claude Coder API管理详解如何集成Claude、OpenAI、DeepSeek等主流模型下一篇Chunker项目使用教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考