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
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
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_ENDView 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) @endView 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上,分享給大家安裝使用)
三、測試
好了,咱先自己測試一下,看看行不行。結果是行的,