Skip to main content

Fabric 原生组件


提醒

本文档仍为 experimental,详细信息可能会随着我们的迭代而发生变化。 欢迎在此页面分享你对 工作组内部讨论 的反馈。

This documentation is still experimental and details are subject to changes as we iterate. Feel free to share your feedback on the discussion inside the working group for this page.

而且,它还包含几个 手动步骤。 请注意,一旦新架构稳定,这将不代表最终的开发者体验。 我们正在开发工具、模板和库,以帮助你快速开始使用新架构,而无需完成整个设置。

Moreover, it contains several manual steps. Please note that this won't be representative of the final developer experience once the New Architecture is stable. We're working on tools, templates and libraries to help you get started fast on the New Architecture, without having to go through the whole setup.

Fabric 原生组件是使用 Fabric 渲染器 渲染在屏幕上的原生组件。 使用 Fabric Native Components 代替 Legacy Native Components 让我们能够收获 新架构 的所有 benefits

A Fabric Native Component is a Native Component rendered on the screen using the Fabric Renderer. Using Fabric Native Components instead of Legacy Native Components allows us to reap all the benefits of the New Architecture:

  • 跨平台一致的强类型接口。
  • 能够用 C++ 编写代码,无论是单独的还是与其他原生平台语言集成,从而减少跨平台重复实现的需要。
  • 使用 JSI(原生代码的 JavaScript 接口),与桥接相比,它允许原生和 JavaScript 代码之间进行更有效的通信。

Fabric Native 组件是从 JavaScript 规范 开始创建的。 然后,代码生成器 创建一些 C++ 脚手架代码,将特定于组件的逻辑(例如,访问某些原生平台功能)连接到 React Native 基础设施的其余部分。 所有平台的 C++ 代码都是相同的。 一旦组件与脚手架代码正确连接,就可以由应用导入和使用。

A Fabric Native Component is created starting from a JavaScript specification. Then Codegen creates some C++ scaffolding code to connect the component-specific logic (for example, accessing some native platform capability) to the rest of the React Native infrastructure. The C++ code is the same for all the platforms. Once the component is properly connected with the scaffolding code, it is ready to be imported and used by an app.

以下部分将指导你逐步创建 Fabric Native 组件,并针对最新版本的 React Native。

The following section guides you through the creation of a Fabric Native Component, step-by-step, targeting the latest version of React Native.

提醒

Fabric Native Components 仅适用于启用 新架构 的情况。 要迁移到 新架构,请按照 迁移指南 操作:::

如何创建 Fabric 原生组件

要创建 Fabric Native 组件,你必须执行以下步骤:

To create a Fabric Native Component, you have to follow these steps:

  1. 定义一组 JavaScript 规范。
  2. 配置组件,以便 代码生成器 可以创建共享代码,并将其添加为应用的依赖。
  3. 编写所需的原生代码。

完成这些步骤后,该组件就可以由应用使用。 该指南展示了如何利用自动链接将其添加到应用,以及如何从 JavaScript 代码引用它。

Once these steps are done, the component is ready to be consumed by an app. The guide shows how to add it to an app by leveraging autolinking, and how to reference it from the JavaScript code.

1. 文件夹设置

为了使组件与应用解耦,最好将模块与应用分开定义,然后将其作为依赖添加到应用中。 这也是你编写可以稍后作为开源库发布的 Fabric Native 组件时要做的事情。

In order to keep the component decoupled from the app, it's a good idea to define the module separately from the app and then add it as a dependency to your app later. This is also what you'll do for writing Fabric Native Component that can be released as open-source libraries later.

在本指南中,你将创建一个 Fabric Native 组件,将一些文本置于屏幕的中心。

For this guide, you are going to create a Fabric Native Component that centers some text on the screen.

在应用的同一级别创建一个新文件夹,并将其命名为 RTNCenteredText

Create a new folder at the same level of the app and call it RTNCenteredText.

在此文件夹中,创建三个子文件夹: jsiosandroid

In this folder, create three subfolders: js, ios, and android.

最终结果应如下所示:

The final result should look like this:

.
├── MyApp
└── RTNCenteredText
├── android
├── ios
└── js

2. JavaScript 规范

新架构 需要以 JavaScript 类型化方言(FlowTypeScript)指定的接口。 代码生成器 使用这些规范来生成强类型语言的代码,包括 C++、Objective-C++ 和 Java。

