6 Star 13 Fork 1

杨建东 / react-native-for-harmony

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

react native for harmony整体架构说明

[TOC]

1. 整体架构说明

1.1 背景说明

待决策:优先基于旧架构分析RN对Open Harmony的依赖

react native从22年年中发布了0.69版本,并正式开始支持新架构(New Architecture),新架构在性能,开发效率等都有重大提升。按照官网的说法,老架构(legacy architecture)将逐步废弃。

Native Module and Native Components are our stable technologies used by the legacy architecture. They will be deprecated in the future when the New Architecture will be stable. The New Architecture uses Turbo Native Module and Fabric Native Components to achieve similar results. ---- RN官网

但鉴于存量的三方类RN框架主要以老架构(legacy architecture)为主,而且后续迁移到新架构本文的部分设计可以复用(复用部分后续介绍),所以本文同时对新,旧架构同时进行了分析。

此外,由于rn for android和ios的原理基本类似,故这里主要以rn for android为例介绍

1.2 老架构

1.2.1 整体简介

React Native整体架构

如上图所示,RN 主要涉及以上两个部分,包括应用层,框架层。其中RN框架,主要包括几个模块(紫色部分需要鸿蒙化)

  • RN应用开发环境 (包括编译工具链,预览调试器,profile等)
  • SDK 和工程目录
  • 框架核心(包括模块化管理,任务调度,线程模型等)
  • UI模块 (涉及ui异步布局,ui解析,ui自定义实现,事件等等)
  • 非UI的各种Native模块(包括UI, 各种系统能力API, 调试预览,异常处理等等)
  • JS框架模块(包括react js框架和React native js框架)
  • JS引擎 & 语言 & FFI 模块(包括JSC引擎,以及RN原生内置的Hermes引擎 ,语言及其跨语言调用)
  • JSI模块 (完成针对不同平台和js引擎的跨语言调用)

常说的RN是一个跨平台框架,只是说RN应用可以跨平台的,而不是说RN框架本身跨平台的。换句话说,RN框架针对不同的平台有不同的实现。上述提到的各个模块都是强耦合系统能力的,尤其是紫色部分需要鸿蒙化

其中sdk,JSI,框架核心,各种Native模块针对不同的os平台都有对应平台语言的实现。而RN JS框架由于最终要调用os能力,故在适配层有不同的实现。

1.2.2 整体工作量(基于老架构)

由于各个模块包含不同的方案,总体工作量需要待决策后补齐

  • RN

不包括商用落地的需求:如性能优化,国际化,无障碍,多语言,安全隐私等

领域 工作量
JS引擎 & 语言 6~10人月
开发环境 & 调试 53人月
SDK & 工程目录 8人月
基础框架核心能力 5人月
框架核心能力-线程模型/任务调度 10人月
框架核心-模块化管理 14人月
UI模块 39人月
API 20~30人月
其他商用落地:安全,性能,无障碍,国际化,隐私,CI等 50人月
三四方库 分析中
总体 220人月 + 三方
  • Open Harmony
领域 工作量(不包括OH4.0) 备注
Ark引擎和语言 分析中
IDE & 工具链 10人月
ArkUI 30人月 + (OH4. 0 20人月)
API 明确应用后分析,基本可控 需要进一步明确典型应用
webview 明确应用后分析,基本可控 OH4.0满足基本功能
需要进一步明确典型应用
三方库 分析中 需要进一步明确典型应用
图形 2人月
包管理 & 元能力 3.2交付

1.2.3 相关数据

以anroid平台为例,代码行数说明:(不包含后续介绍的三方库)

