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:
- 定义一组 JavaScript 规范。
- 配置组件,以便 代码生成器 可以创建共享代码,并将其添加为应用的依赖。
- 编写所需的原生代码。
完成这些步骤后,该组件就可以由应用使用。 该指南展示了如何利用自动链接将其添加到应用,以及如何从 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
.
在此文件夹中,创建三个子文件夹: js
、ios
和 android
。
英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 类型化方言(Flow 或 TypeScript)指定的接口。 代码生成器 使用这些规范来生成强类型语言的代码,包括 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:
- 文件 must 命名为
<MODULE_NAME>NativeComponent
,使用 Flow 时具有.js
或.jsx
扩展名,使用 TypeScript 时具有.ts
或.tsx
扩展名。 代码生成器 仅查找与此模式匹配的文件。 - 该文件必须导出
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.
- TypeScript
- Flow
// @flow strict-local
import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes';
import type {HostComponent} from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
type NativeProps = $ReadOnly<{|
...ViewProps,
text: ?string,
// add other props here
|}>;
export default (codegenNativeComponent<NativeProps>(
'RTNCenteredText',
): HostComponent<NativeProps>);
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.
{
"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.
然后是这个包的依赖。 对于本指南,你需要 react
和 react-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:
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.json
的 name
属性中设置的名称: 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.gradle
和 ReactPackage
类
要准备 Android 来运行 代码生成器,你必须:
英To prepare Android to run Codegen you have to:
- 更新
build.gradle
文件。 - 创建一个实现
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:
- Java
- Kotlin
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'
}
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")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.22")
}
}
apply plugin: 'com.android.library'
apply plugin: 'com.facebook.react'
apply plugin: 'org.jetbrains.kotlin.android'
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.
- Java
- Kotlin
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();
}
}
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
class CenteredTextPackage : ReactPackage {
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =
emptyList()
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
emptyList()
}
React Native 使用 ReactPackage
接口来了解应用必须对库导出的 ViewManager
和 Native 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:
- 运行 代码生成器 查看会生成什么。
- 编写使其工作的原生代码。
当开发使用 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 add
将 RTNCenteredText
模块添加到应用中。 然后,它通过 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:
RTNCenteredTextManager.mm
,一个 Objective-C++ 文件,声明组件导出的内容。RTNCenteredText.h
,实际视图的头文件。RTNCenteredText.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
#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
#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]
作为该方法的最后一条语句; 否则 props
和 oldProps
结构将具有相同的值,并且你将无法使用它们来做出决策和更新组件。
英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_XXX
和 RCT_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.
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.mk
和 CMakeList.txt
文件在应用中配置 Fabric Native 组件,而 RTNCenteredTextManagerDelegate.java
和 RTNCenteredTextManagerInterface.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:
- 代表实际视图的
CenteredText.java
。 - 用于实例化视图的
CenteredTextManager.java
。 - 最后,你必须填写上一步中创建的
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
- Java
- Kotlin
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);
}
}
package com.rtncenteredtext;
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.view.Gravity
import android.widget.TextView
class CenteredText : TextView {
constructor(context: Context?) : super(context) {
configureComponent()
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
configureComponent()
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
configureComponent()
}
private fun configureComponent() {
setBackgroundColor(Color.RED)
gravity = 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
- Java
- Kotlin
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);
}
}
package com.rtncenteredtext
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)
class CenteredTextManager(context: ReactApplicationContext) : SimpleViewManager<CenteredText>(), RTNCenteredTextManagerInterface<CenteredText> {
private val delegate: RTNCenteredTextManagerDelegate<CenteredText> = RTNCenteredTextManagerDelegate(this)
override fun getDelegate(): ViewManagerDelegate<CenteredText> = delegate
override fun getName(): String = NAME
override fun createViewInstance(context: ThemedReactContext): CenteredText = CenteredText(context)
@ReactProp(name = "text")
override fun setText(view: CenteredText, text: String?) {
view.text = text
}
companion object {
const val NAME = "RTNCenteredText"
}
}
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
- Java
- Kotlin
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();
}
}
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
class CenteredTextPackage : ReactPackage {
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =
- emptyList()
+ listOf(CenteredTextManager(reactContext))
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
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.
- 打开
android/gradle.properties
文件 - 向下滚动到文件末尾并将
newArchEnabled
属性从false
切换到true
。
JavaScript
最后,你可以在 JavaScript 应用中使用该组件。
英Finally, you can use the component in your JavaScript application.
- TypeScript
- Flow
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow strict-local
*/
import React from 'react';
import type {Node} from 'react';
import RTNCenteredText from 'rtn-centered-text/js/RTNCenteredTextNativeComponent';
const App: () => Node = () => {
return (
<RTNCenteredText
text="Hello World!"
style={{width: '100%', height: 30}}
/>
);
};
export default App;
/**
* 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.