The New Architecture requires interfaces specified in a typed dialect of JavaScript (either Flow or TypeScript). Codegen uses these specifications to generate code in strongly-typed languages, including C++, Objective-C++, and Java.

包含此规范的文件必须满足两个要求:

There are two requirements the file containing this specification must meet:

  1. 文件 must 命名为 <MODULE_NAME>NativeComponent,使用 Flow 时具有 .js.jsx 扩展名,使用 TypeScript 时具有 .ts.tsx 扩展名。 代码生成器 仅查找与此模式匹配的文件。
  2. 该文件必须导出 HostComponent 对象。

以下是 Flow 和 TypeScript 中 RTNCenteredText 组件的规范。 在 js 文件夹中创建具有正确扩展名的 RTNCenteredTextNativeComponent 文件。

Below are specifications of the RTNCenteredText component in both Flow and TypeScript. Create a RTNCenteredTextNativeComponent file with the proper extension in the js folder.

import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';

export interface NativeProps extends ViewProps {
text?: string;
// add other props here
}

export default codegenNativeComponent<NativeProps>(
'RTNCenteredText',
) as HostComponent<NativeProps>;

在规范文件的开头,有导入。 每个 Fabric 原生组件所需的最重要的导入是:

At the beginning of the spec files, there are the imports. The most important imports, required by every Fabric Native Component are:

  • HostComponent: 导出的组件需要符合的类型。
  • codegenNativeComponent 功能: 负责在 JavaScript 运行时实际注册组件。

文件的第二部分包含组件的 props属性("properties" 的缩写)是特定于组件的信息,可让你自定义 React 组件。 在本例中,你想要控制组件的 text 属性。

The second section of the files contains the props of the component. Props (short for "properties") are component-specific information that let you customize React components. In this case, you want to control the text property of the component.

最后,spec 文件导出 codegenNativeComponent 通用函数的返回值,通过传递组件名称进行调用。

Finally, the spec file exports the returned value of the codegenNativeComponent generic function, invoked passing the name of the component.

提醒

JavaScript 文件从库导入类型,无需设置适当的节点模块并安装其依赖。 这样做的结果是 IDE 可能无法解析导入语句,并且可能会输出错误和警告。 一旦 Fabric Native 组件被添加为 React Native 应用的依赖,这些就会消失。

3. 组件配置

接下来,你需要为 代码生成器 和自动链接添加一些配置。

Next, you need to add some configuration for Codegen and auto-linking.

其中一些配置文件在 iOS 和 Android 之间共享,而其他配置文件则特定于平台。

Some of these configuration files are shared between iOS and Android, while the others are platform-specific.

共享

共享配置是一个 package.json 文件,yarn 在安装模块时将使用该文件。 在 RTNCenteredText 目录的根目录中创建 package.json 文件。

The shared configuration is a package.json file that will be used by yarn when installing your module. Create the package.json file in the root of the RTNCenteredText directory.

package.json
{
"name": "rtn-centered-text",
"version": "0.0.1",
"description": "Showcase a Fabric Native Component with a centered text",
"react-native": "js/index",
"source": "js/index",
"files": [
"js",
"android",
"ios",
"rtn-centered-text.podspec",
"!android/build",
"!ios/build",
"!**/__tests__",
"!**/__fixtures__",
"!**/__mocks__"
],
"keywords": ["react-native", "ios", "android"],
"repository": "https://github.com/<your_github_handle>/rtn-centered-text",
"author": "<Your Name> <your_email@your_provider.com> (https://github.com/<your_github_handle>)",
"license": "MIT",
"bugs": {
"url": "https://github.com/<your_github_handle>/rtn-centered-text/issues"
},
"homepage": "https://github.com/<your_github_handle>/rtn-centered-text#readme",
"devDependencies": {},
"peerDependencies": {
"react": "*",
"react-native": "*"
},
"codegenConfig": {
"name": "RTNCenteredTextSpecs",
"type": "components",
"jsSrcsDir": "js"
}
}

文件的上部包含一些描述性信息,例如组件的名称、版本及其源文件。 确保更新 <> 中包含的各种占位符: 替换所有出现的 <your_github_handle><Your Name><your_email@your_provider.com> 标记。

The upper part of the file contains some descriptive information like the name of the component, its version and its source files. Make sure to update the various placeholders which are wrapped in <>: replace all the occurrences of the <your_github_handle>, <Your Name>, and <your_email@your_provider.com> tokens.