代码类别 代码行数 说明
JAVA (框架) 10W+ 全部重写
JS (RN JS框架) 26W+ 适配层重写,大部分复用
JS (工具链 4W 适配层重写,大部分复用
c++ (JSI) ??? 适配层重写(harmony基于napi)
C++ (Hermes,Yoga) ??? 编译配置(cmake,c库)

1.2.4 三四方库

说明:其中的二三四方,主要相对于OS(如android); 三方特指RN本身。

强调: React、React Native本身的强大之处在于社区丰富度上,meta维护的只是很小的一部分(core部分),其他功能都在React Native Community运作,而且这些功能都是React或React Native应用必须的。

主要分三类:

  • Native生态:Android:30+ (build.gradle) , 这里直列了core部分的依赖
  • Node JS生态: ~700(node_moduels),这里直列了core部分的依赖
  • React Native 生态:1195 react community,以及基于React Native进行二次封装的:比如Expo(遵循React Native规范),国内包括类RN部分(独立规范)。

其中,比较典型的三四方库,如下:(Android为例)

  • 典型Android/java库

具体参考 框架: build.gradle

代码行数 来源 功能介绍 官网&开源&代码路径 工作量
com.facebook.fresco meta 图片加载和渲染 MIT License
https://frescolib.org/
https://github.com/facebook/fresco
hermes meta js引擎 MIT license
https://hermesengine.dev/
https://github.com/facebook/hermes
com.facebook.fbjni meta jsi的android实现,跨语言数据传递 Apache-2.0 license
https://github.com/facebookincubator/fbjni
com.facebook.soloader meta 动态链接库管理 Apache-2.0 license
https://github.com/facebook/SoLoader
com.facebook.yoga meta 布局库
java.util.concurrent 四方 任务调度 https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/package-summary.html
javax.inject.Provider 四方 模块化依赖注入加载 Apache License, Version 2.0
http://javax-inject.github.io/javax-inject/
https://github.com/javax-inject/javax-inject
okhttp3 四方 网络加载 Apache-2.0 license
https://square.github.io/okhttp/
https://github.com/square/okhttp
okio
jsc 四方 js引擎 https://developer.apple.com/documentation/javascriptcore
https://github.com/WebKit/WebKit/tree/main/Source/JavaScriptCore
Libevent 四方 3-clause BSD
https://libevent.org/
https://github.com/libevent/libevent
folly-deprecate-dynamic-initializer 四方 调试
fbflipper 四方 调试 MIT License
https://fbflipper.com/docs/features/react-native/
https://github.com/facebook/flipper
react-native-debugger ? 四方 调试 MIT license
https://github.com/jhen0409/react-native-debugger

其他的四方java库,后续需要进一步打开分析,如下:

    1. appcompat-v7
    1. boost_1_76_0
    1. List item
    1. double-conversion-1.1.6
    1. glog-0.3.5
    1. Fmt
    1. com.android.builder.model.v2.AndroidModel
    1. androidx.core.util.Pools
    1. com.google.code.findbugs
    1. androidx.autofill
    1. androidx.swiperefreshlayout

以及各种测试框架:

  1. junit:junit:${JUNIT_VERSION
  2. org.assertj:assertj-core:${ASSERTJ_VERSION
  3. org.mockito:mockito-core:${MOCKITO_CORE_VERSION
  4. org.powermock:powermock-api-mockito2:${POWERMOCK_VERSION}
  5. org.powermock:powermock-classloading-xstream:${POWERMOCK_VERSION}
  6. org.powermock:powermock-module-junit4-rule:${POWERMOCK_VERSION}
  7. org.robolectric:robolectric:${ROBOLECTRIC_VERSION}
  8. fileTree(dir: "src/main/third-party/java/buck-android-support/", include: ["*.jar"])
  9. androidx.test:runner:${ANDROIDX_TEST_VERSION}
  10. androidx.test:rules:${ANDROIDX_TEST_VERSION}
  11. org.mockito:mockito-core:${MOCKITO_CORE_VERSION}
  • 典型Node/js库

具体参考:

  1. Rn框架 npm package.json
  2. RnHello World package.json

React Native Comunity本身也是Node/js的一部分。

代码行数 来源 功能介绍 官网&开源&代码路径 工作量
cli react社区 RN工具链:脚手架cli
react-native-picker react社区 UI picker https://github.com/react-native-picker/picker
react-native-camera react社区 camera https://github.com/react-native-camera/react-native-camera
react-native-pager-view react社区 pager-view https://github.com/callstack/react-native-pager-view
react-native-webview react社区 webview https://github.com/react-native-webview/react-native-webview
react-native-clipboard react社区 clipboard https://github.com/react-native-clipboard/clipboard
react-native-async-storage react社区 async-storage https://github.com/react-native-async-storage/async-storage
react-native-geolocation react社区 geolocation https://github.com/michalchudziak/react-native-geolocation
metro系列库(20个) react社区 RN工具链: bundler MIT license
https://facebook.github.io/metro/
https://github.com/facebook/metro
flow系列库 meta React FLow语言
对标Typescript的一种js衍生语言
实际上:基于js的静态类型检测器
MIT License
https://flow.org/
https://github.com/flowjs/flow.js/
expo系列(149) 四方 react native衍生开发框架
使用react native范式
增加组件和api等各种能力
MIT license
https://expo.dev/
https://github.com/expo/expo/tree/main/packages

1.3 新架构整体简介(后续打开)

具体参照官网介绍: 新架构 业界介绍

https://cloud.tencent.com/developer/article/1889895 https://cloud.tencent.com/developer/article/1878792 https://cloud.tencent.com/developer/article/1950899

主要是性能和开发工程效率的增强,换句话说,对上不影响RN应用,对下不影响系统适配。具体,主要包括以下几个方面

  • Turbo Native module和codegen:用以代替native module,实现模块化效率提升,以及针对js代码生成c++适配代码从而保证跨语言一致性
  • Fabric Native Component渲染:用以代替Native Component, 具体基于react并发模型,以及 ui核心模块的c++实现(异步并行布局),实现跨平台共享和渲染效率提升
  • Hermes内置,和RN共版本迭代
  • react-naitve-skia目标实现自渲染机制,但是目前处于alpha release 的阶段

1.3.1 整体简介

总的来说,0.69主要在以下几个方面进行比较大的改动,同时也涉及鸿蒙化相关的工作

  • 跨语言调用:从json桥接改为jsi,支持同步调用,性能更优,并支持codegen保证前后代码接口类型安全
  • 并发能力:前端框架通过React18版本支持了对并发的支持
  • Fabric Render:布局等渲染功能native c++化,从而支持不同平台共用同一套方案
  • turbo module模块化:基于反射实现依赖注入,支持按需懒加载
  • 内置js引擎(harmes)和rn共版本管理

React Native新旧框架对比

1.3.2 相关数据

待补

2 JS 引擎模块

2.1 基本介绍

官网介绍:JavaScriptRuntime

RN基于js框架开发,依赖js引擎。此外由于一般跟os是不同语言的实现,所以存在跨语言调用实现。为了屏蔽不同OS和不同JS的差异,RN引入了JSI实现跨语言的统一封装。具体如下:

enter description here

2.1.1 ArkJS和ES的gap

  1. eval等功能,由于安全原因被限制,实际当中RN应用要使用怎么办
  2. harmony的ts版本固定的,当前4.2.4, 由于ts迭代较快,开发者想使用最新ts版本该怎么办。

待补

2.1.2 NAPI GAP

待补

2.1.3 ArkJS调试运行信息GAP

jsc,Hermes都是开源代码,三方可以直接定制后并集成到产品中。而arkj虽然是开源的,但是不能产品化定制,而且h头文件不能使用。并且三方IDE依赖的调试能力,arkjs没有对外提供。

待补

2.2 方案说明

待决策:JS引擎方案选择,理论上应该两种方案都支持。推荐方案1

  • 方案1: Hamony上porting RN的Hermes/JSC引擎

    • 优点:rn原生推荐:定制化能力强,如增强调试,binary size裁剪,动态化加载,支持的es spec丰富,ts版本可以配置
    • 缺点:harmony禁止jit功能,性能有一定损耗;跨语言调用存在api性能损耗
  • 方案2: 使用ArkJS引擎

    • 优点:API直接调用,理论上得性能更优(AOT等)
    • 缺点:部分es不支持(eval 、new Function等),ts版本不可变(当前:4.2.3),动态化能力受限,如上定制化能力受限(如三方IDE不能使用断点能力)

2.3 需求描述

RN框架团队(待补)

SR需求 领域 工作量
方案1:porting Hermes/JSC RN 6人月
方案2:NAPI适配JSI,以支持Harmony平台能力 RN 10人月
harmony团队

只列出通用部分,其他对方舟的依赖具体见各个模块,涉及调试(基于ark机制获取堆信息,断点调试,work上支持js 动态import js文件,热加载等),worker能力增强,taskpool等

SR需求 领域 工作量
方案2:ts版本可配置 方舟
方案2:头文件暴露 方舟

3. 开发环境等

3.1 基本介绍

包括工具链,预览调试器等,具体参照官网介绍:

主要涉及以下几个方面:

  • 支持的开发平台:window,mac,linux
  • 支持的IDE环境,包括vsc(涉及插件开发)和os原生IDE
  • 调试和预览环境,主要包括:react native debug tool,metro等
  • 性能和调优工具(如trace,profile,hmr加载,ram bundle分包加载等)

由于上述能力都是强耦合系统的,所以都涉及harmony化的相关工作。

  • 工具链

需要在harmony平台支持 Expo Go、CLI、debug tool等涉及bundle,框架重启,远程加载、debug配置,应用安装,堆栈调试,ui inspect等各种开发和调试的工作。

具体的说,以Expo Go为例,具体涉及yarn或npm等相关的命令

yarn/npm: 工具链命令

其中执行以下命令后,会启动metro server,完成bundle,框架重启,远程加载、debug配置,应用安装等的功能 yarn start

metro server

  • IDE预览调试等,具体见:debuging

React Developer Tools:预览&Inspect&JS断点调试

以及四方的调试工具,react-native-debugger react-native-debugger

3.2 方案说明

总体上,建议这部分工作整体上follow RN现有设计和功能即可。

以工具链为例,RN工具链,metro本身既要编译react native应用和框架前端相关的代码,也要通过调用各种os的编译工具链完成对rn后端代码的编译。编译后的产物,以文件读取,或http加载,内存加载等各种方式动态加载到不同os平台的js环境当中。

RN工具链总体介绍

待决策:推荐使用方案一:

说明:尽管metro和hvigor都是基于npm/Yarn实现的,理论上是可以融合在一起的(基于higor底座实现),但是由于rn是基于flowjs实现的,而且本身依赖巨量的三分npm/Yarn库,将metro融合到hvigor中,对hvigor打包工具(基于rollup)的能力要求极高,而且不方便react工具链的维护。所以采用异步加载 + 全局挂载的方式,本质上把

  • 方案一:follow现有方案 优点:方便rn对工具链的维护 缺点:依赖动态化加载能力

  • 方案二: 约点:混合编程和同步编译,不依赖动态加载能力 缺点:对rn现有架构改变大,需要将metro能力融合到hvigor中,对hvigor能力要求高,工作量大

3.3 需求描述

  • RN框架团队(待补)
SR描述 领域 工作量
支持各种yarn等工具链的相关命令 RN 5
在vsc开发调试鸿蒙应用需要的插件 RN 10
支持metro调试,涉及对rn框架、应用打包并应用动态加载,abc热机制 RN 10
支持各种debug tool,预览,inspect,断点调试等功能,涉及websoket以及rn js框架修改 RN 10
支持各种trace,profile,hmr,ram bundle和内敛引用优化能力 RN 8
方案2:将metro的各种工具融合到hvigor中,支持folwjs和ets/TS混合编程 RN 10
  • harmony团队(待补)
SR描述 领域 工作量
使用方舟js引擎:基于ark机制获取堆信息 方舟
使用方舟js引擎:work上支持js 动态import js文件 方舟
支持获取ui组件信息,以支持inspect(需要demo验证,可能不需要) ArkUI
工具链编译配置能力开放, 具体为能被三方触发调用, IDE
方案2(待定):识别将metro的各种工具融合到hvigor中,hvigor的能力缺乏 IDE

4. SDK工程目录

RN框架针对不同的平台有不同的实现,并具体通过暴露接口 + 二进制交付的方式给系统应用集成。

4.1 基本介绍

具体参照官网介绍:

其中关键的几点如下(以android 为例):

  • SDK二进制集成依赖:
dependencies {
    ...
    implementation "com.facebook.react:react-native:+" // From node_modules
}

allprojects {
    repositories {
        maven {
            // All of React Native (JS, Android binaries) is installed from npm
            url "$rootDir/../node_modules/react-native/android"
        }
        ...
    }
    ...
}
  • API调用(涉及UI显示,rn框架参数配置,如网络加载,资源读写等)
public class MyReactActivity extends Activity implements DefaultHardwareBackBtnHandler {
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SoLoader.init(this, false);

        mReactRootView = new ReactRootView(this);
        List<ReactPackage> packages = new PackageList(getApplication()).getPackages();
        // 有一些第三方可能不能自动链接,对于这些包我们可以用下面的方式手动添加进来:
        // packages.add(new MyReactNativePackage());
        // 同时需要手动把他们添加到`settings.gradle`和 `app/build.gradle`配置文件中。

        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setCurrentActivity(this)
                .setBundleAssetName("index.android.bundle")
                .setJSMainModulePath("index")
                .addPackages(packages)
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
         // 注意这里的MyReactNativeApp 必须对应"index.js"中的
        // "AppRegistry.registerComponent()"的第一个参数
        mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null);

        setContentView(mReactRootView);
    }

    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }
}
  • rn 应用目录 类比其他系统,增强相应的目录,或相应的文件,或编译运行开关,具体见如下harmony部分。

rn 应用目录 其中,harmony内部为遵循OH目录结构的目录。

rn for harmony 框架目录

  • rn工程目录

类比其他系统,增强相应的目录,或相应的文件,或编译运行开关,具体见如下ReactHarmony部分。

enter description here

还有其他的要求,如RN jar的权限声明,基于node_module的发布等等,具体见官网介绍

4.2 方案说明

待决策:整体上follow RN现有设计和功能 har包支持worker har支持权限配置 har支持页面配置 支持工程外路径的module加载

4.3 需求描述

  • RN框架团队(待补)
SR描述 领域 工作量
提供reactRoot组件用于显示rn应用,对接事件等 RN框架团队 2人月
提供react instance api完成各种参数配置,支持多实例 RN框架团队 2人月
对接harmony的用户框架的各种生命周期 RN框架团队 2人月
整体以二进制har包交付,并发布到npm市场 RN框架团队 2人月
  • harmony团队
SR描述 领域 工作量
har包支持worker IDE W
har支持权限配置和权限合并 包管理 3.2:23/330交付
支持工程外路径的module加载 IDE

5 框架核心- 线程模型/任务调度

其中实例管理,显示窗口, 上下文环境比较简单,这里主要介绍线程模型/任务调度,其他后续章节单独介绍

5.1 基本介绍

官网介绍:Threading Model

任务调度,线程模型 此外,由于涉及多线程并发的问题,故RN使用了以下能力完成各种并发操作

ava.util.concurrent

5.2 方案说明

背景:harmony不允许三方使用worker,而是推荐基于taskpool实现。但是,使用taskpool的话,本质上没有follow rn的设计思路。即使不考虑taskpool是否能做到线程模型的能力,也存在不被rn社区接收的可能性。

待决策:

  • 方案1: 使用worker实现,允许三方启动worker,调用方无感知。 优点:follow rn现有的设计思路 缺点:harmony的work能力缺失严重,开发便捷性差,性能欠缺。
  • 方案2: 使用task pool实现 优点:无感知线程能力 缺点:对rn现有架构改动比较大,能力gap大,短期无法验证功能gap
  • 方案1:基于worker实现

    harmony worker 限制和低效

    • 方案说明

      本质上是基于worker实现类似线程的能力。但harmony现在的worker机制存在限制,具体如下:

      • 只能在父子worker之间通信数据

        • 不支持web的share worker功能
      • 数据共享效率低

        • 需要序列化
        • transfer无法共享
      • 任务调度能力较低

        • 无法传递函数
        • 宏任务、微任务无法在worker之间通信
      • 同步机制: 待原型验证 由于worker本质上是一种actor模型,通过深度copy序列化避免了数据竞态操作,故理论上么有对锁的依赖,但不排除对同步信号的依赖

  • 方案详细说明

    • 只能在父子worker之间通信数据

    由于只能在父子父子worker之间通信数据,故所有的通信都要绕道ui线程进行通信,再由于worker之间数据共享效率低,势必影响线程使用的性能。同时导致线程通信管理的复杂性提升。

    • 数据共享效率低 如前所述,RN多线程机制依赖大量的跨线程数据通信机制,每次传递数据都要序列化才能共享(部分可以直接transfer除外),势必影响RN应用的整体性能。
    • 任务调度能力较低 无法传递函数,尽管通过一定程度可以规避,但是会导致代码耦合度和代码行数提高,具体无法实现如下命令者模式的代码,而必须转为远程代理模式,即发送端和接受端必须同时耦合命令的行为。
  private final class CreateViewOperation extends ViewOperation {

    private final ThemedReactContext mThemedContext;
    private final String mClassName;
    private final @Nullable ReactStylesDiffMap mInitialProps;

    public CreateViewOperation(
        ThemedReactContext themedContext,
        int tag,
        String className,
        @Nullable ReactStylesDiffMap initialProps) {
      super(tag);
      mThemedContext = themedContext;
      mClassName = className;
      mInitialProps = initialProps;
      Systrace.startAsyncFlow(Systrace.TRACE_TAG_REACT_VIEW, "createView", mTag);
    }

    @Override
    public void execute() {
      Systrace.endAsyncFlow(Systrace.TRACE_TAG_REACT_VIEW, "createView", mTag);
      mNativeViewHierarchyManager.createView(mThemedContext, mTag, mClassName, mInitialProps);
    }
  }
  • 方案2:基于taskpool

worker的各种限制,本质可以理解为宏任务,微任务无法在worker之间传递,无法实现类似其他os,如android的runnable/looper,或者ios的任务调度能力。理论上,如果taskpool支持跨线程的高效任务调度,也可以实现rn的线程模型的功能和性能要求。

  • 方案说明

使用方舟的TaskPool机制实现。和方案1的本质区别在于,开发者不感知thread,不需要维护对应的生命周期 , 而是由taskpool统一管理。理论上,统一管理可以降低系统的资源消耗,提高系统的整体性能。

一个典型示例为下:

import taskpll from "@ohos.taskpool"

@concurrent
function someLogic(para: Para) {
	//some prcess to para
	//.....
	
   return para;
}

async function controller() {
	let para = {message: "creatInstance", value: "Text"}
	await taskpool.execute(someLogic, para)

}

controller().then(result => {
	//some prcess to para
})

或者使用Task,增加以下优先级或者生命周期控制的能力等,本质上区别不大。

目前方舟的规划里,someLogic只能是顶层函数或类的static函数。但上述worker的问题,依然存在。同时没有task的结构化并发机制来处理各种异常,保留了actor的特征,及序列化来避免竞态,但存在深度copy的性能问题。

  • 方案详细说明:待taskpooll机制完善后增加。

5.3 需求描述

实现上述方案,需要实现如下SR

  • 方案1

    • RN框架团队(待补)
SR描述 领域 工作量
线程和消息通信管理 RN框架团队 2人月
ui线程管理和通信 RN框架团队 2人月
js线程管理和通信 RN框架团队 2人月
native模块线程管理和通信 RN框架团队 2人月
其他线程管理和通信 RN框架团队 2人月
  • harmony
SR描述 领域 工作量
支持类似web的shared worker机制,实现任意worker之间可以之间通信 方舟
work通信支持高效数据传递,规避序列化带来的各种损耗,随之而来带来同步竞态问题 方舟
work通信支持任务或者函数通信,从而解耦发送接收双方的功能,提高编程效率 方舟
宏任务,微任务可以在worker之间传递 方舟
  • 方案2(待taskpool能力实现后增加说明)

    • RN框架团队(待补)

    • harmony(待补)

6. 框架核心- 模块化管理

RN本质上只是一个JS开发框架,不具备完整的系统能力,换句话说,其对应的能力都要依赖OS原生能力的实现。OS的能力比较多,为了提高管理效率:降低启动时间、内存消耗,同时方便代码工程管理,RN提供了模块化机制来管理RN的各种功能。模块化管理包括运行时和代码工程编译时的功能解耦。具体通过对不同的功能实现在开发态和运行态的高内聚,低耦合,从而实现框架在运行时根据应用需要,按需加载必要功能模块,从而极大降低启动时间和运行时内存。同时为了避免在应用使用时加载对应模块所需要的时间,在懒加载的同时,提供了预加载配置能力,方便对重要和常用的功能进行预加载。此外,模块本身具备cache能力,即只需要加载一次既可,后续不需要重复加载。

需要强调的是,rn的模块化不是文件级的模块,而是一个功能场景的模块,如ui模块,js框架模块等等。ui模块内部等各个组件也是模块化的,即只有使用text才会创建对应的text实例。

同时,实践中,有些功能,RN可能没有封装,故其提供了自定义模块化功能,用于开发者按需实现所需的功能并以模块化的方式加载管理,同时可以发布到NPM市场,方便其他开发者使用。

6.1 基本介绍

官网:NativeModule 官网:模块npm发布

用一般的工厂模式通过new 构造实例来实现懒加载,存在各种问题。如样板代码多,单元测试麻烦,实例生命周期管理复杂,没有cache复用机制等等。通过依赖注入的方式实现可以有效避免上述问题。具体参照:为什么需要依赖注入, 这里引用几句关键的话如下:

为了解决以上问题,依赖注入模式应运而生了,它用容器替代了工厂,我们不要再新建工厂类,工程师只需要处理依赖关系即可,对象的创建Constructor、对象之间的装配dependency和对象的销毁destroy都交由容器来处理,所以依赖注入又被称为控制反转。 控制反转或者依赖注入其实遵循了以下两个法则:

好莱坞法则:不要打电话给我们,我们会打给你 “我们”就可以理解为容器,打电话的时机交由容器来控制。 迪米特法则(最少知识原则):不要和陌生人说话 对象应该依赖接口,而不是具体实现,对其它对象要尽可能少的了解。

RN for Android具体通过遵循标准 JSR-330的以下依赖注入库实现,相关描述见javax.inject介绍, 和接口说明

javax.inject.Provider

最终实现在需要时, 需要通过反射构造出模块的实例。而通过反射能力有着需要样板重复代码,通过元编程能力实现依赖配置注入器从而提高开发效率,如下库。

java.lang.annotation

@Model
public class Index {

    @Inject
    Provider<Boundary> boundary;

    public String getMessage() {
        return boundary.get().message();
    }

}
@Stateless
public class Boundary {

    public String message() {
        return "Good morning";
    }
}

此外,react native提供了ReactPackage等相关接口,为了支持应用开发者扩展自定义的native module。这里涉及各种java的元编程能力,具体如下

@Retention(RUNTIME)
public @interface ReactMethod {
  boolean isBlockingSynchronousMethod() default false;
}

React Native把 moulde归为四类, MainPackageCorePackageDebugPackge,自定义ReactPackage

其中,MainPackage代码如下:本质上就是预先创建了一个Map,维护了各种模块的holer:Provider,并在应用需要时动态创建对应的module实例


@ReactModuleList(
    nativeModules = {
      AccessibilityInfoModule.class,
      AppearanceModule.class,
      AppStateModule.class,
      BlobModule.class,
      FileReaderModule.class,
      AsyncStorageModule.class,
      ClipboardModule.class,
      DialogModule.class,
      FrescoModule.class,
      I18nManagerModule.class,
      ImageLoaderModule.class,
      ImageStoreManager.class,
      IntentModule.class,
      NativeAnimatedModule.class,
      NetworkingModule.class,
      PermissionsModule.class,
      ShareModule.class,
      SoundManagerModule.class,
      StatusBarModule.class,
      ToastModule.class,
      VibrationModule.class,
      WebSocketModule.class,
    })
public class MainReactPackage extends TurboReactPackage {

  private MainPackageConfig mConfig;

  public MainReactPackage() {}

  /** Create a new package with configuration */
  public MainReactPackage(MainPackageConfig config) {
    mConfig = config;
  }

  @Override
  public @Nullable NativeModule getModule(String name, ReactApplicationContext context) {
    switch (name) {
      case AccessibilityInfoModule.NAME:
        return new AccessibilityInfoModule(context);
      case AppearanceModule.NAME:
        return new AppearanceModule(context);
      case AppStateModule.NAME:
        return new AppStateModule(context);
       .....
}

  @Override
  public ReactModuleInfoProvider getReactModuleInfoProvider() {
    try {
      Class<?> reactModuleInfoProviderClass =
          Class.forName("com.facebook.react.shell.MainReactPackage$$ReactModuleInfoProvider");
      return (ReactModuleInfoProvider) reactModuleInfoProviderClass.newInstance();
    } catch (ClassNotFoundException e) {
      // In OSS case, the annotation processor does not run. We fall back on creating this byhand
      Class<? extends NativeModule>[] moduleList =
          new Class[] {
            AccessibilityInfoModule.class,
            AppearanceModule.class,
            AppStateModule.class,
            BlobModule.class,
            FileReaderModule.class,
            AsyncStorageModule.class,
            ClipboardModule.class,
            DialogModule.class,
            FrescoModule.class,
            I18nManagerModule.class,
            ImageLoaderModule.class,
            ImageStoreManager.class,
            IntentModule.class,
            NativeAnimatedModule.class,
            NetworkingModule.class,
            PermissionsModule.class,
            ShareModule.class,
            StatusBarModule.class,
            SoundManagerModule.class,
            ToastModule.class,
            VibrationModule.class,
            WebSocketModule.class
          };

      final Map<String, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
      for (Class<? extends NativeModule> moduleClass : moduleList) {
        ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);

        reactModuleInfoMap.put(
            reactModule.name(),
            new ReactModuleInfo(
                reactModule.name(),
                moduleClass.getName(),
                reactModule.canOverrideExistingModule(),
                reactModule.needsEagerInit(),
                reactModule.hasConstants(),
                reactModule.isCxxModule(),
                TurboModule.class.isAssignableFrom(moduleClass)));
      }

      return new ReactModuleInfoProvider() {
        @Override
        public Map<String, ReactModuleInfo> getReactModuleInfos() {
          return reactModuleInfoMap;
        }
      };
    } catch (InstantiationException e) {
      throw new RuntimeException(
          "No ReactModuleInfoProvider for CoreModulesPackage$$ReactModuleInfoProvider", e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(
          "No ReactModuleInfoProvider for CoreModulesPackage$$ReactModuleInfoProvider", e);
    }
  }

}

一个典型的NativeUI ManagerModule如下

@ReactModule(name = UIManagerModule.NAME)
public class UIManagerModule extends ReactContextBaseJavaModule
    implements OnBatchCompleteListener, LifecycleEventListener, UIManager {
  public static final String TAG = UIManagerModule.class.getSimpleName();

// ......
  public static final String NAME = "UIManager";
// ......

  @Override
  public void onHostResume() {
    mUIImplementation.onHostResume();
  }

// ......
  @ReactMethod
  public void createView(int tag, String className, int rootViewTag, ReadableMap props) {
    if (DEBUG) {
      String message =
          "(UIManager.createView) tag: " + tag + ", class: " + className + ", props: " + props;
      FLog.d(ReactConstants.TAG, message);
      PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.UI_MANAGER, message);
    }
    mUIImplementation.createView(tag, className, rootViewTag, props);
  }

  @ReactMethod
  public void updateView(final int tag, final String className, final ReadableMap props) {
    if (DEBUG) {
      String message =
          "(UIManager.updateView) tag: " + tag + ", class: " + className + ", props: " + props;
      FLog.d(ReactConstants.TAG, message);
      PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.UI_MANAGER, message);
    }
    mUIImplementation.updateView(tag, className, props);
  }
  // ......
}

