diff --git a/zh-cn/environment.md b/zh-cn/environment.md index 5dbf58e4ea1eaf0cef47e5ed07276354ffa4b5ba..a9a1944c887af861d27e1342ad39f75fc4baf10f 100644 --- a/zh-cn/environment.md +++ b/zh-cn/environment.md @@ -14,6 +14,8 @@ 官网地址: +> [!WARNING] Node 的版本应大于等于 16,推荐 18 + ### 验证安装 输入 node -v,npm -v 输出版本就是安装成功了 @@ -38,42 +40,25 @@ npm install -g yarn 安装完 yarn 之后就可以用 yarn 代替 npm 了,例如用 `yarn` 代替 `npm install` 命令,用 `yarn add 某第三方库名` 代替 `npm install 某第三方库名`。 -## 搭建 ios 环境 +## 搭建 iOS 环境 -ios 环境主要用于效果比对和 RN Demo 的开发。因为 arkui 对标 swift,**rnoh 也对标 ios** -,推荐搭建 ios 环境,方便对齐效果。也可以在 ios 环境可以运行 codegen 生成代码。 +iOS 环境主要用于效果比对和 React Native Demo 的开发。因为 ArkUI 对标 SwiftUI,RNOH 也对标 iOS,推荐搭建 iOS 环境,方便对齐效果。也可以在 iOS 环境使用 Codegen 生成代码。 > [!WARNING] 请先参考官方的 React-Naitve + ios 的环境搭建文档 [React Native Step Up](https://www.reactnative.cn/docs/environment-setup)。 ## 搭建 Android 环境 -搭建 Android 环境,用于效果比对和 RN Demo 的开发和 codegen 生成代码。 +搭建 Android 环境,用于效果比对和 React Native Demo 的开发和使用 Codegen 生成代码。 > [!WARNING] 请先参考官方的 React-Naitve + Android 的环境搭建文档 [React Native Step Up](https://www.reactnative.cn/docs/environment-setup) -将 ios / Android 环境搭建好,并成功运行 React-Native 官方给定的 demo 后再进行下一步。 +将 iOS / Android 环境搭建好,并成功运行 React-Native 官方给定的 demo 后再进行下一步。 ## 搭建 HarmonyOS 环境 ### IDE 和手机版本 -HarmonyOS 环境需要注意 IDE 版本、OpenHarmony SDK 版本和手机版本是否符合要求。 - -- 2023.10.30 版本: - -DevEco Studio 版本:4.0.3.601 - -OpenHarmony(API10): 4.0.10.11 - -工程机版本:NOH-AN00 204.0.0.65(SP4C00E70R1P12) - -- 2023.12.30 版本: - -DevEco Studio 版本:4.1.3.401 - -OpenHarmony(API11): 4.1.0.36 - -工程机版本:NOH-AN00 204.1.0.60(SP10C00E60R1P17) +HarmonyOS 环境需要注意 IDE 版本、SDK 版本和手机版本是否符合要求。具体下载地址请咨询 RNOH 接口人。 ### 在 mac 上安装鸿蒙模拟器 @@ -85,25 +70,13 @@ OpenHarmony(API11): 4.1.0.36 RNOH 包含的内容: -1. react-native-harmony:react-native 的 Hamrony 拓展; -2. react-native-harmony-cli:react-native-cli 的 Hamrony 拓展; -3. tester:配置好 HarmonyOS 支持的 React-Native 项目; - -- 绿区: +1. react-native-harmony:react-native 的 HarmonyOS 拓展; +2. react-native-harmony-cli:react-native-cli 的 HarmonyOS 拓展; +3. tester:配置好的 HarmonyOS 支持的 React-Native 项目; -拉取[ReactNative_OpenHarmony](https://codehub-g.huawei.com/ReactNativeOpenHarmony/rnoh/home)项目 +拉取[rnoh](https://github.com/react-native-openharmony/rnoh)项目,需要授权账号。 -- 蓝区: - -拉取[rnoh](https://github.com/react-native-openharmony/rnoh)项目 - -按需选择分支: - -`swm_main` / `main` 是SWM团队的主干分支; - -`master` / `master_green` 是HUAWEI团队的主干分支; - -`xxx_third_party` 是在 `xxx` 的基础上,加入了 React-Native 三方库。 +选择 `main` 分支 ## 创建新项目 @@ -160,7 +133,6 @@ react-native run-android 在 `tester` 目录下,运行: ```sh -npm run preinstall npm i ``` @@ -176,6 +148,12 @@ npm run dev 生成的 bundle 会放在 `tester/harmony/entry/src/main/resources/rawfile` 目录下。 +或者使用热更新,使用方式和 Android/iOS 一致: + +```sh +npm run start +``` + - 运行 打开 DevEco Studio,Build 并运行 entry 模块 diff --git a/zh-cn/fabric.md b/zh-cn/fabric.md index f570624f6c98bac7eb036ec8220c789fff89dc09..75b3cafd730b5475075a93909e074c3649313fff 100644 --- a/zh-cn/fabric.md +++ b/zh-cn/fabric.md @@ -8,8 +8,6 @@ Fabric 组件是一种使用 Fabric 渲染器渲染并展示在屏幕上的 UI 在开发 Fabric 组件前,需要先创建一个 JavaScript 接口描述文件。之后 Codegen 会根据这个文件创建一些 C++ 脚手架代码,用于将部分组件逻辑(比如调用原生平台接口能力)与 React Native 结合起来。C++ 代码在各个平台都是一样的,只要组件能够与生成的 C++ 代码连接起来,就可以导入到 App 并运行。 -因为 HarmonyOS 平台暂时还没有 codegen 工具,所以我们需要使用 Android 平台的 codegen 来生成相关的 C++ 代码,然后复制到 HarmonyOS 平台使用。 - ## 如何创建 Fabric 组件 若要创建一个 Fabric 组件,需要遵循以下步骤: @@ -26,6 +24,7 @@ Fabric 组件是一种使用 Fabric 渲染器渲染并展示在屏幕上的 UI ``` . +├── MyApp └── RTNCenteredText ├── android(Android 的原生实现代码) ├── ios(iOS 的原生实现代码) @@ -39,8 +38,10 @@ Fabric 组件是一种使用 Fabric 渲染器渲染并展示在屏幕上的 UI 对于声明类型的代码文件必须满足以下两点要求: -文件必须使用 `NativeComponent` 命名,在使用 Flow 时,以 `.js` 或 `.jsx` 为后缀名;在使用 Typescript 时,以 `.ts` 或 `.tsx` 为后缀名。Codegen 只会找到匹配这些命名规则的文件; -代码中必须要输出 HostComponent 对象。 +1. 文件必须使用 `NativeComponent` 命名,在使用 Flow 时,以 `.js` 或 `.jsx` 为后缀名;在使用 Typescript 时,以 `.ts` 或 `.tsx` 为后缀名。Codegen 只会找到匹配这些命名规则的文件; + +2. 代码中必须要输出 HostComponent 对象。 + 以下是使用 Flow 和 TypeScript 声明的 RTNCenteredText 组件。在 `js` 目录中,创建一个命名为 `RTNCenteredText` 并带有相应后缀名的文件。 @@ -100,17 +101,17 @@ export default codegenNativeComponent( #### Shared -shared 是 package.json 文件中的一个配置项,它将在 yarn 安装模块时被调用。请在 `RTNCenteredText` 的根目录创建 `package.json` 文件。 +shared 是 package.json 文件中的一个配置项,它将在 yarn/npm 安装模块时被调用。请在 `RTNCenteredText` 的根目录创建 `package.json` 文件。 ```json { "name": "rtn-centered-text", "version": "0.0.1", "description": "Showcase a Fabric component with a centered text", - "react-native": "js/index", - "source": "js/index", + "react-native": "src/index", + "source": "src/index", "files": [ - "js", + "src", "android", "ios", "harmony", @@ -134,10 +135,17 @@ shared 是 package.json 文件中的一个配置项,它将在 yarn 安装模 "react": "*", "react-native": "*" }, + "harmony": { + "codegenConfig": { + "specPaths": [ + "./src" + ] + } + }, "codegenConfig": { "name": "RTNCenteredTextSpecs", "type": "components", - "jsSrcsDir": "js" + "jsSrcsDir": "src" } } ``` @@ -279,20 +287,75 @@ ReactPackage 接口的用途是让 React Native 为使用 App 中的 ViewManager Codegen 会在 App 编译的时候自动运行。 -#### HarmonyOS +#### HarmonyOS (ArkTS Fabric) + +> [!WARNING] 接入 codegen 之后,同一个模块中 ArkTS 版本和 CAPI 版本的 Fabric 无法共存,请先选择好实现方式 + +HarmonyOS 需要在 RN 工程中通过运行脚本来执行 Codegen。 + +打开 RN 工程下的 package.json,如 `MyApp/package.json`,添加: + +```json +{ + ... + "scripts": { + ... + "codegen": "react-native codegen-harmony --rnoh-module-path ./harmony/react_native_openharmony" + }, + ... +} +``` + +> codegen-harmony 参数介绍: + +1. --rnoh-module-path: 指定 @rnoh/react-native-openharmony 模块的相对路径,用于存储生成的 ts 文件;如果使用 har 包引入 rnoh 模块,则需要指向:./harmony/entry/oh_modules/@rnoh/react-native-openharmony" -HarmonyOS 平台暂时还没有 Codegen,所以我们需要手动运行 Android 的 Codegen,然后把生成的代码复制过来使用。 +2. --cpp-output-path: 指定用于存储生成的 C++ 文件的输出目录的相对路径,默认 ./harmony/entry/src/main/cpp/generated; + +3. --project-root-path: 包根目录的相对路径。 + +#### HarmonyOS (CAPI Fabric) + +> [!WARNING] 接入 codegen 之后,同一个模块中 ArkTS 版本和 CAPI 版本的 Fabric 无法共存,请先选择好实现方式 + +目前 Codegen 暂时不支持构造 CAPI 版本的 Fabric 模块,,所以我们需要手动运行 Android 的 Codegen,然后把生成的代码复制过来使用。 > [!WARNING] 请务必先把 Android 的 Codegen 配置好再执行以下操作 +删除 package.json 中的 harmony.codegenConfig 字段 + +```diff +{ + ... + "harmony": { +- "codegenConfig": { +- "specPaths": [ +- "./src" +- ] +- } + }, +} +``` + 首先我们需要一个 React-Native App 来执行 Codegen,假设 App 的目录是和 当前目录平级的 `MyApp`,执行以下命令来创建一个 Gradle 任务来执行 Codegen。 > [!tip] 在运行 Codegen 之前,您需要在 Android 中的 App 启动新架构。您可以通过修改 gradle.properties 文件中的 newArchEnabled 属性,将 false 改为 true。 ```bash -cd MyApp -yarn add ../RTNCenteredText +// 进入模块工程 +cd RTNCenteredText + +// 打包模块 +npm pack + +// 进入 App 工程 +cd ../MyApp + +// 本地路径安装模块 +npm i file:../RTNCenteredText/rtn-centered-text-0.0.1.tgz + cd android + ./gradlew generateCodegenArtifactsFromSchema ``` @@ -321,23 +384,30 @@ codegen │ ├── EventEmitters.h │ ├── Props.cpp │ ├── Props.h +│ ├── RTNCenteredTextSpecsJSI-generated.cpp +│ ├── RTNCenteredTextSpecsJSI.h │ ├── ShadowNodes.cpp -│ └── ShadowNodes.h +│ ├── ShadowNodes.h +│ ├── States.cpp +│ └── States.h └── schema.json ``` -`codegen/jni/react/renderer/components/RTNCenteredText` 目录下的代码是 HarmonyOS 需要的。将这些代码复制到 `harmony/rtn-centered-text/src/main/cpp` 文件夹下,并修改一下各文件 "include" 的路径。 +`codegen/jni/react/renderer/components/RTNCenteredText` 目录下的代码是 HarmonyOS 需要的。(RTNCenteredTextSpecsJSI-generated.cpp、RTNCenteredTextSpecsJSI.h除外) + +将这些代码文件复制到 `harmony/rtn-centered-text/src/main/cpp` 文件夹下,并 **修改一下各文件 "include" 的路径**。 如 `ComponentDescriptor.h` ```diff ... +- #include + #include "ShadowNodes.h" #include ... ``` -然后在同级目录创建 `CenteredTextJSIBinder.h`, `CenteredTextNapiBinder.h`, `CenteredTextPackage.h` 等三个文件,目录结构如下 +然后在同级目录创建 `CMakeLists.txt`,`RTNCenteredTextJSIBinder.h`,`RTNCenteredTextPackage.h`,目录结构如下 ```md harmony @@ -353,16 +423,14 @@ harmony │ │ ├── Props.h │ │ ├── ShadowNodes.cpp │ │ ├── ShadowNodes.h - │ │ ├── CenteredTextJSIBinder.h - │ │ ├── CenteredTextNapiBinder.h - │ │ └── CenteredTextPackage.h - │ ├──ets + │ │ ├── States.cpp + │ │ ├── States.h + │ │ ├── RTNCenteredTextJSIBinder.h + │ │ └── RTNCenteredTextPackage.h │ └── modules.json5 ├── build-profile.json5 ├── hvigorfile.ts - ├── index.ets - ├── oh-package.json5 - └── ts.ts + └── oh-package.json5 ``` @@ -374,24 +442,24 @@ harmony cmake_minimum_required(VERSION 3.13) set(CMAKE_VERBOSE_MAKEFILE on) -file(GLOB rnoh_centered_text_SRC CONFIGURE_DEPENDS *.cpp) -add_library(rnoh_centered_text SHARED ${rnoh_centered_text_SRC}) -target_include_directories(rnoh_centered_text PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(rnoh_centered_text PUBLIC rnoh) +file(GLOB rtn_centered_text_SRC CONFIGURE_DEPENDS *.cpp) +add_library(rtn_centered_text SHARED ${rtn_centered_text_SRC}) +target_include_directories(rtn_centered_text PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(rtn_centered_text PUBLIC rnoh) ``` -#### **CenteredTextJSIBinder.h** +#### **RTNCenteredTextJSIBinder.h** ```cpp #include "RNOHCorePackage/ComponentBinders/ViewComponentJSIBinder.h" namespace rnoh { -class CenteredTextJSIBinder : public ViewComponentJSIBinder { +class RTNCenteredTextJSIBinder : public ViewComponentJSIBinder { facebook::jsi::Object createNativeProps(facebook::jsi::Runtime &rt) override { auto object = ViewComponentJSIBinder::createNativeProps(rt); object.setProperty(rt, "text", "string"); @@ -407,47 +475,18 @@ JSI Binder 的作用是桥接 JS 和 C++,将属性从 JS 端传递到 C++ 端 -#### **CenteredTextNapiBinder.h** - -```cpp -#include "RNOHCorePackage/ComponentBinders/ViewComponentNapiBinder.h" -#include "Props.h" - -namespace rnoh { - -class CenteredTextNapiBinder : public ViewComponentNapiBinder { -public: - napi_value createProps(napi_env env, facebook::react::ShadowView const shadowView) override { - napi_value napiViewProps = ViewComponentNapiBinder::createProps(env, shadowView); - if (auto props = std::dynamic_pointer_cast(shadowView.props)) { - return ArkJS(env) - .getObjectBuilder(napiViewProps) - .addProperty("text", props->text) - .build(); - } - return napiViewProps; - }; -}; -} //namespace rnoh -``` - - - -Napi Binder 的作用是桥接 C++ 和 ArkTs ,将属性从 C++ 端传递到 ArkTs 端。 - -#### **CenteredTextPackage.h** +#### **RTNCenteredTextPackage.h** ```cpp #include "RNOH/Package.h" -#include "ComponentDescriptor.h" -#include "CenteredTextJSIBinder.h" -#include "CenteredTextNapiBinder.h" +#include "ComponentDescriptors.h" +#include "RTNCenteredTextJSIBinder.h" namespace rnoh { -class CenteredTextPackage : public Package { +class RTNCenteredTextPackage : public Package { public: CenteredTextPackage(Package::Context ctx): Package(ctx) {} @@ -455,12 +494,8 @@ public: return {facebook::react::concreteComponentDescriptorProvider()}; } - ComponentNapiBinderByString createComponentNapiBinderByName() override { - return {{"RTNCenteredText", std::make_shared()}}; - } - ComponentJSIBinderByString createComponentJSIBinderByName() override { - return {{"RTNCenteredText", std::make_shared()}}; + return {{"RTNCenteredText", std::make_shared()}}; } }; } // namespace rnoh @@ -642,40 +677,33 @@ public class RTNCenteredTextPackage implements ReactPackage { 新增的代码实例化了一个 RTNCenteredTextManager 对象,用于让 React Natve 运行时渲染 Fabric 组件。 -#### HarmonyOS +#### HarmonyOS (ArkTS Fabric) -HarmonyOS 平台中 Fabric 组件的原生代码必须包含以下三个部分: +HarmonyOS 平台中 ArkTS 版本的 Fabric 组件的原生代码必须包含以下三个部分: 1. 创建用于实现组件的 RTNCenteredText.ets -2. 创建 index.ets -3. 修改 oh-package.json5,hvigorfile.ts,module.json5 +2. 创建 RTNCenteredTextPackage.ts +3. 创建用于导出模块的 index.ets 和 ts.ts +4. 修改 oh-package.json5,hvigorfile.ts,module.json5 -HarmonyOS 第三方库目录文件结构应为如下: +> [!TIP] 可以在 DevEco Studio 中通过 File -> New -> Module.. -> Static Lirbrary 创建空壳模块,以此为基础修改文件内容 + +HarmonyOS 原生代码文件结构应为如下: ```md harmony └── rtn-centered-text ├── src │ └── main - │ ├── cpp - │ │ ├── CMakeLists.txt - │ │ ├── ComponentDescriptors.h - │ │ ├── EventEmitters.cpp - │ │ ├── EventEmitters.h - │ │ ├── Props.cpp - │ │ ├── Props.h - │ │ ├── ShadowNodes.cpp - │ │ ├── ShadowNodes.h - │ │ ├── CenteredTextJSIBinder.h - │ │ ├── CenteredTextNapiBinder.h - │ │ └── CenteredTextPackage.h │ ├──ets + | | ├── RTNCenteredTextPackage.ts │ │ └── RTNCenteredText.ets │ └── modules.json5 ├── build-profile.json5 ├── hvigorfile.ts + ├── index.ets ├── oh-package.json5 - └── index.ets + └── ts.ts ``` @@ -683,47 +711,44 @@ harmony #### **RTNCenteredText.ets** ```ts -import { Descriptor, ComponentBuilderContext, ViewBaseProps, Tag } from 'rnoh'; -import { RNComponentFactory, RNOHContext, RNViewBase } from 'rnoh' - -export const CENTERED_TEXT_TYPE: string = "RTNCenteredText" - -export type CenteredTextProps = ViewBaseProps & { - text: string -} - -export type CenteredTextDescriptor = Descriptor<"RTNCenteredText", ViewBaseProps> - +import { RNComponentContext, RNViewBase } from '@rnoh/react-native-openharmony'; +// import codegen 生成的内容 +import { RNC } from "@rnoh/react-native-openharmony/generated"; @Component export struct RTNCenteredText { - ctx!: RNOHContext - tag: number = 0 - @BuilderParam buildCustomComponent: (componentBuilderContext: ComponentBuilderContext) => void - @State descriptor: CenteredTextDescriptor = {} as CenteredTextDescriptor - private unregisterDescriptorChangesListener?: () => void = undefined + public static readonly NAME = RNC.RTNCenteredText.NAME + public ctx!: RNComponentContext + public tag: number = 0 + @State private descriptorWrapper: RNC.RTNCenteredText.DescriptorWrapper = {} as RNC.RTNCenteredText.DescriptorWrapper + private eventEmitter: RNC.RTNCenteredText.EventEmitter | undefined = undefined + private cleanUpCallbacks: (() => void)[] = [] aboutToAppear() { - this.descriptor = this.ctx.descriptorRegistry.getDescriptor(this.tag) - this.unregisterDescriptorChangesListener = this.ctx.descriptorRegistry.subscribeToDescriptorChanges(this.tag, - (newDescriptor) => { - this.descriptor = (newDescriptor as CenteredTextDescriptor) + this.eventEmitter = new RNC.RTNCenteredText.EventEmitter(this.ctx.rnInstance, this.tag) + this.onDescriptorWrapperChange(this.ctx.descriptorRegistry.findDescriptorWrapperByTag(this.tag)!) + this.cleanUpCallbacks.push(this.ctx.descriptorRegistry.subscribeToDescriptorChanges(this.tag, + (_descriptor, newDescriptorWrapper) => { + this.onDescriptorWrapperChange(newDescriptorWrapper! as RNC.RTNCenteredText.DescriptorWrapper) } - ) + )) + } + + private onDescriptorWrapperChange(descriptorWrapper: RNC.RTNCenteredText.DescriptorWrapper) { + this.descriptorWrapper = descriptorWrapper } aboutToDisappear() { - this.unregisterDescriptorChangesListener?.() + this.cleanUpCallbacks.forEach(cb => cb()) } build() { RNViewBase({ ctx: this.ctx, tag: this.tag }) { - Text(this.descriptor.props.text) - .fontColor("red") - .fontSize(12) - .textAlign(TextAlign.Center) - .width("100%") - .height("100%") + Text(this.descriptorWrapper.props.text) + .height("100%") + .width("100%") + .fontSize(30) + .textAlign(TextAlign.Center) } } } @@ -733,19 +758,182 @@ export struct RTNCenteredText { 该部分是 RTNCenteredText 的 HarmonyOS 原生实现。 +创建 `ts.ts` + + + +#### **ts.ts** + +```ts +export * from "./src/main/ets/RTNCenteredTextPackage"; +``` + + + 创建 `index.ets` - + #### **index.ets** ```ts +export * from "./ts"; export * from "./src/main/ets/RTNCenteredText"; ``` -修改 `oh-package.json5`,`hvigorfile.ts`,`module.json5` +#### HarmonyOS (CAPI Fabric) + +HarmonyOS 平台中 CAPI 版本的 Fabric 组件的原生代码必须包含以下部分: + +1. 创建用于实现组件的 RTNCenteredTextComponentInstance.h、RTNCenteredTextComponentInstance.cpp; +2. 创建用于对接 ArkUI的 xxxNode.h、xxxNode.cpp;(若框架已经实现相关的Node,此步可以跳过。本例中需要用到的 TextNode、StackNode 框架已经实现,故无需自行创建) +3. Cpp 脚手架(请看 codegen 章节) +4. 修改 oh-package.json5,hvigorfile.ts,module.json5 + +> [!TIP] 可以在 DevEco Studio 中通过 File -> New -> Module.. -> Static Lirbrary 创建空壳模块,以此为基础修改文件内容 + +HarmonyOS 原生代码文件结构应为如下: + +```md +harmony +└── rtn-centered-text + ├── src + │ └── main + │ ├── cpp + │ │ ├── CMakeLists.txt + │ │ ├── ComponentDescriptors.h + │ │ ├── EventEmitters.cpp + │ │ ├── EventEmitters.h + │ │ ├── Props.cpp + │ │ ├── Props.h + │ │ ├── ShadowNodes.cpp + │ │ ├── ShadowNodes.h + │ │ ├── CenteredTextJSIBinder.h + │ │ ├── CenteredTextPackage.h + │ │ ├── RTNCenteredTextComponentInstance.h + │ │ └── RTNCenteredTextComponentInstance.cpp + │ └── modules.json5 + ├── build-profile.json5 + ├── hvigorfile.ts + └── oh-package.json5 +``` + +创建 `RTNCenteredTextComponentInstance.h` + + + +#### **RTNCenteredTextComponentInstance.h** + +```cpp +#pragma once +#include "ShadowNodes.h" +#include "RNOH/CppComponentInstance.h" +#include "RNOH/arkui/StackNode.h" +#include "RNOH/arkui/TextNode.h" + +namespace rnoh { +class RTNCenteredTextComponentInstance : public CppComponentInstance { +private: + using FragmentTouchTargetByTag = std::unordered_map>; + + TextNode m_textNode{}; + StackNode m_stackNode{}; + +public: + RTNCenteredTextComponentInstance(Context context); + StackNode &getLocalRootArkUINode() override; + +protected: + void onPropsChanged(SharedConcreteProps const &props) override; +}; +} // namespace rnoh +``` + + + +创建 `RTNCenteredTextComponentInstance.cpp` + + + +#### **RTNCenteredTextComponentInstance.cpp** +```cpp +#include "RTNCenteredTextComponentInstance.h" + +namespace rnoh { + +RTNCenteredTextComponentInstance::RTNCenteredTextComponentInstance(Context context) + : CppComponentInstance(std::move(context)) { + m_stackNode.insertChild(m_textNode, 0); +} + +StackNode &RTNCenteredTextComponentInstance::getLocalRootArkUINode() { return m_stackNode; } + +void RTNCenteredTextComponentInstance::onPropsChanged(SharedConcreteProps const &props) { + CppComponentInstance::onPropsChanged(props); + if (props == nullptr) { + return; + } + m_textNode.setTextContent(props->text); + m_textNode.setFontSize(30.0); + m_textNode.setAlignment(ARKUI_ALIGNMENT_CENTER); +} + +} // namespace rnoh +``` + + + +修改 `RTNCenteredTextPackage.h` + + + +#### **RTNCenteredTextPackage.cpp** +```cpp +#include "RNOH/Package.h" +#include "ComponentDescriptors.h" +#include "RTNCenteredTextJSIBinder.h" +#include "RTNCenteredTextComponentInstance.h" + +namespace rnoh { + +class RTNCenteredTextComponentInstanceFactoryDelegate : public ComponentInstanceFactoryDelegate { +public: + using ComponentInstanceFactoryDelegate::ComponentInstanceFactoryDelegate; + + ComponentInstance::Shared create(ComponentInstance::Context ctx) override { + if (ctx.componentName == "RTNCenteredText") { + return std::make_shared(std::move(ctx)); + } + return nullptr; + } +}; + +class RTNCenteredTextPackage : public Package { +public: + RTNCenteredTextPackage(Package::Context ctx): Package(ctx) {} + + ComponentInstanceFactoryDelegate::Shared createComponentInstanceFactoryDelegate() override { + return std::make_shared(); + } + + std::vector createComponentDescriptorProviders() override { + return {facebook::react::concreteComponentDescriptorProvider()}; + } + + ComponentJSIBinderByString createComponentJSIBinderByName() override { + return {{"RTNCenteredText", std::make_shared()}}; + } +}; +} // namespace rnoh +``` + + + +#### HarmonyOS (ArkTS Fabric 和 CAPI Fabric 共有部分) + +修改 `oh-package.json5`,`hvigorfile.ts`,`module.json5`,或自行创建 @@ -753,18 +941,24 @@ export * from "./src/main/ets/RTNCenteredText"; ```json { + "license": "ISC", + "types": "", "devDependencies": { - "rnoh": "file:../rnoh" }, - "name": "rnoh-centered-text", + "name": "rtn-centered-text", + "description": "", "main": "index.ets", - "type": "module" + "version": "0.0.1", + "dependencies": { + "@rnoh/react-native-openharmony": "file:../react_native_openharmony" + } } + ``` - + #### **hvigorfile.ts** @@ -780,12 +974,13 @@ export { harTasks } from "@ohos/hvigor-ohos-plugin"; ```json { - "module": { - "name": "centered_text", - "type": "har", - "deviceType": ["default"] - } + module: { + name: 'centered_text', + type: 'har', + deviceTypes: ['default'], + }, } + ``` @@ -794,14 +989,44 @@ export { harTasks } from "@ohos/hvigor-ohos-plugin"; #### Shared -首先,需要将包含模块的 NPM 包添加到 App。可以使用以下命令执行此操作: +首先,需要将包含模块的 NPM 包添加到 App。请确保 package.json 已经配置安装好以下依赖: + +```json +{ + ... + "dependencies": { + "react-native-harmony": "版本 >= 0.72.15", + ... + }, + "overrides": { + "@react-native/codegen": "0.74.0" + }, + ... +} +``` + +执行以下操作,假设 MyApp 为您的 App 工程路径 ```bash -cd tester -yarn add ../RTNCenteredText +// 进入模块工程 +cd RTNCenteredText + +// 打包模块 +npm pack + +// 进入 App 工程 +cd ../MyApp + +// 本地路径安装模块 +npm i file:../RTNCenteredText/rtn-centered-text-0.0.1.tgz + +// 执行以下命令执行 codegen (HarmonyOS ArkTS Fabric only) + +npm run codegen + ``` -此命令会将 RTNCalculator 模块添加到 App 内的 node_modules 目录。 +此命令会将 RTNCenteredText 模块添加到 App 内的 node_modules 目录。 #### Android @@ -810,106 +1035,154 @@ yarn add ../RTNCenteredText 1. 打开 android/gradle.properties; 2. 滑到文件底部,将 newArchEnabled 的值从 false 修改为 true。 -#### HarmonyOS +#### HarmonyOS(ArkTS Fabric 和 CAPI Fabric 共有部分) > [!tip] 待完善能力:HarmonyOS 平台目前暂时不支持 AutoLink,所以需要自行配置。 首先使用 DevEco Studio 打开 React-Native 项目里的鸿蒙工程 `harmony` +目前 HarmonyOS 工程暂不支持引入工程外的模块,所以需要手动将模块的 HarmonyOS 源码复制到工程内。 + +复制 `RTNCenteredText/harmony/centered_text` 到 `harmony` 工程根目录下。 + +修改 `MyApp/harmony/build-profile.json5`,在 modules 字段添加: + +```json +{ +... + modules: [ + ... + { + name: 'centered_text', + srcPath: './centered_text', + } + ] +} +``` ##### 引入原生端代码 -打开 `entry/oh-package.json5`,添加以下依赖,引入鸿蒙原生端的代码 +打开 `MyApp/harmony/entry/oh-package.json5`,添加以下依赖,引入鸿蒙原生端的代码 ```json "dependencies": { - "rnoh": "file:../rnoh", - "rnoh-centered-text": "file:../../node_modules/RTNCenteredText/harmony/rtn-centered-text" + "@rnoh/react-native-openharmony": "file:../react_native_openharmony", + "rtn-centered-text": "file:../../node_modules/RTNCenteredText/harmony/rtn-centered-text" } ``` -在终端运行以下命令 +点击右上角的 `sync` 按钮同步工程,或在终端运行以下命令 ```bash cd entry -ohpm install --no-link +ohpm install +``` + +#### HarmonyOS(ArkTS Fabric) + +打开 `MyApp/harmony/entry/src/main/ets/pages/Index.ets`,添加: + +```diff +... ++ import { RTNCenteredText } from "rtn-centered-text" + +@Builder +export function buildCustomRNComponent(ctx: ComponentBuilderContext) { + Stack() { + if (ctx.componentName === SampleView.NAME) { + SampleView({ + ctx: ctx.rnComponentContext, + tag: ctx.tag, + }) + } ++ else if (ctx.componentName === RTNCenteredText。NAME) { ++ RTNCenteredText({ ++ ctx: ctx.rnComponentContext, ++ tag: ctx.tag, ++ }) ++ } + ... + } + .position({x: 0, y: 0}) +} +... +``` + +打开 `MyApp/harmony/entry/src/main/ets/RNPackageFactory.ts`,添加: + +```diff +import type {RNPackageContext, RNPackage} from '@rnoh/react-native-openharmony/ts'; +import {SamplePackage} from 'rnoh-sample-package/ts'; ++ import { RTNCenteredTextPackage } from "rtn-centered-text/ts"; + +export function createRNPackages(ctx: RNPackageContext): RNPackage[] { + return [ + new SamplePackage(ctx), ++ new RTNCenteredTextPackage(ctx), + ]; +} ``` -##### 配置 CMakeLists 和引入 CenteredTextPackge +编译、运行即可。 + +#### HarmonyOS(CAPI Fabric) -打开 `entry/src/main/cpp/CMakeLists.txt`,添加: +打开 `MyApp/harmony/entry/src/main/cpp/CMakeLists.txt`,添加: ```diff project(rnapp) cmake_minimum_required(VERSION 3.4.1) +set(CMAKE_SKIP_BUILD_RPATH TRUE) set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}") -set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") +set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules") ++ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp") +set(LOG_VERBOSITY_LEVEL 1) +set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments") +set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie") +set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use +add_compile_definitions(WITH_HITRACE_SYSTRACE) add_subdirectory("${RNOH_CPP_DIR}" ./rn) -# RNOH_BEGIN: add_package_subdirectories +# RNOH_BEGIN: manual_package_linking_1 add_subdirectory("../../../../sample_package/src/main/cpp" ./sample-package) -+ add_subdirectory("${OH_MODULE_DIR}/rnoh-centered-text/src/main/cpp" ./centered-text) -# RNOH_END: add_package_subdirectories ++ add_subdirectory("${OH_MODULES}/rtn-centered-text-capi/src/main/cpp" ./centered-text) +# RNOH_END: manual_package_linking_1 + +file(GLOB GENERATED_CPP_FILES "./generated/*.cpp") add_library(rnoh_app SHARED + ${GENERATED_CPP_FILES} "./PackageProvider.cpp" "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp" ) - target_link_libraries(rnoh_app PUBLIC rnoh) -# RNOH_BEGIN: link_packages +# RNOH_BEGIN: manual_package_linking_2 target_link_libraries(rnoh_app PUBLIC rnoh_sample_package) -+ target_link_libraries(rnoh_app PUBLIC rnoh_centered_text) -# RNOH_END: link_packages ++ target_link_libraries(rnoh_app PUBLIC rtn_centered_text) +# RNOH_END: manual_package_linking_2 ``` -打开 `entry/src/main/cpp/PackageProvider.cpp`,添加: +打开 `MyApp/harmony/entry/src/main/cpp/PackageProvider.cpp`,添加: ```diff #include "RNOH/PackageProvider.h" #include "SamplePackage.h" -+ #include "CenteredTextPackage.h" ++ #include "RTNCenteredTextPackage.h" using namespace rnoh; std::vector> PackageProvider::getPackages(Package::Context ctx) { return { + std::make_shared(ctx), std::make_shared(ctx), -+ std::make_shared(ctx) ++ std::make_shared(ctx), }; } ``` -##### 在 ArkTs 侧引入 CenteredText - -打开 `entry/src/main/ets/pages/Index.ets`,添加: - -```diff -... -import { SampleView, SAMPLE_VIEW_TYPE, PropsDisplayer } from "rnoh-sample-package" -+ import { RTNCenteredText, CENTERED_TEXT_TYPE } from "rnoh-centered-text" - -@Builder -export function CustomComponentBuilder(ctx: ComponentBuilderContext) { - if (ctx.componentName === SAMPLE_VIEW_TYPE) { - SampleView({ - ctx: ctx.rnohContext, - tag: ctx.tag, - buildCustomComponent: CustomComponentBuilder - }) - } -+ else if (ctx.componentName === CENTERED_TEXT_TYPE) { -+ RTNCenteredText({ -+ ctx: ctx.rnohContext, -+ tag: ctx.tag, -+ }) -+ } - ... -} -... -``` +编译、运行即可。 #### JavaScript @@ -918,7 +1191,7 @@ export function CustomComponentBuilder(ctx: ComponentBuilderContext) { 1. 在 js 文件中导入组件。假设要在 App.js 进行导入,需要添加这行代码: ```js -import RTNCenteredText from "rtn-centered-text/js/RTNCenteredTextNativeComponent"; +import RTNCenteredText from "rtn-centered-text/src/RTNCenteredTextNativeComponent"; ``` 2. 接下来,在 React Native 组件里进行调用。调用的语法和其它组件相同: @@ -930,18 +1203,31 @@ import RTNCenteredText from "rtn-centered-text/js/RTNCenteredTextNativeComponent #### **App.js** ```js -// ... other code +/** + * Sample React Native App + * https://github.com/facebook/react-native + * + * @format + * @flow strict-local + */ +import React from 'react'; +import type {Node} from 'react'; +import {SafeAreaView} from 'react-native'; +import RTNCenteredText from 'rtn-centered-text/src/RTNCenteredTextNativeComponent'; + const App: () => Node = () => { // ... other App code ... return ( - // ...other React Native elements... - - // ...other React Native Elements + + + ); }; + +export default App; ``` diff --git a/zh-cn/turbomodule.md b/zh-cn/turbomodule.md index c39ac1bce275772bfa6791bb92f22c4a47ad6e3f..6f48655df3c5d76360a9284489c531b0a4e28c41 100644 --- a/zh-cn/turbomodule.md +++ b/zh-cn/turbomodule.md @@ -13,7 +13,7 @@ Turbo Modules 是升级版的 Native Modules,是基于 JSI 开发的一套 JS 创建一个 Turbo Native Module 分为以下步骤: 1. 声明 JavaScript 接口类型; -2. 编写脚手架代码(Android 和 iOS 平台可以通过 Codegen 生成); +2. 配置模块以支持 Codegen 自动生成脚手架代码; 3. 编写原生代码完成模块实现。 接下来会创建一个简单的名为 `RTNCalculator` 的 TurboModule 作为示例。 @@ -23,7 +23,8 @@ Turbo Modules 是升级版的 Native Modules,是基于 JSI 开发的一套 JS 我们按照一般的三方库目录结构来配置: ```md -. +TurboModulesGuide +├── MyApp └── RTNCalculator ├── android(Android 的原生实现代码) ├── ios(iOS 的原生实现代码) @@ -83,13 +84,15 @@ export default TurboModuleRegistry.get("RTNCalculator") as Spec | null; 最后,调用 `TurboModuleRegistry.get` 并传入模块名,它将在 Turbo Native Module 可用的时候进行加载。 +将 js 声明文件放入 `src` 文件夹下。 + +> [!TIP] 当我们在编写 JavaScript 代码时,如果没有配置好对应的模块或依赖安装,就从第三方库导入类型,可能会使的您的 IDE 不能正确载入导入声明,从而显示错误或警告。这种情况是正常的,它不会在您添加模块到 App 的时候出现问题。 + ### 3. Codegen 配置 接下来,需要为 Codegen 和自动链接添加一些配置。Codegen 的作用是生成 C++ 脚手架代码,负责串联 JS 和原生侧。 -有一些配置文件在 Android/iOS 平台是通用的,而有的仅能在某一平台使用。 - -HarmonyOS 平台暂时不支持 Codegen,TurboModule 的 C++ 代码需要自行编写。 +有一些配置文件在 Android/iOS/HarmonyOS 平台是通用的,而有的仅能在某一平台使用。 #### Shared @@ -97,54 +100,69 @@ shared 是 package.json 文件中的一个配置项,它将在 yarn 安装模 ```json { - "name": "rtn-calculator", - "version": "0.0.1", - "description": "Add numbers with TurboModules", - "react-native": "src/index", - "source": "src/index", - "files": [ - "src", - "android", - "ios", - "harmony", - "rtn-calculator.podspec", - "!android/build", - "!ios/build", - "!**/__tests__", - "!**/__fixtures__", - "!**/__mocks__" - ], - "keywords": ["react-native", "ios", "android", "harmony"], - "repository": "https://github.com//rtn-calculator", - "author": " (https://github.com/)", - "license": "MIT", - "bugs": { - "url": "https://github.com//rtn-calculator/issues" - }, - "homepage": "https://github.com//rtn-calculator#readme", - "devDependencies": {}, - "peerDependencies": { - "react": "*", - "react-native": "*" - }, - "codegenConfig": { - "name": "RTNCalculatorSpec", - "type": "modules", - "jsSrcsDir": "src", - "android": { - "javaPackageName": "com.rtncalculator" + "name": "rtn-calculator", + "version": "0.0.1", + "description": "Add numbers with TurboModules", + "react-native": "src/index", + "source": "src/index", + "files": [ + "src", + "android", + "ios", + "harmony", + "rtn-calculator.podspec", + "!android/build", + "!ios/build", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__" + ], + "keywords": ["react-native", "ios", "android", "harmony"], + "repository": "https://github.com//rtn-calculator", + "author": " (https://github.com/)", + "license": "MIT", + "bugs": { + "url": "https://github.com//rtn-calculator/issues" + }, + "homepage": "https://github.com//rtn-calculator#readme", + "devDependencies": {}, + "peerDependencies": { + "react": "*", + "react-native": "*" + }, + "harmony": { + "codegenConfig": { + "specPaths": [ + "./src" + ] + } + }, + "codegenConfig": { + "name": "RTNCalculatorSpec", + "type": "modules", + "jsSrcsDir": "src", + "android": { + "javaPackageName": "com.rtncalculator" + } } } -} + ``` -将 Codegen 的配置声明到 codegenConfig 字段。codegenConfig 是一个用于存放要生成的第三方库的对象数组,每个对象又包含其它三个字段: +将 Codegen 的 Android 和 iOS 配置声明到 codegenConfig 字段,HarmonyOS 配置到 harmony.codegenConfig 字段。 + +Android 和 iOS 的 codegenConfig 是一个用于存放要生成的第三方库的对象数组,每个对象又包含其它三个字段: - name:第三方库的名称。按照惯例,名称应以 Spec 为结尾 + - type:在这个 npm 包里的模块类型。在本例中,我们开发的是 Turbo Native Module,所以值为 modules + - jsSrcsDir:用于找到 js 接口声明文件的相对路径,它将被 Codegen 解析 + - android.javaPackageName:由 Codegen 生成的 Java 包名 (需与 AndroidManifest.xml 中包名一致) +HarmonyOS 的 codegenConfig 字段只需要配置 js 接口声明文件的相对路径。 + #### Android 若要在 Android 平台运行 Codegen,需要创建三个文件: @@ -152,7 +170,8 @@ shared 是 package.json 文件中的一个配置项,它将在 yarn 安装模 1. 带有 Codegen 配置信息的 build.gradle 文件 2. AndroidManifest.xml 3. 一个实现 ReactPackage 接口的 Java 类 - 在文件创建完成后,android 目录文件结构应该是这样的: + +在文件创建完成后,android 目录文件结构应该是这样的: ```md android @@ -268,120 +287,28 @@ Codegen 会在 App 编译的时候自动运行。 #### HarmonyOS -> [!tip] 待完善能力:因为 HarmonyOS 平台暂时不支持 Codegen,也不能复用安卓的 C++ 代码,所以这部分需要自行编写和添加。 - -在 `harmony/rtn-calculator/src/main/cpp` 目录下创建: `CMakeLists.txt`,`CalculatorPacakge.h`,`CalculatorTurboModule.h`,`CalculatorTurboModule.cpp`。 +HarmonyOS 需要在 RN 工程中通过运行脚本运行 Codegen。 -```md -harmony -└── rtn-calculator - ├── src - │ └── main - │ ├── cpp - │ │ ├── CalculatorPacakge.h - │ │ ├── CMakeLists.txt - │ │ ├── CalculatorTurboModule.cpp - │ │ └── CalculatorTurboModule.h - │ ├──ets - │ └── modules.json5 - ├── build-profile.json5 - ├── hvigorfile.ts - ├── index.ets - ├── oh-package.json5 - └── ts.ts -``` +打开 RN 工程下的 package.json,如 `MyApp/package.json`,添加: - - -#### **CMakeLists.txt** - -```cmake -# the minimum version of CMake -cmake_minimum_required(VERSION 3.13) -set(CMAKE_VERBOSE_MAKEFILE on) - -file(GLOB rnoh_calculator_SRC CONFIGURE_DEPENDS *.cpp) -add_library(rnoh_calculator SHARED ${rnoh_calculator_SRC}) -target_include_directories(rnoh_calculator PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(rnoh_calculator PUBLIC rnoh) -``` - - - - - -#### **CalculatorTurboModule.h** - -```cpp -# pragma once -# include "RNOH/ArkTSTurboModule.h" - -namespace rnoh { - class JSI_EXPORT RTNCalculatorTurboModule : public ArkTSTurboModule { - public: - RTNCalculatorTurboModule(const ArkTSTurboModule::Context ctx, const std::string name); - }; -} // namespace rnoh -``` - - - - - -#### **CalculatorTurboModule.cpp** - -```cpp -#include "CalculatorTurboModule.h" -#include "RNOH/ArkTSTurboModule.h" - -using namespace rnoh; -using namespace facebook; - -static jsi::Value __hostFunction_RTNCalculatorTurboModule_add(jsi::Runtime &rt, react::TurboModule, const jsi::Value *args, size_t count) { - return static_cast(turboModule).callAsync(rt, "add", args, count); -} - -RTNCalculatorTurboModule::RTNCalculatorTurboModule(const ArkTSTurboModule::Context ctx, const std::string name) : ArkTSTurboModule(ctx, name) { - methodMap_["add"] = MethodMetadata{2, __hostFunction_RTNCalculatorTurboModule_add}; +```json +{ + ... + "scripts": { + ... + "codegen": "react-native codegen-harmony --rnoh-module-path ./harmony/react_native_openharmony" + }, + ... } ``` - - -通过 `RNOH/Package.h` 来导出 CalculatorPackage +> codegen-harmony 参数介绍: - - -#### **CalculatorPacakge.h** +1. --rnoh-module-path: 指定 rnoh OHOS 模块的相对路径,用于存储生成的 ts 文件;如果使用 har 包引入 rnoh 模块,则需要指向:./harmony/entry/oh_modules/@rnoh/react-native-openharmony" -```cpp -#include "RNOH/Package.h" -#include "CalculatorTurboModule.h" - -using namespace rnoh; -using namespace facebook; -class NativeRTNCalculatorFactoryDelegate : public TurboModuleFactoryDelegate { - public: - SharedTurboModule createTurboModule(Context ctx, const std::string &name) const override { - if (name == "RTNCalculator") { - return std::make_shared(ctx, name); - } - return nullptr; - } -} +2. --cpp-output-path: 指定用于存储生成的 C++ 文件的输出目录的相对路径,默认 ./harmony/entry/src/main/cpp/generated; -namespace rnoh { - class CalculatorPackage : public Package { - public: - CalculatorPackage(Package::Context ctx) : Package(ctx) {} - std::unique_ptr createTurboModuleFactoryDelegate() override { - return std::make_unique(); - } - }; -} // namespace rnoh -``` - - +3. --project-root-path: 包根目录的相对路径。 ### 4. 原生代码 @@ -517,21 +444,18 @@ HarmonyOS 平台上 Turbo Native Module 的原生代码需执行如下步骤: 1. 创建用于实现模块的 CalculatorModule.ts 2. 创建 CalculatorPackage.ts -3. 创建 index.ets 和 ts.ts -4. 修改 oh-package.json5,hvigorfile.ts,module.json5 +3. 创建用于导出模块的 index.ets 和 ts.ts +4. 创建 oh-package.json5,hvigorfile.ts,module.json5 -HarmonyOS 第三方库目录文件结构应为如下: +> [!TIP] 可以在 DevEco Studio 中通过 File -> New -> Module.. -> Static Lirbrary 创建空壳模块,以此为基础修改文件内容 + +HarmonyOS 第三方库原生代码文件结构应为如下: ```md harmony -└── rtn-calculator +└── calculator ├── src │ └── main - │ ├── cpp - │ │ ├── CalculatorPacakge.h - │ │ ├── CMakeLists.txt - │ │ ├── CalculatorTurboModule.cpp - │ │ └── CalculatorTurboModule.h │ ├──ets │ │ ├── CalculatorModule.ts │ │ └── CalculatorPackage.ts @@ -550,12 +474,13 @@ harmony #### **CalculatorModule.ts** ```ts -import { TurboModule } from "rnoh/ts"; +import { TurboModule } from '@rnoh/react-native-openharmony/ts'; +import { TM } from "@rnoh/react-native-openharmony/generated/ts" -export class CalculatorModule extends TurboModule { - add(a: number, b: number): Promise { - return new Promise((resolve) => resolve(a + b)); - } +export class CalculatorModule extends TurboModule implements TM.RTNCalculator.Spec { + add(a: number, b: number): Promise { + return new Promise((resolve) => resolve(a + b)); + } } ``` @@ -570,20 +495,27 @@ export class CalculatorModule extends TurboModule { #### **CalculatorPackage.ts** ```ts -import { RNPackage, TurboModulesFactory } from 'rnoh/ts; -import type { TurboModule, TurboModuleContext } from 'rnoh/ts'; +import { + RNPackage, + TurboModulesFactory, +} from "@rnoh/react-native-openharmony/ts"; +import type { + TurboModule, + TurboModuleContext, +} from "@rnoh/react-native-openharmony/ts"; +import { TM } from "@rnoh/react-native-openharmony/generated/ts"; import { CalculatorModule } from './CalculatorModule'; class CalculatorModulesFactory extends TurboModulesFactory { createTurboModule(name: string): TurboModule | null { - if (name === 'RTNCalculator') { - return new CalculatorModule(this.ctx) + if (name === TM.RTNCalculator.NAME) { + return new CalculatorModule(this.ctx); } return null; } hasTurboModule(name: string): boolean { - return name === 'RTNCalculator'; + return name === TM.RTNCalculator.NAME; } } @@ -592,6 +524,7 @@ export class CalculatorPackage extends RNPackage { return new CalculatorModulesFactory(ctx); } } + ``` @@ -621,7 +554,7 @@ export * from "./ts"; -修改 `oh-package.json5`,`hvigorfile.ts`,`module.json5` +修改 `oh-package.json5`,`hvigorfile.ts`,`module.json5`,或自行创建 @@ -629,13 +562,19 @@ export * from "./ts"; ```json { + "license": "ISC", + "types": "", "devDependencies": { - "rnoh": "file:../rnoh" }, - "name": "rnoh-calculator", + "name": "rtn-calculator", + "description": "", "main": "index.ets", - "type": "module" + "version": "0.0.1", + "dependencies": { + "@rnoh/react-native-openharmony": "file:../react_native_openharmony" + } } + ``` @@ -656,11 +595,11 @@ export { harTasks } from "@ohos/hvigor-ohos-plugin"; ```json { - "module": { - "name": "calculator", - "type": "har", - "deviceType": ["default"] - } + module: { + name: 'calculator', + type: 'har', + deviceTypes: ['default'], + }, } ``` @@ -670,11 +609,41 @@ export { harTasks } from "@ohos/hvigor-ohos-plugin"; #### Shared -首先,需要将包含模块的 NPM 包添加到 App。可以使用以下命令执行此操作: +首先,需要将包含模块的 NPM 包添加到 App。请确保 package.json 已经配置安装好以下依赖: + +```json +{ + ... + "dependencies": { + "react-native-harmony": "版本 >= 0.72.15", + ... + }, + "overrides": { + "@react-native/codegen": "0.74.0" + }, + ... +} +``` + +执行以下操作,假设 MyApp 为您的 App 工程路径 ```bash -cd tester -yarn add ../RTNCalculator +// 进入模块工程 +cd RTNCalculator + +// 打包模块 +npm pack + +// 进入 App 工程 +cd ../MyApp + +// 本地路径安装模块 +npm i file:../RTNCalculator/rtn-calculator-0.0.1.tgz + +// 执行以下命令执行 codegen (HarmonyOS only) + +npm run codegen + ``` 此命令会将 RTNCalculator 模块添加到 App 内的 node_modules 目录。 @@ -694,87 +663,60 @@ yarn add ../RTNCalculator ##### 引入原生端代码 -打开 `entry/oh-package.json5`,添加以下依赖,引入鸿蒙原生端的代码 +目前 HarmonyOS 工程暂不支持引入工程外的模块,所以需要手动将模块的 HarmonyOS 源码复制到工程内。 -```json -"dependencies": { - "rnoh": "file:../rnoh", - "rnoh-calculator": "file:../../node_modules/RTNCalculator/harmony/rtn-calculator" - } -``` +复制 `RTNCalculator/harmony/calculator` 到 `harmony` 工程根目录下。 -在终端运行以下命令 +修改 `MyApp/harmony/build-profile.json5`,在 modules 字段添加: -```bash -cd entry -ohpm install --no-link +```json +{ +... + modules: [ + ... + { + name: 'calculator', + srcPath: './calculator', + } + ] +} ``` -##### 配置 CMakeLists 和引入 CalculatorPackge - -打开 `entry/src/main/cpp/CMakeLists.txt`,添加: +打开 `MyApp/harmony/entry/oh-package.json5`,添加以下依赖,引入鸿蒙原生端的代码 -```diff -project(rnapp) -cmake_minimum_required(VERSION 3.4.1) -set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}") -set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") -set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp") - -add_subdirectory("${RNOH_CPP_DIR}" ./rn) - -# RNOH_BEGIN: add_package_subdirectories -add_subdirectory("../../../../sample_package/src/main/cpp" ./sample-package) -+ add_subdirectory("${OH_MODULE_DIR}/rnoh-calculator/src/main/cpp" ./calculator) -# RNOH_END: add_package_subdirectories - -add_library(rnoh_app SHARED - "./PackageProvider.cpp" - "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp" -) - -target_link_libraries(rnoh_app PUBLIC rnoh) - -# RNOH_BEGIN: link_packages -target_link_libraries(rnoh_app PUBLIC rnoh_sample_package) -+ target_link_libraries(rnoh_app PUBLIC rnoh_calculator) -# RNOH_END: link_packages +```json +"dependencies": { + "@rnoh/react-native-openharmony": "file:../react_native_openharmony", + "rtn-calculator": "file:../calculator" + } ``` -打开 `entry/src/main/cpp/PackageProvider.cpp`,添加: - -```diff -#include "RNOH/PackageProvider.h" -#include "SamplePackage.h" -+ #include "CalculatorPackage.h" - -using namespace rnoh; +点击右上角的 `sync` 按钮同步工程,或在终端运行以下命令 -std::vector> PackageProvider::getPackages(Package::Context ctx) { - return { - std::make_shared(ctx), -+ std::make_shared(ctx) - }; -} +```bash +cd entry +ohpm install ``` ##### 在 ArkTs 侧引入 Calculator TurboModule -打开 `entry/src/main/ets/RNPackageFactory.ts`,添加: +打开 `MyApp/harmony/entry/src/main/ets/RNPackageFactory.ts`,添加: ```diff -import {RNPackageContext, RNPackage} from 'rnoh/ts'; +import type {RNPackageContext, RNPackage} from '@rnoh/react-native-openharmony/ts'; import {SamplePackage} from 'rnoh-sample-package/ts'; -+ import {CalculatorPackage} from 'rnoh-calculator/ts'; ++ import { CalculatorPackage } from "rtn-calculator/ts"; export function createRNPackages(ctx: RNPackageContext): RNPackage[] { return [ new SamplePackage(ctx), -+ new CalculatorPackage(ctx) ++ new CalculatorPackage(ctx), ]; } ``` +编译、运行即可。 + #### JavaScript 以下是一个在 App.js 中调用 add 方法的例子: @@ -795,7 +737,7 @@ import React from "react"; import { useState } from "react"; import type { Node } from "react"; import { SafeAreaView, StatusBar, Text, Button } from "react-native"; -import RTNCalculator from "rtn-calculator/js/NativeCalculator.js"; +import RTNCalculator from "rtn-calculator/src/NativeCalculator.js"; const App: () => Node = () => { const [result, setResult] = useState(null); @@ -819,3 +761,5 @@ export default App; ``` + +> [!TIP] 可通过 npm run start 使用热更新