然后是这个包的依赖。 对于本指南,你需要 reactreact-native

Then there are the dependencies for this package. For this guide, you need react and react-native.

最后,代码生成器 配置由 codegenConfig 字段指定。 它包含一个库数组,每个库都由其他三个字段定义:

Finally, the Codegen configuration is specified by the codegenConfig field. It contains an array of libraries, each of which is defined by three other fields:

  • name: 库的名称。 按照惯例,你应该添加 Spec 后缀。
  • type: 该包包含的模块的类型。 在本例中,它是 Fabric 原生组件,因此要使用的值为 components
  • jsSrcsDir: 访问由 代码生成器 解析的 js 规范的相对路径。

iOS:创建 .podspec 文件

对于 iOS,你需要创建一个 rtn-centered-text.podspec 文件,该文件将模块定义为你的应用的依赖。 它将保留在 RTNCenteredText 的根目录中,与 ios 文件夹一起。

For iOS, you'll need to create a rtn-centered-text.podspec file which will define the module as a dependency for your app. It will stay in the root of RTNCenteredText, alongside the ios folder.

该文件将如下所示:

The file will look like this:

rtn-centered-text.podspec
require "json"

package = JSON.parse(File.read(File.join(__dir__, "package.json")))

Pod::Spec.new do |s|
s.name = "rtn-centered-text"
s.version = package["version"]
s.summary = package["description"]
s.description = package["description"]
s.homepage = package["homepage"]
s.license = package["license"]
s.platforms = { :ios => "11.0" }
s.author = package["author"]
s.source = { :git => package["repository"], :tag => "#{s.version}" }

s.source_files = "ios/**/*.{h,m,mm,swift}"

install_modules_dependencies(s)
end

.podspec 文件必须是 package.json 文件的同级文件,其名称是我们在 package.jsonname 属性中设置的名称: rtn-centered-text

The .podspec file has to be a sibling of the package.json file, and its name is the one we set in the package.json's name property: rtn-centered-text.

文件的第一部分准备了我们在整个文件中使用的一些变量。 然后,有一个部分包含一些用于配置 Pod 的信息,例如名称、版本和描述。

The first part of the file prepares some variables that we use throughout the file. Then, there is a section that contains some information used to configure the pod, like its name, version, and description.

新架构的所有要求都已封装在 install_modules_dependencies 中。 它负责根据当前启用的体系结构安装适当的依赖。 它还会自动在旧架构中安装 React-Core 依赖。

All the requirements for the New Architecture have been encapsulated in the install_modules_dependencies. It takes care of installing the proper dependencies based on which architecture is currently enabled. It also automatically installs the React-Core dependency in the old architecture.

Android:build.gradleReactPackage

要准备 Android 来运行 代码生成器,你必须:

To prepare Android to run Codegen you have to:

  1. 更新 build.gradle 文件。
  2. 创建一个实现 ReactPackage 接口的 Java/Kotlin 类。

在这些步骤结束时,android 文件夹应如下所示:

At the end of these steps, the android folder should look like this:

android
├── build.gradle
└── src
└── main
└── java
└── com
└── rtncenteredtext
└── CenteredTextPackage.java

build.gradle 文件

首先在 android 文件夹下创建一个 build.gradle 文件,内容如下:

First, create a build.gradle file in the android folder, with the following contents:

build.gradle
buildscript {
ext.safeExtGet = {prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
repositories {
google()
gradlePluginPortal()
}
dependencies {
classpath("com.android.tools.build:gradle:7.3.1")
}
}

apply plugin: 'com.android.library'
apply plugin: 'com.facebook.react'

android {
compileSdkVersion safeExtGet('compileSdkVersion', 33)
namespace "com.rtncenteredtext"

defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', 21)
targetSdkVersion safeExtGet('targetSdkVersion', 33)
buildConfigField("boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true")
}
}

repositories {
mavenCentral()
google()
}

dependencies {
implementation 'com.facebook.react:react-native'
}

ReactPackage

然后,你需要一个实现 ReactPackage 接口的类。 要运行 代码生成器 进程,你不必完全实现 Package 类: 一个空的实现足以让应用将模块作为适当的 React Native 依赖并尝试生成脚手架代码。

Then, you need a class that implements the ReactPackage interface. To run the Codegen process, you don't have to completely implement the Package class: an empty implementation is enough for the app to pick up the module as a proper React Native dependency and to try and generate the scaffolding code.