6.2 方案说明

参照

本质上,bundle less方案或者说harmony的import 模块化方案就是用来实现按需加载的,同时由于rn涉及到了es module和common js 模块,故二者都需要要支持。但由于,harmony的模块化只是文件级的模块,故为了满足功能场景的模块,如ui模块,js框架模块等等,rn for harmony的实现,需要各个模块相互解耦,否则即使harmony原生支持文件级模块解耦,也无法实现rn懒加载相应的能力。

这里可以参考angular等业界著名js框架的实现,具体通过以下三方JS库实现在harmony上的模块化方案。具体通过typedi实现类似javax.inject.Provider的功能,通过reflect-metadata实现类似java.lang.annotation,其他的元编程能力,使用ts-node的compiler api,这个和arkui的@Prop一样的原理。

typedi

reflect-metadata

ts-node

6.3 需求描述

目前看主要是RN团队的工作,harmony理论上没有额外需求(需要进一步原型确认) 这里不包括上述提到的三方库的适配实现。

  • RN团队
SR需求 领域 工作量
基于依赖注入和装饰器元编程能力实现模块化基座和管理 RN团队 2人月
支持关键模块的可配置的预加载 RN团队 2人月
模块化支持cache机制 RN团队 2人月
功能代码的工程级别解耦 RN团队 2人月
自定义模块化功能(如@ReactMethod,@ReactProp等,模块注册能力如ReactPackage等) RN团队 2人月
支持模块按需发布到npm市场功能 RN团队 2人月
打通和react native前端框架的对接 RN团队 2人月
  • harmony(需要进一步原型验证)
