调试调测
Metro热加载
React Native 使用Metro构建 JavaScript 代码和资源,本节介绍了配置步骤和使用方式。
配置Metro
您需要在项目的 metro.config.js 文件中配置 harmony 平台的 Metro 配置选项。它可以导出:
- 一个对象(推荐),将与Metro的内部配置默认值合并。
- 一个函数,该函数将使用Metro的内部配置默认值被调用,并返回最终的配置对象。
在 React Native 中,你的 Metro 配置应该扩展@react-native/metro-config或@expo/metro-config。这些包含构建和运行 React Native 应用所需的基本默认值。
为了在 harmony 平台上配置 Metro,您还需要拓展 react-native-harmony,通过 createHarmonyMetroConfig 创建 harmony 平台的 Metro。
下面是示例工程中默认的 metro.config.js 文件:
// metro.config.js
const { mergeConfig, getDefaultConfig } = require('@react-native/metro-config');
const {
createHarmonyMetroConfig,
} = require('@react-native-oh/react-native-harmony/metro.config');
/**
* Metro配置
* https://metrobundler.dev/docs/configuration
*
* @type {import("metro-config").ConfigT}
*/
const config = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true
},
}),
},
};
module.exports = mergeConfig(
getDefaultConfig(__dirname),
createHarmonyMetroConfig({
reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony',
}),
config
);
如果您使用的是 RNApp 启动的 RN 框架,那么这一步可以跳过。如果您使用的是 RNSurface 启动的 RN 框架,您就需要创建一个 RNComponentContext,并在 context 的 devToolsController 中增加如下的事件监听,并删除原有的 RNInstance 实例,重新启动一个新的 RNInstance 实例,即可启用热加载的功能:
this.ctx.devToolsController.eventEmitter.subscribe("RELOAD", async () => {
this.cleanUp();
ths.init();
})
使用Metro
RNOH 提供了 MetroJSBundleProvider API 用于加载Metro服务提供的 jsBundle,使用方法如下:
-
场景一 使用
RNApp.ets如果你使用了 RNOH 提供的组件
RNApp,则只需要将new MetroJSBundleProvider()传给jsBundleProvider属性即可:RNApp({ ... // 方式1 // jsBundleProvider: new MetroJSBundleProvider() jsBundleProvider: new TraceJSBundleProviderDecorator( new AnyJSBundleProvider([ // 方式2 new MetroJSBundleProvider(), ]), this.rnohCoreContext.logger), })harmony 工程准备好后,用数据线将真机与电脑连接,打开新的命令行工具并执行:
hdc rport tcp:8081 tcp:8081接下来打开js侧控制台,在 RN 工程目录中执行以下命令启动 RN 应用:
npm run start执行成功后显示以下的内容:

start命令会使用工程目录下默认的metro.config.js,您也可以在package.json文件中配置start命令,使用自定义的 config 文件:··· "scripts": { "start": "react-native start --config metro.config.harmony.js" } ··· -
场景二 不使用
RNApp.ets不使用
RNApp的话,需要开发者自己去创建并管理RNInstance,假设你已经拥有了一个RNInstance的实例rnInstance,加载 Metro 服务的方法可参考如下代码:rnInstance.runJSBundle(new MetroJSBundleProvider())harmony 工程准备好后,接下来打开 js 侧控制台,在 RN 工程目录中执行以下命令启动 RN 应用:
npm run start然后用数据线将真机与电脑连接,打开新的命令行工具并执行:
hdc rport tcp:8081 tcp:8081执行成功后显示以下的内容:

start命令会使用工程目录下默认的metro.config.js,您也可以在package.json文件中配置start命令,使用自定义的 config 文件:··· "scripts": { "start": "react-native start --config metro.config.harmony.js" } ··· -
场景三 访问局域网中的 Metro 服务
-
方法一:
上面的方案需要连接数据线并转发8081端口,**RNOH** 另外还提供了另外一个 API(`MetroJSBundleProvider.fromServerIp`),该方法接收3个参数:-
ip- Metro 服务器的 IP 地址 -
port- Metro 服务的端口地址,默认 8081 -
appKeys- App name合集,默认空数组使用方法便是将
MetroJSBundleProvider.fromServerIp替换掉上面的new MetroJSBundleProvider(),如:
RNApp({ ... jsBundleProvider: new TraceJSBundleProviderDecorator( new AnyJSBundleProvider([ MetroJSBundleProvider.fromServerIp('192.168.43.14', 8081), ]), this.rnohCoreContext.logger), }) -
-
方法二:
可以通过摇一摇弹出
React Native Dev Menu对话框,点击Settings选项,进入Settings页面后点击Debug Server host & port for device选项,会弹出输入框,输入ip地址和端口后重新打开应用即可。

如果希望通过摇一摇打开
React Native Dev Menu对话框,需要在module.json5内配置权限"requestPermissions": [ { "name": "ohos.permission.ACCELEROMETER" } ],
这种方式只需要保证手机能连通电脑,打开 APP 后便能访问 Metro 服务,不需要连接数据线和转发端口。
-
使用 Metro 服务加载 bundle 时如下场景皆可触发应用刷新:
- 在编辑器中编辑代码,并保存,便可以看到改动点自动更新到手机上
- 在命令行工具中输入r,也会触发应用更新
- 在命令行工具中输入d,在弹出的
React Native Dev Menu对话框中选择 Reload 也会触发应⽤刷新
JS调试
打开React Native Dev Menu
注意:在 Release 模式下,Dev Menu 不可使用。
-
分别执行以下命令:
hdc rport tcp:8081 tcp:8081npm run start执行成功后显示以下的内容:

-
在手机上打开应用。
-
在命令行工具中输入
d,便可以看到手机上弹出了React Native Dev Menu对话框。
Element Inspector
您可以通过 Element Inspector 查看 RN 元素的盒子模型及样式,具体操作步骤如下:
-
通过 Metro 加载 bundle,打开 React Native Dev Menu
对话框后选择Toggle Element Inspector`。 -
选择
Inspect,然后点击要审查的元素,便可看到黑色蒙层中显示出了元素的层级关系、样式以及盒子模型,如图:
-
点击蒙层中面包屑的其他节点可查看当前元素的父级/子级元素的样式。
-
重新点击页面上的其他元素,便可切换到该元素。
-
取消选中蒙层中的
Inspect,便可关闭元素审查。
断点调试
在启用断点调试之前,需要您启动RN实例的时候开启 debugger。您可以在 RNApp 中以 rnInstanceConfig 的参数的形式传入:
// index.ets
RNApp({
rnInstanceConfig: {
enableDebugger: true,
···
},
···
})
在编码过程中,您可以使用React DevTools 来设置断点,并调试您的代码。
报错信息输出
LogBox日志
LogBox 是手机侧的故障提示框,当js侧发生错误时手机的应用界面上便会弹出错误告警,如图:

右侧部分即为 LogBox,展示了错误的说明、源码以及调用栈信息。如果您使用的是Metro服务加载bundle,点击设备上 LogBox 中的调用栈,便可在电脑端中使用编辑器(如VSCode)自动打开对应的文件以供您查看并定位问题。
如果您使用的是 RNApp 启动的 RN 框架,那么这一步可以跳过。如果您使用的是 RNSurface 启动的 RN 框架,您就需要创建一个 RNComponentContext 并创建 LogBox 的构造器,并在 context 的 devToolsController 中增加如下的事件监听,并进行对应的启动和关闭 LogBox 弹窗的操作:
this.logBoxDialogController = new CustomDialogController({
cornerRadius: 0,
customStyle: true,
alignment: DialogAlignment.TopStart,
backgroundColor: Color.Transparent,
builder: LogBoxDialog({
ctx: RNComponentContext,
rnInstance: this.rnInstance,
initialProps: this.initialProps,
buildCustomComponent: this.buildCustomComponent,
})
})
···
this.rnInstance.getTurboModule<LogBoxTurboModule>(LogBoxTurboModule.NAME).eventEmitter.subscribe("SHOW", () => {
this.logBoxDialogController.open();
})
this.rnInstance.getTurboModule<LogBoxTurboModule>(LogBoxTurboModule.NAME).eventEmitter.subscribe("HIDE", () => {
this.logBoxDialogController.close();
})
控制台日志
DevEco Studio
当js侧发生错误时,通过以下方法可在 DevEco Studio 上查看到错误信息:
-
手机连接到电脑,打开
DevEco Studio,打开一个项目; -
在
DevEco Studio下方点击Log; -
在
HiLog的筛选栏中一次选择你连接的设备、All logs of selected app、[应用包名]、Warn/Error; -
设置完后,js 侧发生故障时,故障信息便能实时的显示在
DevEco Studio,如图:
命令行工具
若是通过Metro服务加载bundle,当js侧发生错误时,命令行工具便会输出错误发生的原因和具体位置,如图:

其他
在 React Native 中亦可通过 console.log、console.warn、throw 等触发一个日志或告警。
添加Trace
JS侧
可通过引入 react-native 提供的 Systrace 添加 Trace。
例如添加一个名为 ONPRESS 的同步 Trace,以跟踪一个按钮的 onPress:
import {Systrace} from 'react-native';
...
<Button
onPress={() => {
Systrace.beginEvent('ONPRESS');
// do something
Systrace.endEvent();
}}
/>
例如添加一个名为 ASYNC 的异步 Trace,通过两个按钮控制开始和结束:
import {Systrace} from 'react-native';
...
let traceCookie = null;
...
<Button
onPress={() => {
if (traceCookie === null) {
traceCookie =
Systrace.beginAsyncEvent('ASYNC');
}
}}
/>
<Button
onPress={() => {
if (traceCookie !== null) {
Systrace.endAsyncEvent('ASYNC', traceCookie);
}
}}
/>
/>
ArkTS侧
ArkTS 侧大多数类和对象的成员中存在一个 logger 或者 ctx 对象(ctx 对象内涵一个 logger,logger 内提供了封装的 startTracing() 用于添加 Trace)。
例如添加一个名为 myTrace 的 Trace:
const stopTracing = this.logger.clone("myTrace").startTracing()
// do something
stopTracing()
也可以直接使用未封装的 @ohos.hiTraceMeter 能力添加 Trace。
例如添加一个名为 myTrace 的 Trace,使用 0 作为识别标志(用于区分同名 Trace):
import hiTrace from '@ohos.hiTraceMeter';
...
hiTrace.startTrace(`myTrace`, 0)
// do something
hiTrace.finishTrace(`myTrace`, 0)
C++侧
可通过引入 react/renderer/debug/SystraceSection.h ,实例化一个 facebook::react::SystraceSection 对象添加 Trace。
Trace的范围与该 facebook::react::SystraceSection 对象的生命周期(作用域)相同,可以使用大括号控制范围。
例如添加一个名为 myTrace 的 Trace:
#include <react/renderer/debug/SystraceSection.h>
...
{
facebook::react::SystraceSection s("myTrace");
// do something
}
通过监听 RNOHErrors 捕获 Bundle 内错误信息
需要注意的是:该方法仅在打包 bundle 时关闭 dev 选项,即进行 product 模式打包时,才能对 bundle 内错误进行监听:
"npm run codegen && react-native bundle-harmony --dev=false"
可通过 RNOHCoreContext 获取上下文对象,使用 subscribeToRNOHError 进行监听。
如以下例子所示,可在 EntryAbility.ets 的 onCreate 对 bundle 内的错误进行监听。
import { RNAbility, RNInstanceError, RNOHCoreContext } from '@rnoh/react-native-openharmony';
import Want from '@ohos.app.ability.Want';
import {JSON} from '@kit.ArkTS';
export default class EntryAbility extends RNAbility {
getPagePath() {
return 'pages/Index';
}
override onCreate(want: Want): void {
super.onCreate(want);
AppStorage.get<RNOHCoreContext>('RNOHCoreContext')!.subscribeToRNOHErrors((err) => {
console.log('HERE: error logged from entryability! ' + JSON.stringify(err))
console.log('HERE: err.getMessage: ' + err.getMessage())
console.log('HERE: err.getSuggestions:' + JSON.stringify(err.getSuggestions()), null, 2)
console.log('HERE: err.getExtraData: ' + JSON.stringify(err.getExtraData()), null, 2)
console.log('HERE: err.getStack: ' + JSON.stringify(err.getStack(), null, 2))
//Not all errors are RNInstanceError's, so we need to check for that.
if (err instanceof RNInstanceError) {
console.log('HERE: err.getRNInstanceId: ' + err.getRNInstanceId())
console.log('HERE: err.getRNInstanceName: ' + err.getRNInstanceName())
}
})
}
}
在本例子中,主要是对捕获的 error 进行打印,因此在其中添加了 console.log(),在终端中打印出具体的 error 信息:
例如,当需要 error 中的 Message 信息时,直接调用 err.getMessage() 即可。
console.log('HERE: err.getMessage: ' + err.getMessage())
同时,也支持对 error 错误中的 RN 实例信息的补充,例如:
if (err instanceof RNInstanceError)
{
console.log('HERE: err.getRNInstanceId: ' + err.getRNInstanceId())
console.log('HERE: err.getRNInstanceName: ' + err.getRNInstanceName())
}
当捕获的 error 属于 RN 实例的 error 时,就打印出 RN 实例错误相关的信息。