创建一个 android/src/main/java/com/rtncenteredtext 文件夹,并在该文件夹内创建一个 CenteredTextPackage.java 文件。

Create an android/src/main/java/com/rtncenteredtext folder and, inside that folder, create a CenteredTextPackage.java file.

CenteredTextPackage.java
package com.rtncenteredtext;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Collections;
import java.util.List;

public class CenteredTextPackage implements ReactPackage {

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}

}

React Native 使用 ReactPackage 接口来了解应用必须对库导出的 ViewManagerNative Modules 使用哪些原生类。

The ReactPackage interface is used by React Native to understand what native classes the app has to use for the ViewManager and Native Modules exported by the library.

4. 原生代码

最后一步要求你编写一些原生代码以将组件的 JavaScript 端连接到平台提供的内容。 这个过程需要两个主要步骤:

The last step requires you to write some native code to connect the JavaScript side of the Component to what is offered by the platforms. This process requires two main steps:

  1. 运行 代码生成器 查看会生成什么。
  2. 编写使其工作的原生代码。

当开发使用 Fabric Native 组件的 React Native 应用时,应用有责任使用 代码生成器 实际生成代码。 然而,当将 Fabric 组件开发为库时,它需要引用生成的代码,并且了解应用将生成什么内容非常有用。

When developing a React Native app that uses a Fabric Native Component, it is the responsibility of the app to actually generate the code using Codegen. However, when developing a Fabric Component as a library, it needs to reference the generated code, and it is useful to see what the app will generate.

作为 iOS 和 Android 的第一步,本指南展示了如何手动执行 代码生成器 使用的脚本来生成所需的代码。 有关 代码生成器 的更多信息可参见 here

As the first step for both iOS and Android, this guide shows how to execute manually the scripts used by Codegen to generate the required code. Further information on Codegen can be found here.

提醒

代码生成器 在这一步中生成的代码不应提交到版本控制系统。 React Native 应用能够在构建应用时生成代码。 这允许应用确保所有库都为正确版本的 React Native 生成了代码。

iOS

生成代码 - iOS

要运行 iOS 平台的 Codegen,请打开终端并运行以下命令:

To run Codegen for the iOS platform, open a terminal and run the following command:

cd MyApp
yarn add ../RTNCenteredText
cd ..
node MyApp/node_modules/react-native/scripts/generate-codegen-artifacts.js \
--path MyApp/ \
--outputPath RTNCenteredText/generated/

该脚本首先使用 yarn addRTNCenteredText 模块添加到应用中。 然后,它通过 generate-codegen-artifacts.js 脚本调用 代码生成器

This script first adds the RTNCenteredText module to the app with yarn add. Then, it invokes Codegen via the generate-codegen-artifacts.js script.

--path 选项指定应用的路径,而 --outputPath 选项告诉脚本在哪里输出生成的代码。

The --path option specifies the path to the app, while the --outputPath option tells the script where to output the generated code.

此过程的输出是以下文件夹结构:

The output of this process is the following folder structure:

generated
└── build
└── generated
└── ios
├── FBReactNativeSpec
│ ├── FBReactNativeSpec-generated.mm
│ └── FBReactNativeSpec.h
├── RCTThirdPartyFabricComponentsProvider.h
├── RCTThirdPartyFabricComponentsProvider.mm
└── react
└── renderer
└── components
├── RTNCenteredTextSpecs
│ ├── ComponentDescriptors.h
│ ├── EventEmitters.cpp
│ ├── EventEmitters.h
│ ├── Props.cpp
│ ├── Props.h
│ ├── RCTComponentViewHelpers.h
│ ├── ShadowNodes.cpp
│ └── ShadowNodes.h
└── rncore
├── ComponentDescriptors.h
├── EventEmitters.cpp
├── EventEmitters.h
├── Props.cpp
├── Props.h
├── RCTComponentViewHelpers.h
├── ShadowNodes.cpp
└── ShadowNodes.h

该组件的相关路径是 generated/build/generated/ios/react/renderer/components/RTNCenteredTextSpecs。 该文件夹包含组件所需的所有生成代码。

The relevant path for the component is generated/build/generated/ios/react/renderer/components/RTNCenteredTextSpecs. This folder contains all the generated code required by your Component.

有关生成文件的更多详细信息,请参阅 代码生成器 部分。

See the Codegen section for further details on the generated files.

注意