SR需求 领域 工作量
元编程能力暴露(使用方舟的前提下) 方舟编译器

7. UI模块

涉及基础部分,如异步布局,ui解析,ui自定义实现(涉及绘制,布局,功能增加等). 相关介绍如下:

以及事件,手势,动画等等,相关介绍如下:

7.1 基本介绍

7.1.2 基础部分分析

对RN应用的任意一个UI控件/组件,RN框架最终都会创建一个原生的UI控件/组件,具体如下表格(来自RN官网)。

RN组件和原生组件关系 但是由于RN的行为和OS组件的行为(如布局能力,组件属性)存在一定的gap:可能是有原生没有这个能力(如felx布局),也可能是由于原生的行为和rn存在差异(如事件冒泡)等。故RN(新架构有个实验特效:rn canvas提供类似flutter彻底的自渲染能力)只是没有图形的能力,但UI框架的能力基本都具备,具体如:UI事件管理(包括自定义事件dispatch等),hittest和冒泡,基于yoga的异步布局模型,甚至某些组件(如Image)还包括draw能力等等。具体实现如下图,即在js的组件和原生组件之间,rn提供了一个叫shadowNode的功能,本质上类似arkui的render组件,用于完成前述述gap能力。具体通过

RN for android的ui模块整体介绍

