ReactNative: 将自定义的ReactNative组件制作成第三方库的详细流程(制作-->发布)
一、简介
在讲本篇博文之前,需要你熟知怎么自定义ReactNative组件,然后才好学习将自定义的ReactNative组件制作成第三方库。本文中的自定义的ReactNative组件LoginManager API 源自上篇文章,所以需要先看一下上篇博文。言归正传,ReactNative的确提供了一个非常便捷的方式来扩展Native模块。如果要把模块做成第三方组件的话,还有一些工作要做:首先以一个静态库工程来编译模块代码,提供JavaScript的封装,最后创建Package.json来支持node的引用。在步骤开始之前,我先把上篇文章中创建Native原生模块的LoginManager API组件的类贴出来,如下所示:
LoginManager.h
// // LoginManager.h // RNDemo // // Created by 夏远全 on 2020/1/16. // Copyright © 2020 Facebook. All rights reserved. // #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import <React/RCTUtils.h> #import <React/RCTEventDispatcher.h> #import <React/RCTBridgeModule.h> NS_ASSUME_NONNULL_BEGIN //OC中定义一个枚举并导出 typedef NS_ENUM(NSInteger, MoveDiretion){ MoveDiretionNone, MoveDiretionLeft, MoveDiretionRight, MoveDiretionBottom, MoveDiretionTop }; @interface LoginManager : NSObject<RCTBridgeModule> @end NS_ASSUME_NONNULL_END
View Code
LoginManager.m
// // LoginManager.m // RNDemo // // Created by 夏远全 on 2020/1/16. // Copyright © 2020 Facebook. All rights reserved. // #import "LoginManager.h" #ifdef DEBUG #define NSLog(format, ...) printf("[%s] %s [第%d行] %s\n", __TIME__, __FUNCTION__, __LINE__, [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]); #else #define NSLog(format, ...) #endif @implementation LoginManager //初始化, 添加屏幕旋转监听者 -(instancetype)init { self = [super init]; if (self) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil]; } return self; } //导出模块类 RCT_EXPORT_MODULE(); //重映射,auth为code方法的新名称 RCT_REMAP_METHOD(auth,code:(NSString *)account{ NSLog(@"%s---获取验证----",__func__); NSLog(@"account----%@",account); }); //login为方法名 RCT_EXPORT_METHOD(login:(NSString *)account password:(NSString *)password{ NSLog(@"%s---登录账号----",__func__); NSLog(@"account----%@",account); NSLog(@"password----%@",password); }); //logout为方法名 RCT_EXPORT_METHOD(logout:(NSString *)account{ NSLog(@"%s---退出账号----",__func__); NSLog(@"account----%@",account); }); //设置普通的回调函数 //fetchUserInfoWithToken为方法名,successCallback为成功回调,failureCallback为失败回调 RCT_EXPORT_METHOD(fetchUserInfoWithToken:(NSString *)token success:(RCTResponseSenderBlock)successCallback failure:(RCTResponseErrorBlock)failureCallback { if(token.length>0 && successCallback){ successCallback(@[ @"account = xiayuanquan", @"password = 123456", [NSString stringWithFormat:@"token = %@",token] ]); } else{ if(failureCallback){ failureCallback( [[NSError alloc] initWithDomain:NSOSStatusErrorDomain code:404 userInfo: @{NSLocalizedDescriptionKey: @"token exception"}] ); } } }); //设置异步处理的回调函数 //sendMessage为方法名,successCallback为成功回调,failureCallback为失败回调 RCT_EXPORT_METHOD(sendMessage:(NSString *)message success:(RCTPromiseResolveBlock)successCallback failure:(RCTPromiseRejectBlock)failureCallback { if(message.length>0 && successCallback){ successCallback(@"发送成功!"); } else{ if(failureCallback){ failureCallback(@"300",@"发送失败",nil); } } }); //重写constantsToExport, 枚举常量导出 - (NSDictionary<NSString *, id> *)constantsToExport { return @{ @"MoveDiretionNone": @(MoveDiretionNone), @"MoveDiretionLeft": @(MoveDiretionLeft), @"MoveDiretionRight": @(MoveDiretionRight), @"MoveDiretionBottom": @(MoveDiretionBottom), @"MoveDiretionTop": @(MoveDiretionTop) }; } //定义一个移动方法,根据传入的枚举值移动 //move为方法名 RCT_EXPORT_METHOD(move:(MoveDiretion)moveDiretion{ switch(moveDiretion){ case MoveDiretionNone: NSLog(@"仍保持原始位置 --- MoveDiretionNome"); break; case MoveDiretionLeft: NSLog(@"向左边移动位置 --- MoveDiretionLeft"); break; case MoveDiretionRight: NSLog(@"向右边移动位置 --- MoveDiretionRight"); break; case MoveDiretionBottom: NSLog(@"向下边移动位置 --- MoveDiretionBottom"); break; case MoveDiretionTop: NSLog(@"向上边移动位置 --- MoveDiretionTop"); break; } }); //可以重写队列,给当前模块类指定自定义的串行队列。若不指定,则系统默认会给当前模块类随机分配一个串行队列。 //这个方法一旦重写。当前模块的所有方法均会在该自定义的串行队列中异步执行 -(dispatch_queue_t)methodQueue{ return dispatch_queue_create("com.facebook.ReactNative.LoginManagerQueue", DISPATCH_QUEUE_SERIAL); } //定义一个方法,获取线程和队列信息 //thread为方法名 RCT_EXPORT_METHOD(thread:(BOOL)newQueue{ const char *queueName = dispatch_queue_get_label([self methodQueue]); NSLog(@"当前线程1 ------- %@-----%s", [NSThread currentThread], queueName); if(newQueue){ dispatch_async(dispatch_get_main_queue(), ^{ const char *queueName2 = dispatch_queue_get_label(dispatch_get_main_queue()); NSLog(@"当前线程2 ------- %@ -------%s", [NSThread currentThread], queueName2); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ const char *queueName3 = dispatch_queue_get_label(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); NSLog(@"当前线程3 ------- %@-------%s", [NSThread currentThread], queueName3); }); } }); //获取当前屏幕的尺寸 static NSDictionary *Dimensions(){ CGFloat width = MIN(RCTScreenSize().width, RCTScreenSize().height); CGFloat height = MAX(RCTScreenSize().width, RCTScreenSize().height); CGFloat scale = RCTScreenScale(); if(UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)){ width = MAX(RCTScreenSize().width, RCTScreenSize().height); height = MIN(RCTScreenSize().width, RCTScreenSize().height); } return @{ @"width": @(width), @"height": @(height), @"scale": @(scale) }; } //定义一个方法,获取屏幕信息 //getDimensions为方法名 RCT_EXPORT_METHOD(getDimensions:(RCTResponseSenderBlock)callback{ if (callback) { callback(@[[NSNull null], Dimensions()]); } }); //监听方法,使用RCTEventDispatcher的eventDispatcher调用sendDeviceEventWithName函数发送事件信息 //可以将事件名称作为常量导出,提供给ReactNative中使用,也即添加到constantsToExport方法中的字典中即可。类似上面的枚举。 @synthesize bridge = _bridge; -(void)orientationDidChange:(NSNotification *)notification { [_bridge.eventDispatcher sendDeviceEventWithName:@"orientationDidChange" body:@{ @"orientation": UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation) ? @"Landscape": @"Portrait", @"Dimensions": Dimensions()} ]; } //移除监听者 -(void)dealloc{ [[NSNotificationCenter defaultCenter] removeObserver:self]; } @end
View Code
RCTConvert+MoveDiretion.h
// // RCTConvert+MoveDiretion.h // RNDemo // // Created by 夏远全 on 2020/1/17. // Copyright © 2020 Facebook. All rights reserved. // #import <React/RCTConvert.h> NS_ASSUME_NONNULL_BEGIN @interface RCTConvert (MoveDiretion) @end NS_ASSUME_NONNULL_END
View Code
RCTConvert+MoveDiretion.m
// // RCTConvert+MoveDiretion.m // RNDemo // // Created by 夏远全 on 2020/1/17. // Copyright © 2020 Facebook. All rights reserved. // #import "RCTConvert+MoveDiretion.h" #import "LoginManager.h" @implementation RCTConvert (MoveDiretion) //给RCTConvert类添加扩展,这样在模块方法调用中使用常量导出的枚举值,通信到Native中时,会从整型自动转换为定义的枚举类型 RCT_ENUM_CONVERTER(MoveDiretion,(@{ @"MoveDiretionNone": @(MoveDiretionNone), @"MoveDiretionLeft": @(MoveDiretionLeft), @"MoveDiretionRight": @(MoveDiretionRight), @"MoveDiretionBottom": @(MoveDiretionBottom), @"MoveDiretionTop": @(MoveDiretionTop), }), MoveDiretionNone, integerValue) @end
View Code
二、步骤
1、使用xcode创建一个名为LoginManager的静态库。
2、打开静态库,将上面贴出来的已经实现好了的Native模块LoginManager API组件的类全部拷贝进去或进行替换。
3、选择Build Settings,配置Header Search Paths路径。
6、打开终端,初始化一个新的RN项目,随意设置一个名称为:LoginManagerTestProject。(本人安装的ReactNative版本较低)
//初始化 react-native init LoginManagerTestProject --version 0.44.3
7、进入目录node_modules。
//进入node_modules cd LoginManagerTestProject/node_modules
8、创建要制作的第三库文件夹,指定名称为:react-native-login-manager。
//创建第三方名称 mkdir react-native-login-manager
9、进入这个第三方库文件夹。
//进入库文件 cd react-native-login-manager
10、创建ios文件夹。
//创建ios文件 mkdir ios
11、将之前创建的静态库中的根目录下的文件全部copy到这个RN工程LoginManagerTestProject/node_modules/react-native-login-manager/ios目录下。目录结构如下:
12、使用xcode打开这个RN工程LoginManagerTestProject,将静态库LoginManager的.xcodeproj文件 拖到 RN工程LoginManagerTestProject的Libraries文件下。
13、手动添加libLoginManager.a静态包,然后编译,如果编译不出错,则success。
14、使用终端或者webStorm等编程工具进入到RN工程LoginManagerTestProject/node_modules/react-native-login-manager目录下,创建index.js文件,它是整个原生模块的入口,我们这里只是将原生LoginManager模块类进行导出。此时也就完成了第三方库制作的第一步了,仅仅可以自己使用。 (还需要发布到npm上,分享给大家安装使用)
三、测试
好了,咱先自己测试一下,看看行不行。结果是行的,