使用 代码生成器 生成脚手架代码时,iOS 不会自动清理 build 文件夹。 例如,如果你更改了 Spec 名称,然后再次运行 代码生成器,则旧文件将被保留。 如果发生这种情况,请记住在再次运行 代码生成器 之前删除 build 文件夹。

When generating the scaffolding code using Codegen, iOS does not clean the build folder automatically. If you changed the Spec name, for example, and then run Codegen again, the old files will be retained. If that happens, remember to remove the build folder before running the Codegen again.

cd MyApp/ios
rm -rf build

编写原生 iOS 代码

现在脚手架代码已经生成,是时候为 Fabric 组件编写原生代码了。 你需要在 RTNCenteredText/ios 文件夹中创建三个文件:

Now that the scaffolding code has been generated, it's time to write the Native code for your Fabric Component. You need to create three files in the RTNCenteredText/ios folder:

  1. RTNCenteredTextManager.mm,一个 Objective-C++ 文件,声明组件导出的内容。
  2. RTNCenteredText.h,实际视图的头文件。
  3. RTNCenteredText.mm、视图的执行情况。
RTNCenteredTextManager.mm
RTNCenteredTextManager.mm
#import <React/RCTLog.h>
#import <React/RCTUIManager.h>
#import <React/RCTViewManager.h>

@interface RTNCenteredTextManager : RCTViewManager
@end

@implementation RTNCenteredTextManager

RCT_EXPORT_MODULE(RTNCenteredText)

RCT_EXPORT_VIEW_PROPERTY(text, NSString)

@end

该文件是 Fabric Native 组件的管理器。 React Native 运行时使用管理器对象来注册模块、属性和方法,以使它们可供 JavaScript 端使用。

This file is the manager for the Fabric Native Component. React Native runtime uses manager objects to register the modules, properties and methods to make them available to the JavaScript side.

最重要的调用是 RCT_EXPORT_MODULE,它需要导出模块,以便 Fabric 可以检索并实例化它。

The most important call is to the RCT_EXPORT_MODULE, which is required to export the module so that Fabric can retrieve and instantiate it.

然后,你必须公开 Fabric 原生组件的 text 属性。 这是通过 RCT_EXPORT_VIEW_PROPERTY 宏完成的,指定名称和类型。

Then, you have to expose the text property for the Fabric Native Component. This is done with the RCT_EXPORT_VIEW_PROPERTY macro, specifying a name and a type.

信息

还有其他宏可用于导出自定义属性、触发器和其他构造。 你可以查看指定它们 here 的代码。

RTNCenteredText.h
RTNCenteredText.h
#import <React/RCTViewComponentView.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface RTNCenteredText : RCTViewComponentView

@end

NS_ASSUME_NONNULL_END

该文件定义了 RTNCenteredText 视图的接口。 在这里,你可以添加你可能想要在视图上调用的任何原生方法。 对于本指南,你不需要任何东西,因此界面是空的。

This file defines the interface for the RTNCenteredText view. Here, you can add any native method you may want to invoke on the view. For this guide, you don't need anything, therefore the interface is empty.

RTNCenteredText.mm
RTNCenteredText.mm
#import "RTNCenteredText.h"

#import <react/renderer/components/RTNCenteredTextSpecs/ComponentDescriptors.h>
#import <react/renderer/components/RTNCenteredTextSpecs/EventEmitters.h>
#import <react/renderer/components/RTNCenteredTextSpecs/Props.h>
#import <react/renderer/components/RTNCenteredTextSpecs/RCTComponentViewHelpers.h>

#import "RCTFabricComponentsPlugins.h"

using namespace facebook::react;

@interface RTNCenteredText () <RCTRTNCenteredTextViewProtocol>
@end

@implementation RTNCenteredText {
UIView *_view;
UILabel *_label;
}

+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<RTNCenteredTextComponentDescriptor>();
}

- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared<const RTNCenteredTextProps>();
_props = defaultProps;

_view = [[UIView alloc] init];
_view.backgroundColor = [UIColor redColor];

_label = [[UILabel alloc] init];
_label.text = @"Initial value";
[_view addSubview:_label];

_label.translatesAutoresizingMaskIntoConstraints = false;
[NSLayoutConstraint activateConstraints:@[
[_label.leadingAnchor constraintEqualToAnchor:_view.leadingAnchor],
[_label.topAnchor constraintEqualToAnchor:_view.topAnchor],
[_label.trailingAnchor constraintEqualToAnchor:_view.trailingAnchor],
[_label.bottomAnchor constraintEqualToAnchor:_view.bottomAnchor],
]];