RN for android ui架构

RN for android ui架构关键类

进一步,如前所述,rn本身么有图形能力,故还需要依赖os的原生ui能力。这里包括,几个关键点:

  • 自定义ui组件能力(增加或覆盖view的通用能力)
  • vSync信号
  • ui调度pipeline

这里主要以Image,Text,Scroll三种UI组件为例介绍RN对系统原生UI的依赖。

以Image为例,以下几个回调是Harmony么有或者存在差异的, RN Image官网 OH Gitlee

onLoad onError onLoadEnd onLoadStart onPartialLoad onProgress progressiveRenderingEnabled

此外,上述各个过程都可以配置额外的place holder,或者在加载个某个过程做特殊的处理,如高斯模糊,如下 enter description here react通过android的自定义ui能力 + fresco实现上述场景,继承关系具体如下: enter description here

本质上理解为ReactImageVIew增加或覆盖ImagView的通用能力,并最终用于ui显示和事件交互等。

事件交互涉及的几个关键代码如下

public class ReactImageManager extends SimpleViewManager<ReactImageView> {
    @ReactProp(name = "shouldNotifyLoadEvents")
      public void setLoadHandlersRegistered(ReactImageView view, boolean shouldNotifyLoadEvents) {
   view.setShouldNotifyLoadEvents(shouldNotifyLoadEvents);
  }
}

