众所周知 React Native 自 2021 年开始就宣布支持新的渲染器 Fabric 并开始逐渐迁移新架构 ,据 RN 开发团队的计划逐步迁移并在 2024 年底将会将新架构上线到 RN 正式版默认启用,并逐步放弃对旧架构的支持(当然段时间不会立即将旧架构部分移除,会在一定时间内保持兼容,就如同燃油车到电车的过程,那自然现在的阶段就会出现油电混动车)所以作为依赖库的维护者,我们现在开发和迁移自己的依赖就是最好的时机。
本篇博客就是基于我在迁移一个之前的开源依赖库 (react-native-ad 这是最早做国内字节跳动穿山甲、腾讯优量汇、快手等平台聚合广告的开源 React Native 移动应用流量变现库),最近这段时间我一直在做依赖库的升级和适配,既然选择这个时间点来升级迭代,那必然是要同时适配新旧架构的,当然我也相信我们应该是最早一批国内 React Native 广告聚合库支持 RN 新架构的,欢迎有需要的小伙伴联系我们,我们也会积极参与社区活动帮助开发者们适配 RN 新架构。
当然,这篇博客其实更多的是面向 React Native 库开发者以及 IOS 开发者想往 React Native 跨平台开发发展和想深入学习原生开发的前端开发小伙伴们。因为篇幅所限,所以首篇博客内容会比较偏向于对比新旧架构对于 Native Component 的用法之间区别。
首先就是 JavaScript(Typescript)层,在旧架构中组件的定义和参数传递约束性其实是比较低的,几乎没有什么很强的定义映射,在旧架构中通常使用 requireNativeComponent
接口导出原生层实现的 Native Components 组件。
React Native New Architecture Codegen ⚒️
在新架构中则是引入了一个“新概念” Codegen,简单来说就是可以先定义 Typescript 的 Fabric Native Components 接口部分,然后由 React Native Codegen 首先生成 C++ 接口定义,随后库作者实现相应接口即可,在 IOS 中由于 ObjectC++ 能够比较友好的调用 C++ 的定义所以写起来现在就会比较舒服了,但是 Android 部分由于 Java 层和 C++ 的互调用还是需要走 JNI 所以除了 C++ 的接口部分之外还生成了 Java 与 C++ 之间的一些 JNI 桥接代码,当然篇幅所限 Android 部分的实现我下一篇博客接着讲,这篇博客主要讲 IOS 部分的实现。
通过下列代码可以对比出来新旧架构层在 Typescript(目前 Codegen 暂时仅支持 Flow 或 TypeScript 定义用于定义接口)的定义上区别
// 旧架构
import React from 'react';
import { requireNativeComponent, ViewStyle } from 'react-native';
const FeedAdComponent = requireNativeComponent('FeedAd');
export interface FeedAdProps {
codeid: string;
style?: ViewStyle;
onAdError?: Function;
}
const TxFeedAd = (props: FeedAdProps) => {
return (
<FeedAdComponent
codeid={props.codeid}
style={props.style}
onAdError={props.onAdError}
/>
)
}
// 新架构
import type { ViewProps } from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
import type {
DirectEventHandler,
Int32,
} from 'react-native/Libraries/Types/CodegenTypes'; // https://github.com/reactwg/react-native-new-architecture/discussions/27
export type AdFailWithErrorEventReply = Readonly<{
code: Int32;
message: string;
}>;
interface FeedViewProps extends ViewProps {
codeId: string;
onAdError?: DirectEventHandler<
Readonly<AdFailWithErrorEventReply>
>; // 广告加载、渲染失败
}
export default codegenNativeComponent<FeedViewProps>('FeedView');
通过上面 react-native-ad 中 Feed 广告的重构部分代码摘录就能看出来,首先旧架构中 requireNativeComponent 获取到的原生对象其实是一个 any 类型的 Comment 所以在导出组件时,通常需要另外定义一个用于导出合理类型声明的函数组件套用。而新架构上的话 codegenNativeComponent 直接通过定义类型泛型能够直接导出指定 props 类型的 Comment 组件。
其次,能够看出来新架构这边是更需要规范定义 props 参数类型的如回调函数类型需要使用 DirectEventHandler 或者 EventHandler 泛型定义,而在旧架构中对类型定义就比较随意定义了一个 Function 类型,除此之外细心的小伙伴们可能发现了在回调函数中的参数我这边用到了 Int32 这个类型,前端的小伙伴可能对这个类型就比较陌生了,还记得我们上面说到的 Codegen 会通过我们这里的 Typescript 定义的代码生成 C++ 接口代码吗?是的你没猜错,这里 Int32 就是对应 C++ 中的 Int32 数据类型,因此接口层定义肯定是不支持全部的 JS 类型的,仅支持部分数据类型映射(这里可以参考官方文档)
当然,除了通过 Typescript 接口定义之外,还需要配置 Codegen(这边指的是旧的库项目中直接准备升级的话,如果是通过 react-native-bob 创建的新依赖库的话会默认配置好的)首先文件名是有规范性的,这是为了方便 Codegen 能够识别哪些 ts 代码文件需要生成 NativeSpec(C++ 接口定义代码)要求如下:
1、该文件必须命名为 <MODULE_NAME>NativeComponent
,使用 Flow 时带有 .js
或 .jsx
扩展名,或者 .ts
或 .tsx
2、该文件必须导出 HostComponent
对象
完成这些之后,以上面的 FeedView 广告组件为例,我们目前的文件夹结构应该如下
.
├── android
├── ios
├── package.json
├── src
│ ├── FeedViewNativeComponent.ts
│ └── index.tsx
├── tsconfig.json
└── yarn.lock
定义文件符合规范后,就是在 package.json 中配置 codegenConfig
部分了,其中 name
配置库的名称(可以简单理解为生成代码前缀部分),type
表示生成的代码类型(目前改参数有 components
、modules
、all
三个类型分别代表 Fabric Native Components、 Turbo Native Modules 和包含两种类型的全部),jsSrcsDir
为 Codegen 解析的 js 规范代码文件的相对路径。
{
"name": "react-native-ad",
"version": "1.0.0",
"description": "This is an aggregate advertising package.",
"private": true,
"main": "lib/commonjs/index",
"module": "lib/module/index",
"types": "lib/typescript/src/index.d.ts",
"react-native": "src/index",
"source": "src/index",
……
"codegenConfig": {
"name": "RCTAdViewSpec",
"type": "all",
"jsSrcsDir": "src"
}
}
配置项目 Podspec 部分 👾
对于 IOS 部分,还需要创建或者修改库项目的 .podspec
文件,该文件名需要根据 package.json 中 name 字段部分命名相同,例如我上面包名为 react-native-ad 那么在项目根目录下就是需要创建 react-native-ad.podspec 文件
require "json"
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
Pod::Spec.new do |s|
s.name = "react-native-ad"
s.version = package["version"