_label.textAlignment = NSTextAlignmentCenter;

self.contentView = _view;
}

return self;
}

- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &oldViewProps = *std::static_pointer_cast<RTNCenteredTextProps const>(_props);
const auto &newViewProps = *std::static_pointer_cast<RTNCenteredTextProps const>(props);

if (oldViewProps.text != newViewProps.text) {
_label.text = [[NSString alloc] initWithCString:newViewProps.text.c_str() encoding:NSASCIIStringEncoding];
}

[super updateProps:props oldProps:oldProps];
}

@end

Class<RCTComponentViewProtocol> RTNCenteredTextCls(void)
{
return RTNCenteredText.class;
}

该文件包含视图的实际实现。

This file contains the actual implementation of the view.

它从一些导入开始,这需要你读取 代码生成器 生成的文件。

It starts with some imports, which require you to read the files generated by Codegen.

该组件必须符合 代码生成器 生成的特定协议,在本例中为 RCTRTNCenteredTextViewProtocol

The component has to conform to a specific protocol generated by Codegen, in this case, RCTRTNCenteredTextViewProtocol.

然后,该文件定义了一个静态 (ComponentDescriptorProvider)componentDescriptorProvider 方法,Fabric 使用该方法来检索描述符提供程序以实例化该对象。

Then, the file defines a static (ComponentDescriptorProvider)componentDescriptorProvider method which Fabric uses to retrieve the descriptor provider to instantiate the object.

然后是视图的构造函数: init 方法。 在此方法中,使用 代码生成器 中的 RTNCenteredTextProps 类型创建 defaultProps 结构体非常重要。 你需要将其分配给私有 _props 才能正确初始化 Fabric Native 组件。 初始化程序的其余部分是标准 Objective-C 代码,用于创建视图并使用 AutoLayout 对其进行布局。

Then, there is the constructor of the view: the init method. In this method, it is important to create a defaultProps struct using the RTNCenteredTextProps type from Codegen. You need to assign it to the private _props to initialize the Fabric Native Component correctly. The remaining part of the initializer is standard Objective-C code to create views and layout them with AutoLayout.

最后两块是 updateProps 方法和 RTNCenteredTextCls 方法。

The last two pieces are the updateProps method and the RTNCenteredTextCls method.

每次 JavaScript 中 prop 发生变化时,Fabric 都会调用 updateProps 方法。 作为参数传递的 props 被向下转换为正确的 RTNCenteredTextProps 类型,然后根据需要使用它们来更新原生代码。 请注意,必须调用超类方法 [super updateProps] 作为该方法的最后一条语句; 否则 propsoldProps 结构将具有相同的值,并且你将无法使用它们来做出决策和更新组件。

The updateProps method is invoked by Fabric every time a prop changes in JavaScript. The props passed as parameters are downcasted to the proper RTNCenteredTextProps type, and then they are used to update the native code if needed. Notice that the superclass method [super updateProps] must be invoked as the last statement of this method; otherwise the props and oldProps struct will have the same values, and you'll not be able to use them to make decisions and to update the component.

最后,RTNCenteredTextCls 是另一个静态方法,用于在运行时检索类的正确实例。

Finally, the RTNCenteredTextCls is another static method used to retrieve the correct instance of the class at runtime.

提醒

与 Legacy Native Components 不同的是,Fabric 需要手动实现 updateProps 方法。 仅使用 RCT_EXPORT_XXXRCT_REMAP_XXX 宏导出属性是不够的。

安卓

Android 遵循一些与 iOS 类似的步骤。 你必须生成代码,然后必须编写一些原生代码才能使其正常工作。

Android follows some similar steps to iOS. You have to generate the code, and then you have to write some native code to make it works.

生成代码 - Android

要生成代码,你需要手动调用 代码生成器。 这与你需要为 iOS 执行的操作类似: 首先,你需要将包添加到应用中,然后需要调用脚本。

To generate the code, you need to manually invoke Codegen. This is done similarly to what you need to do for iOS: first, you need to add the package to the app and then you need to invoke a script.

Running Codegen for Android
cd MyApp
yarn add ../RTNCenteredText
cd android
./gradlew generateCodegenArtifactsFromSchema

该脚本首先将包添加到应用中,与 iOS 的方式相同。 然后,在移动到 android 文件夹后,它调用 Gradle 任务来生成脚手架代码。