public class ReactImageView extends GenericDraweeView {
 public void setShouldNotifyLoadEvents(boolean shouldNotify) {
    // Skip update if shouldNotify is already in sync with the download listener
    if (shouldNotify == (mDownloadListener != null)) {
      return;
    }

    if (!shouldNotify) {
      mDownloadListener = null;
    } else {
      final EventDispatcher mEventDispatcher =
          UIManagerHelper.getEventDispatcherForReactTag((ReactContext) getContext(), getId());

      mDownloadListener =
          new ReactImageDownloadListener<ImageInfo>() {
            @Override
            public void onProgressChange(int loaded, int total) {
              // TODO: Somehow get image size and convert `loaded` and `total` to image bytes.
              mEventDispatcher.dispatchEvent(
                  ImageLoadEvent.createProgressEvent(
                      UIManagerHelper.getSurfaceId(ReactImageView.this),
                      getId(),
                      mImageSource.getSource(),
                      loaded,
                      total));
            }

            @Override
            public void onSubmit(String id, Object callerContext) {
              mEventDispatcher.dispatchEvent(
                  ImageLoadEvent.createLoadStartEvent(
                      UIManagerHelper.getSurfaceId(ReactImageView.this), getId()));
            }

            @Override
            public void onFinalImageSet(
                String id, @Nullable final ImageInfo imageInfo, @Nullable Animatable animatable) {
              if (imageInfo != null) {
                mEventDispatcher.dispatchEvent(
                    ImageLoadEvent.createLoadEvent(
                        UIManagerHelper.getSurfaceId(ReactImageView.this),
                        getId(),
                        mImageSource.getSource(),
                        imageInfo.getWidth(),
                        imageInfo.getHeight()));
                mEventDispatcher.dispatchEvent(
                    ImageLoadEvent.createLoadEndEvent(
                        UIManagerHelper.getSurfaceId(ReactImageView.this), getId()));
              }
            }

            @Override
            public void onFailure(String id, Throwable throwable) {
              mEventDispatcher.dispatchEvent(
                  ImageLoadEvent.createErrorEvent(
                      UIManagerHelper.getSurfaceId(ReactImageView.this), getId(), throwable));
            }
          };
    }

    mIsDirty = true;
  }
}

