Fabric 组件
这个文档仍然是实验性的,随着我们的迭代,细节会有变化。欢迎在工作小组内的讨论中分享你的反馈。
此外,它还包含几个手动步骤。请注意新架构尚未稳定下来,最终的开发者体验会继续迭代改善。我们正在努力开发工具、模板和库,以帮助你在新架构上快速入门,而不需要经历整个设置过程。
Fabric 组件是一种使用 Fabric 渲染器渲染并展示在屏幕上的 UI 组件。在新架构中,使用 Fabric 组件替代原生组件具有以下优势:
- 各个平台的强类型接口声明是一致的;
- 您可以使用 C++ 编写组件或迁移其它平台的原生代码,以此避免在跨平台重复实现组件;
- 通过替换 Bridge 为 JSI(使用原生代码编写的 JavaScript 接口),提升 JavaScript 与原生代码的通讯效率。
在开发 Fabric 组件前,您需要先创建一个 JavaScript 接口描述文件。之后 Codegen 会根据这个文件创建一些 C++ 脚手架代码,用于将部分组件逻辑(比如调用原生平台接口能力)与 React Native 结合起来。C++ 代码在各个平台都是一样的,只要组件能够与生成的 C++ 代码连接起来,就可以导入到 App 并运行。
接下来的内容将一步步指导您针对 React Native 0.70.0 创建一个 Fabric 组件。
Fabric 组件仅适用于新架构。若您想了解如何将 App 迁移到新架构,可前往迁移指南。
如何创建 Fabric 组件
若要创建一个 Fabric 组件,您需要遵循以下步骤:
- 声明 JavaScript 接口;
- 配置组件以用于 Codegen 生成统一代码,生成的代码可添加为 App 的依赖;
- 编写所需的原生代码。
完成这些步骤后,组件就可以在 App 里使用了。本指导将向您介绍如何将其添加到 App,并利用自动链接使其能在 JavaScript 代码引用。
1. 目录配置
为了使组件与 App 保持解耦,有个不错的方案是将组件从 App 抽离出来,并添加为 App 的一个依赖。如果您打算开发一个开源的 Fabric 组件,您同样也需要这么做。
在本指导中,您将开发一个用于在屏幕上将文本居中显示的 Fabric 组件。
在与 App 平级的目录,创建名为 RTNCenteredText
的目录。在这个目录中,创建三个子目录:js
、ios
和 android
。创建后的目录结构是这样的:
.
├── MyApp
└── RTNCenteredText
├── android
├── ios
└── js
2. 声明 JavaScript 接口
新架构要求必须使用强类型风格语言声明 JavaScript 接口(Flow 和 TypeScript 皆可)。Codegen
会根据这些接口声明来生成强类型的语言,其中包括 C++、Objective-C 和 Java。
对于声明类型的代码文件必须满足以下两点要求:
- 文件必须使用
<MODULE_NAME>NativeComponent
命名,在使用 Flow 时,以.js
或.jsx
为后缀名;在使用 Typescript 时,以.ts
或.tsx
为后缀名。Codegen 只会找到匹配这些命名规则的文件; - 代码中必须要输出
HostComponent
对象。
以下是使用 Flow 和 TypeScript 声明的 RTNCenteredText
组件。在 js
目录中,创建一个命名为 RTNCenteredText
并带有相应后缀名的文件。
- 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;
// 添加其它 props
}
export default codegenNativeComponent<NativeProps>(
'RTNCenteredText',
) as HostComponent<NativeProps>;
在声明文件的顶部导入了一些内容。以下是开发 Fabric 组件必须要导入的内容:
HostComponent
类型: 导出的组件需要与这个类型保持一致;codegenNativeComponent
函数:负责将组件注册到 JavaScript 运行时。
声明文件的中间部分包含了组件的 props。Props("properties" 的缩写)是用于自定义 React 组件的参数信息。在本例中,您将需要控制组件的 text
属性。
在声明文件的最后部分,导出了泛型函数 codegenNativeComponent
的返回值,此函数需要传递组件的名称。
当我们在编写 JavaScript 代码时,如果没有配置好对应的模块或依赖安装,就从第三方库导入类型,可能会使的您的 IDE 不能正确载入导入声明,从而显示错误或警告。这些问题会在 Fabric 添加为 App 的依赖后得到解决。
3. 组件配置
接下来,您需要为 Codegen 和自动链接添加一些配置。
有一些配置文件在 iOS 和 Android 平台是通用的,而有的仅能在某一平台使用。
Shared
shared 是 package.json
文件中的一个配置项,它将在 yarn 安装您的模块时被调用,请在 RTNCenteredText
的根目录创建 package.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",
"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>
等标记。
接下来是 npm 包的依赖。在本指导中,您会用到 react
和 react-native
。
最后,将 Codegen 的配置声明到 codegenConfig
字段。codegenConfig
是一个用于存放要生成的第三方库 的对象数组,每个对象又包含其它三个字段:
name
:第三方库的名称。按照惯例,名称应以Spec
为结尾type
:在这个 npm 包里的模块类型。在本例中,我们开发的是 Fabric 组件,所以值为components
jsSrcsDir
:用于找到js
接口声明文件的相对路径,它将被 Codegen 解析
iOS:创建 podspec
文件
针对 iOS 平台,您需要创建一个 rtn-centered-text.podspec
文件,它将您的模块定义为 App 里的一个依赖。文件要放在 RTNCenteredText
根目录,与 ios
目录处于同一个位置。
文件内容如下:
require "json"
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
folly_version = '2021.07.22.00'
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
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}"
s.dependency "React-Core"
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
s.pod_target_xcconfig = {
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
}
s.dependency "React-RCTFabric"
s.dependency "React-Codegen"
s.dependency "RCT-Folly", folly_version
s.dependency "RCTRequired"
s.dependency "RCTTypeSafety"
s.dependency "ReactCommon/turbomodule/core"
end
这个 .podspec
文件需要和 package.json
处于同一个目录,并且它的命名应该取自我们在 package.json
的 name
字段配置的值:rtn-centered-text
。
文件首部分设置了一些在后续会使用到的变量。后面一部分内容是一些用来配置 pod 的信息,比如命名、版本和功能描述等。其余内容是一些在新架构中必须要配置的依赖。
Android: build.gradle
, AndroidManifest.xml
, ReactPackage
类
若要在 Android 平台运行 Codegen,您需要创建三个文件:
- 带有
Codegen
配置信息的build.gradle
文件 AndroidManifest.xml
- 一个实现
ReactPackage
接口的 Java 类
在文件创建完成后,android
目录文件结构应该是这样的:
android
├── build.gradle
└── src
└── main
├── AndroidManifest.xml
└── java
└── com
└── rtncenteredtext
└── RTNCenteredTextPackage.java
build.gradle
首先,在 android
目录创建 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.1.1")
}
}
apply plugin: 'com.android.library'
apply plugin: 'com.facebook.react'
android {
compileSdkVersion safeExtGet('compileSdkVersion', 31)
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', 21)
targetSdkVersion safeExtGet('targetSdkVersion', 31)
buildConfigField("boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true")
}
}
repositories {
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$projectDir/../node_modules/react-native/android"
}
mavenCentral()
google()
}
dependencies {
implementation 'com.facebook.react:react-native:+'
}
AndroidManifest.xml
其次,创建 android/src/main
目录,然后在这个目录内创建 AndroidManifest.xml
文件,并编写以下代码:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.rtncenteredtext">
</manifest>
这个 manifest 文件的用途是声明您开发的模块的 Java 包。
ReactPackage
最后,您需要一个继承 TurboReactPackage
接口的类。在运行 Codegen 前,您不用完整实现这个类。对于 App 而言,一个没有实现接口的空类就已经能当做一个 React Native 依赖,Codegen 会尝试生成其脚手架代码。
创建 android/src/main/java/com/rtncenteredtext
目录,在这个目录内创建 RTNCenteredTextPackage.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 RTNCenteredTextPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
ReactPackage
接口的用途是让 React Native 为使用 App 中的 ViewManager
和 Native Modules
,识别出哪些原生类需要在第三方库里导出。
4. 原生代码
最后一步需要您编写用于 JavaScript 端的组件与原生平台交互的原生代码,这包含两个主要步骤:
- 运行 Codegen 并查看其生成的代码;
- 编写原生代码。
在开发一个使用 Fabric 组件 的 React Native App 时,将由 App 负责使用 Codegen 生成代码。但在开发 Fabric 组件的第三方库时,我们需要引用 Codegen 的生成代码,因此查看生成的代码是很有帮助的。
首先,为生成 iOS 和 Android 平台的代码,本指导将向您展示如何手动执行由 Codegen 生成的脚本,以及生成所需要的平台代码。您可以在这里了解到更多关于 Codegen 的内容。
Codegen 生成的代码不该提交到版本管理系统,React Native 会在 App 构建的时候自动生成代码。这是为了确保在 App 内,所有第三方库都正确使用针对某一 React Native 版本的生成代码。