This script first adds the package to the app, in the same way iOS does. Then, after moving to the android folder, it invokes a Gradle task to generate the scaffolding code.

注意

要运行 代码生成器,你需要在 Android 应用中启用 新架构。 这可以通过打开 gradle.properties 文件并将 newArchEnabled 属性从 false 切换到 true 来完成。

生成的代码存储在 MyApp/node_modules/rtn-centered-text/android/build/generated/source/codegen 文件夹中,其结构如下:

The generated code is stored in the MyApp/node_modules/rtn-centered-text/android/build/generated/source/codegen folder and it has this structure:

codegen
├── java
│ └── com
│ └── facebook
│ └── react
│ └── viewmanagers
│ ├── RTNCenteredTextManagerDelegate.java
│ └── RTNCenteredTextManagerInterface.java
├── jni
│ ├── Android.mk
│ ├── CMakeLists.txt
│ ├── RTNCenteredText-generated.cpp
│ ├── RTNCenteredText.h
│ └── react
│ └── renderer
│ └── components
│ └── RTNCenteredText
│ ├── ComponentDescriptors.h
│ ├── EventEmitters.cpp
│ ├── EventEmitters.h
│ ├── Props.cpp
│ ├── Props.h
│ ├── ShadowNodes.cpp
│ └── ShadowNodes.h
└── schema.json

你可以看到 codegen/jni/react/renderer/components/RTNCenteredTextSpecs 的内容看起来与 iOS 对应版本创建的文件相似。 Android.mkCMakeList.txt 文件在应用中配置 Fabric Native 组件,而 RTNCenteredTextManagerDelegate.javaRTNCenteredTextManagerInterface.java 文件则意味着在管理器中使用。

You can see that the content of the codegen/jni/react/renderer/components/RTNCenteredTextSpecs looks similar to the files created by the iOS counterpart. The Android.mk and CMakeList.txt files configure the Fabric Native Component in the app, while the RTNCenteredTextManagerDelegate.java and RTNCenteredTextManagerInterface.java files are meant use in your manager.

有关生成文件的更多详细信息,请参阅 代码生成器 部分。

See the Codegen section for further details on the generated files.

编写原生 Android 代码

Fabric Native Components Android 端的原生代码需要三部分:

The native code for the Android side of a Fabric Native Components requires three pieces:

  1. 代表实际视图的 CenteredText.java
  2. 用于实例化视图的 CenteredTextManager.java
  3. 最后,你必须填写上一步中创建的 CenteredTextPackage.java 的实现。

Android 库中的最终结构应该是这样的。

The final structure within the Android library should be like this.

android
├── build.gradle
└── src
└── main
└── java
└── com
└── rtncenteredtext
├── CenteredText.java
├── CenteredTextManager.java
└── CenteredTextPackage.java
CenteredText.java
CenteredText.java
package com.rtncenteredtext;

import androidx.annotation.Nullable;
import android.content.Context;
import android.util.AttributeSet;
import android.graphics.Color;

import android.widget.TextView;
import android.view.Gravity;

public class CenteredText extends TextView {

public CenteredText(Context context) {
super(context);
this.configureComponent();
}

public CenteredText(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.configureComponent();
}

public CenteredText(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.configureComponent();
}

private void configureComponent() {
this.setBackgroundColor(Color.RED);
this.setGravity(Gravity.CENTER_HORIZONTAL);
}
}

此类代表 Android 将在屏幕上渲染的实际视图。 它继承自 TextView,并使用私有 configureComponent() 函数配置其自身的基本方面。

This class represents the actual view Android is going to represent on screen. It inherit from TextView and it configures the basic aspects of itself using a private configureComponent() function.

CenteredTextManager.java
CenteredTextManager.java
package com.rtncenteredtext;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerDelegate;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.viewmanagers.RTNCenteredTextManagerInterface;
import com.facebook.react.viewmanagers.RTNCenteredTextManagerDelegate;

@ReactModule(name = CenteredTextManager.NAME)
public class CenteredTextManager extends SimpleViewManager<CenteredText>
implements RTNCenteredTextManagerInterface<CenteredText> {

private final ViewManagerDelegate<CenteredText> mDelegate;

static final String NAME = "RTNCenteredText";

public CenteredTextManager(ReactApplicationContext context) {
mDelegate = new RTNCenteredTextManagerDelegate<>(this);
}

@Nullable
@Override
protected ViewManagerDelegate<CenteredText> getDelegate() {
return mDelegate;
}

@NonNull
@Override
public String getName() {
return CenteredTextManager.NAME;
}

@NonNull
@Override
protected CenteredText createViewInstance(@NonNull ThemedReactContext context) {
return new CenteredText(context);
}

@Override
@ReactProp(name = "text")
public void setText(CenteredText view, @Nullable String text) {
view.setText(text);
}
}