ui显示涉及的关键的几个代码如下

  
  public class ReactImageManager extends SimpleViewManager<ReactImageView> {
    @ReactProp(name = "loadingIndicatorSrc")
    public void setLoadingIndicatorSource(ReactImageView view, @Nullable String source) {
      view.setLoadingIndicatorSource(source);
    }
  }
  
  public class ReactImageView extends GenericDraweeView {
    public void setLoadingIndicatorSource(@Nullable String name) {
      Drawable drawable =
        ResourceDrawableIdHelper.getInstance().getResourceDrawable(getContext(), name);
      Drawable newLoadingIndicatorSource =
        drawable != null ? (Drawable) new AutoRotateDrawable(drawable, 1000) : null;
      if (!Objects.equal(mLoadingImageDrawable, newLoadingIndicatorSource)) {
      mLoadingImageDrawable = newLoadingIndicatorSource;
      mIsDirty = true;
     }
    }
	
	  @Override
	  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		if (w > 0 && h > 0) {
		  mIsDirty = mIsDirty || hasMultipleSources() || isTiled();
		  maybeUpdateView();
		}
	  }
  
    public void maybeUpdateView() { //

      GenericDraweeHierarchy hierarchy = getHierarchy();
       if (mDefaultImageDrawable != null) {
         hierarchy.setPlaceholderImage(mDefaultImageDrawable, mScaleType);
       }

      if (mLoadingImageDrawable != null) {
        hierarchy.setPlaceholderImage(mLoadingImageDrawable, ScalingUtils.ScaleType.CENTER);
      }
    }
   }

以上主要涉及绘制和事件,自定义方法;除此之外,还有布局相关的实现,以scollView为例。 RN scroll的详细接口说明见如下:

官网:ScrollView 三方:React-Native 之 ScrollView使用

这里依赖android提供的自定义布局能力,即measure设置大小和layout设置位置,以及通过requestLayout接口通知android触发ui渲染流程。 具体代码如下,NativeViewHierarchyManager 是由shadownode通过yoga在native module线程进行完异步布局后通过线程通信能力最终交到ui线程触发的

@NotThreadSafe
public class NativeViewHierarchyManager {
  public synchronized void updateLayout(
      int parentTag, int tag, int x, int y, int width, int height) {

    try {
      View viewToUpdate = resolveView(tag);

      viewToUpdate.measure(
          View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
          View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));

      ViewParent parent = viewToUpdate.getParent();
      if (parent instanceof RootView) {
        parent.requestLayout();
      }

      // Check if the parent of the view has to layout the view, or the child has to lay itself out.
      if (!mRootTags.get(parentTag)) {
        ViewManager parentViewManager = mTagsToViewManagers.get(parentTag);
        IViewManagerWithChildren parentViewManagerWithChildren;
        if (parentViewManager instanceof IViewManagerWithChildren) {
          parentViewManagerWithChildren = (IViewManagerWithChildren) parentViewManager;
        } else {
          throw new IllegalViewOperationException(
              "Trying to use view with tag "
                  + parentTag
                  + " as a parent, but its Manager doesn't implement IViewManagerWithChildren");
        }
        if (parentViewManagerWithChildren != null
            && !parentViewManagerWithChildren.needsCustomLayoutForChildren()) {
          updateLayout(viewToUpdate, x, y, width, height);
        }
      } else {
        updateLayout(viewToUpdate, x, y, width, height);
      }
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_VIEW);
    }
  }
  
 private void updateLayout(View viewToUpdate, int x, int y, int width, int height) {
    if (mLayoutAnimationEnabled && mLayoutAnimator.shouldAnimateLayout(viewToUpdate)) {
      mLayoutAnimator.applyLayoutUpdate(viewToUpdate, x, y, width, height);
    } else {
      viewToUpdate.layout(x, y, x + width, y + height);
    }
  }
}

此外,为了避免使用yoga的flex布局算法,而不是scollview本身的能力,需要scollview的onlayout能力disable调

public class ReactScrollView extends ScrollView
    implements ReactClippingViewGroup,
        ViewGroup.OnHierarchyChangeListener,
        View.OnLayoutChangeListener,
        FabricViewStateManager.HasFabricViewStateManager,
        ReactOverflowViewWithInset,
        HasScrollState,
        HasFlingAnimator,
        HasScrollEventThrottle {
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    MeasureSpecAssertions.assertExplicitMeasureSpec(widthMeasureSpec, heightMeasureSpec);

    setMeasuredDimension(
        MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    // Call with the present values in order to re-layout if necessary
    // If a "pending" content offset value has been set, we restore that value.
    // Upon call to scrollTo, the "pending" values will be re-set.
    int scrollToX =
        pendingContentOffsetX != UNSET_CONTENT_OFFSET ? pendingContentOffsetX : getScrollX();
    int scrollToY =
        pendingContentOffsetY != UNSET_CONTENT_OFFSET ? pendingContentOffsetY : getScrollY();
    scrollTo(scrollToX, scrollToY);
    ReactScrollViewHelper.emitLayoutEvent(this);
  }
}

