React Native为iOS创建Fabric Components(一)

512 阅读2分钟

根据官方文档实现一个Fabric组件最少需要做三件步骤:

1. 定义一个JavaScript specifications
相当于定义一套接口规范给js和c++,使2边用一同一套接口,C++的接口文件是通过CodeGen工具脚本生成

ts文件方式定义遇到的问题:

  • 对象需要定义成Readonly
  • 数组需要定义成ReadonlyArray
  • OC中的枚举在ts中一般通过type定义为字符串类型
type StrokeColors = ReadonlyArray<ColorValue>
    
type LineDashPattern = ReadonlyArray<Int32>
    
type MapEvent = Readonly<{
  coordinate: {
    latitude: Double;
    longitude: Double;
  };
  position: {
    x: Float;
    y: Float;
  };
  action: string;
  id?: string;
}>;

type CGLineCap =
  | 'butt'
  | 'round'
  | 'square';

type CGLineJoin =
| 'miter'
| 'round'
| 'bevel';  

type LatLng = Readonly<{
  latitude: Double;
  longitude: Double;
}>;

type LatLngs = ReadonlyArray<LatLng>

interface AIRMapPolylineProps extends ViewProps {
  coordinates: LatLngs;
  strokeColor?: ColorValue;
  strokeColors?: StrokeColors;
  strokeWidth?: Float;
  lineCap?: WithDefault<CGLineCap, "butt">;
  lineJoin?: WithDefault<CGLineJoin, "miter">;
  miterLimit?: Float;
  lineDashPhase?: Float;
  lineDashPattern?: LineDashPattern;
  geodesic?: boolean;
  onPress?: BubblingEventHandler<MapEvent>;
}

2. 执行本地CodeGen脚本工具

  • 检查c++接口文件是否能生成
cd 项目根部目录
node ./node_modules/react-native/scripts/generate-artifacts.js --path . --outputPath ./ios

成功后c++接口文件在ios/build路径下

准备好了podspec文件就可以执行下面命令

cd ios目录下
USE_FABRIC=1 RCT_NEW_ARCH_ENABLED=1 pod install --verbose    

遇到问题:

  • ts接口规矩文件必须以NativeComponent结尾,不然CodeGen工具会过滤掉,导致没有c++ 接口文件的生成

3. 编写native code
具体实现参考官网 native-code

遇到的问题:

  • 编译报错
    Invalid operands to binary expression ('const facebook::react::std::vector<T>' and 'const facebook::react::std::vector<T>')

这个主要是对c++数组直接==比较导致的,目前通过oc方法比较数组里面的内容是否一致

  • Fabric组件不释放
    Fabric组件创建后会被存入一个队列里面,被RN维护起来了;所以这里当Fabric组件unmount需要自己实现哪些视图和数据需要被释放

    关注这3个方法:

- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
    // 加载子视图
}   
    
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
    // 卸载子视图
}
    
- (void)prepareForRecycle {
    // 重置属性状态数据
    // 包括一些强引用子视图的释放
}

这3个方法能控制Fabric组件的子视图和数据释放

  • JS中报错在UIManager中未找到组件

    requireNativeComponent: xxx was not found in the UIManager.
    

    这个的原因是Fabric组件也依赖ViewManager,所以还需要单独建立一个ViewManager类,不然这个Fabric不会注册到全局组件数组中

    这是RN加载组建到全局字典中的实现,最终都加载到_componentDataByName

_componentDataByName = [NSMutableDictionary new];
  for (Class moduleClass in _bridge.moduleClasses) {
    if ([moduleClass isSubclassOfClass:[RCTViewManager class]]) {
      RCTComponentData *componentData = [[RCTComponentData alloc] initWithManagerClass:moduleClass bridge:_bridge eventDispatcher:_bridge.eventDispatcher];
      _componentDataByName[componentData.name] = componentData;
    }
  }
  • Fabric组件在js更新属性时c++的updateProps:oldProps:没调用
    这个问题也是ViewManager引起的,需要在ViewManager中把fabric的属性都再申明一遍