CenteredTextManager 是 React Native 用于实例化原生组件的类。 它是实现 代码生成器 生成的接口的类(请参阅 implements 子句中的 RTNCenteredTextManagerInterface 接口),并且它使用 RTNCenteredTextManagerDelegate 类。

The CenteredTextManager is a class used by React Native to instantiate the native component. It is the class that implements the interfaces generated by Codegen (see the RTNCenteredTextManagerInterface interface in the implements clause) and it uses the RTNCenteredTextManagerDelegate class.

它还负责导出 React Native 所需的所有构造: 类本身用 @ReactModule 注释,setText 方法用 @ReactProp 注释。

It is also responsible for exporting all the constructs required by React Native: the class itself is annotated with @ReactModule and the setText method is annotated with @ReactProp.

CenteredTextPackage.java

最后,打开 android/src/main/java/com/rtncenteredtext 文件夹中的 CenteredTextPackage.java 文件并用以下几行更新它

Finally, open the CenteredTextPackage.java file in the android/src/main/java/com/rtncenteredtext folder and update it with the following lines

CenteredTextPackage.java update
package com.rtncenteredtext;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Collections;
import java.util.List;

public class CenteredTextPackage implements ReactPackage {

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
+ return Collections.singletonList(new CenteredTextManager(reactContext));
}

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}

}

添加的行实例化一个新的 RTNCenteredTextManager 对象,以便 React Native 运行时可以使用它来渲染我们的 Fabric Native 组件。

The added lines instantiate a new RTNCenteredTextManager object so that the React Native runtime can use it to render our Fabric Native Component.

5. 将 Fabric Native 组件添加到你的应用中

这是最终看到 Fabric Native 组件在应用上运行的最后一步。

This is the last step to finally see your Fabric Native Component running on your app.

共享

首先,你需要将包含组件的 NPM 包添加到应用中。 这可以通过以下命令来完成:

First of all, you need to add the NPM package which contains the Component to the app. This can be done with the following command:

cd MyApp
yarn add ../RTNCenteredText

此命令将 RTNCenteredText 组件添加到应用的 node_modules 中。

This command adds the RTNCenteredText Component to the node_modules of your app.

如果该软件包之前已添加到你的应用中,你将需要更新它:

If the package was previously added to your app, you will need to update it:

cd MyApp
yarn upgrade rtn-centered-text

iOS

然后,你需要在 iOS 项目中安装新的依赖。 为此,你需要运行以下命令:

Then, you need to install the new dependencies in your iOS project. To do so, you need to run these commands:

cd ios
RCT_NEW_ARCH_ENABLED=1 bundle exec pod install

此命令安装项目的 iOS 依赖。 RCT_NEW_ARCH_ENABLED=1 标志指示 CocoaPods 必须执行一些附加操作才能运行 代码生成器

This command installs the iOS dependencies for the project. The RCT_NEW_ARCH_ENABLED=1 flag instructs CocoaPods that it has to execute some additional operations to run Codegen.

注意

你可能必须运行 bundle install 一次才能使用 RCT_NEW_ARCH_ENABLED=1 bundle exec pod install。 你将不再需要运行 bundle install,除非你需要更改 ruby 依赖。

安卓

Android 配置需要启用 新架构

Android configuration requires to enable the New Architecture.

  1. 打开 android/gradle.properties 文件
  2. 向下滚动到文件末尾并将 newArchEnabled 属性从 false 切换到 true

JavaScript

最后,你可以在 JavaScript 应用中使用该组件。

Finally, you can use the component in your JavaScript application.

App.tsx
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
*/
import React from 'react';
import RTNCenteredText from 'rtn-centered-text/js/RTNCenteredTextNativeComponent';

const App: () => JSX.Element = () => {
return (
<RTNCenteredText
text="Hello World!"
style={{width: '100%', height: 30}}
/>
);
};
export default App;

现在,你可以运行 React Native 应用并在屏幕上看到你的组件。

Now, you can run the React Native app and see your Component on the screen.