7.1.2 事件,手势,动画

后续分析(待补)

7.2 方案说明

总体follow android或者ios的设计思路即可,只不过由于harmony UI是声明式,故重点需要做一些声明式的转换即可。其中,基于状态管理实现rn组件和harmony原生组件的映射,直接使用百度鸿蒙化的思路即可,这里不赘述。

7.3 需求描述

  • RN
SR需求描述 领域 工作量
ui线程管理和消息通信管理, RN 2人月
基于上述机制实现ui创建,属性更新,ui结构管理 RN 2人月
ui 控件级按需加载机制Viewmanager RN 2人月
基于状态管理实现rn组件和harmony原生组件的映射 RN 2人月
各种控件的实现,包括图片,文字,scoll等rn涉及的所有ui能力 RN 15人月
ui异步布局机制,包括shadownode,以及跨语言调用到yoga相关能力,以及跨线程通信 RN 2人月
yoga迁移 RN 2人月
yoga napi扩展 RN 3人月
ui事件机制,包括事件管理,冒泡机制,hittes,派发触发事件监听器等 RN 2人月
ui 层级结构优化 RN 2人月
rootview实现,包括事件派发等等 RN 2人月
rn自定义组件支持 RN 3人月
  • ArkUI

理论上,所有的特性参数shadownode都有,故不存在子组件影响父组件布局的问题

SR描述 android 实现方式 harmony实现方式(spec待定) 工作量
复用组件基础能力+ 部分能力覆盖 通过继承view + override方法实现 @Extend @Override + $.属性方法 3人月
复用组件基础能力,功能增加 通过继承view + 增加方法实现 @Prop(true) 3人月
覆盖内置容器组件的布局算法 view.onLayout + override方法实现 @Extend @Override + onLayout 2人月
覆盖内置容器组件的绘制算法 view.onDraw + override方法实现 @Extend @Override + onDraw 2人月
实现自定义容器组件的布局算法 view.onLayout @Component(true) onLayout 2人月
配置ui组件(包括原生和自定义)的layout相关属性(位置,大小等) view.layout 通过通用方法size/postion配置(待验证) 3人月
dirty组件,触发layout view.requestLayout 修改状态变量(待验证) 1人月
dirty组件,触发draw view.invalid 修改状态变量(待验证) 1人月
自定义事件 view.dispatchEvent 支持自定义派发arkui事件给rn框架,其他事件管理由rn内部实现 3人月
vSync信号回调注册 Choreographer.FrameCallback 图形增加类似机制 1人月
自定义动效机制实现 ?人月

8. JS 框架模块

官网介绍:JavaScriptRuntime

RN本质上只是一个JS开发框架,不具备完整的系统能力,换句话说,其对应的能力都要依赖OS原生能力的实现。react原生依赖浏览器能力,即通过dom api和 w3c web api实现对浏览器能力的对接。而RN要依赖OS原生能力的实现,RN的JS框架主要包括2部分:即React 和react Native部分,两者都是基于flow.JS实现,其中react Native JS就是对平台能力的封装实现。

react native js框架 这里主要完成对os平台的支持,相关工作量主要包在工具链,模块化、api、UI能力中,这里不单独赘述。

9. API 模块

官网介绍,如: API 非标准es API,如 Timer Fetch

9.1 基本介绍

react API分为两部分,原生支持的API (~30个大类,~200个细类API)和四方基于扩展机制通过实现API

9.2 方案说明

实现这里依赖JS引擎模块和工具链模块的决策。

js引擎 \ 工具链 现有rn方案 融合方案
Hermes 方案1. Hermes在global挂载对应能力 + JSI/NAPI 方案2. import + JSI/NAPI
ArkJS引擎 方案3. ts在方舟global挂载对应能力 方案4. import

9.3 需求描述

原生支持部分

  • RN
方案 领域 工作量
方案1 + 扩展机制 RN 30人月
方案2 + 扩展机制 RN 30人月
方案3+ 扩展机制 RN 20人月
方案4+ 扩展机制 RN 20人月

10. 调试和性能模块

官网介绍:Debug 官网介绍:Trace 官网介绍:Profile 官网介绍:性能 三方调试:flipper 待补

http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.helloworld&modulesOnly=false&runModule=true

D:\development\sourcecode\reactnative\master\react-native\index.js

{
    "entryFile": "D:\\\\development\\\\sourcecode\\\\reactnative\\\\master\\\\react-native\\\\index.js",
    "options": {
        "customResolverOptions": {},
        "customTransformOptions": {},
        "dev": true,
        "experimentalImportBundleSupport": false,
        "experimentalImportSupport": false,
        "hot": true,
        "minify": false,
        "platform": "android",
        "runtimeBytecodeVersion": null,
        "shallow": false,
        "type": "module",
        "unstable_allowRequireContext": false,
        "unstable_transformProfile": "default"
    }
}

11. 其他模块

官网介绍:安全 官网介绍:无障碍 官网介绍:国际化 待补

12. 各种小技巧

1. 编译时工具链调试技巧

在react natve的VSC工程根目录,建立.vscode 目录,并在其中建立lauch.json文件, 以调试yarn android命令为例其中内容如下。如提示此系统上禁止运行脚本,请将vscode以管理员方式运行,并在terminal下执行命令:set-ExecutionPolicy RemoteSigned

{
    "configurations": [
      {
        "command": "yarn android",
        "name": "Run yarn android",
        "request": "launch",
        "type": "node-terminal"
      }
    ]
}

2. react native js框架调试技巧

官网:Debugging 四方:ReactNative Debugger

空文件

简介

react native for harmony 展开 收起
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
JavaScript
1
https://gitee.com/jiandong001/react-native-for-harmony.git
git@gitee.com:jiandong001/react-native-for-harmony.git
jiandong001
react-native-for-harmony
react-native-for-harmony
main

搜索帮助