iOS React Native 學習總結


一、簡單介紹

不同於Hybrid ap,React Native里面沒有webview,iOS7中加入了JavaScriptCore.framework框架,而RN正是用JavascriptCore執行js代碼的。

二、環境配置

需要安裝Watchman Flow Node環境,推薦使用Homebrew,注意Xcode版本不能低於7.0

  • brew install node Node安裝
  • brew install watchman
  • brew install flow
  • brew update && brew upgrade 環境更新

三、工程創建

國內需要換源

1 npm config set registry https://registry.npm.taobao.org
2 npm config set disturl https://npm.taobao.org/dist

 

執行以下兩條命令安裝全局npm環境和初始化工程RNDEMO

$ sudo npm install -g react-native-cli
$ react-native init RNDemo

 

cd到工程根目錄中用npm start(如果開啟了自動啟動調試服務器則不需要)啟動服務,然后運行工程

集成RN環境到Native中

1.拷貝node_modules環境到工程中

2.將需要的.xcodeproj文件添加到工程中

3.點擊 項目>Targets>Bulid Phases>Link Binary With Libraries添加對應的靜態庫

4.點擊 項目>Targets>Bulid Settings>Search Paths>Header Search Paths注冊React.xcodeproj文件路徑

5.因為靜態庫里面可能有Category 所以需要在項目>Targets>Build Settings -> other linker flags 中加入-ObjC或者-all_load

四、RN在工程用的一些應用

關於原生組件

  RN能用JS方便地調用官方封裝的組件來加大開發效率,但是一些個性化或者js不太方便的東西還是需要native實現,於是需要native提供一些原生組件來完善。這主要分為兩類,Module類和View類

  1. Module類需要導入頭文件#import "RCTBridgeModule.h"並遵守RCTBridgeModule協議
  2. ViewManager類需要導入頭文件#import "RCTViewManager.h"並繼承至RCTViewManager,遵守RCTBridgeModule協議並實現- (UIView *)view方法return view;即為自定義組件。

  兩種類都需要實現 RCT_EXPORT_MODULE(js_name) 協議宏,js_name是js調用該類名稱,缺省情況下截取類所在文件名,一個類所在文件被引用時,系統會調用其+(void)load函數,當RCT_EXPORT_MODULE()所在文件被引用時,系統調用load 函數,函數里簡單的調用RCTRegisterModule(self) 把自己注冊到一個全局數組RCTModuleClasses,這樣系統中導出的類都會自動注冊到這個全局變量數組里

void RCTRegisterModule(Class moduleClass)
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        RCTModuleClasses = [NSMutableArray new];
    });

    RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)], @"%@ does not conform to the RCTBridgeModule protocol", moduleClass);

    // Register module
    [RCTModuleClasses addObject:moduleClass];
}

 

屬性導出

  RN可以向Native傳遞多種參數,可以以這些支持的類型傳遞數據native組件用EXPORT_VIEW_PROPERTY(<#name#>, <#type#>)來導出類的屬性,js傳遞數據會調用相應的set方法,native可以在set方法里面做些邏輯和數據處理。支持字符串NSString,NSArray數組,基本數據類型,NSnumeber,NSDictionary字典等,自定義類型,並在RCTCovert中,重定義了如下幾種數據類型來處理一些泛型

typedef NSArray NSArrayArray __deprecated_msg("Use NSArray");
typedef NSArray NSStringArray __deprecated_msg("Use NSArray");
typedef NSArray NSStringArrayArray __deprecated_msg("Use NSArray<NSArray *>");
typedef NSArray NSDictionaryArray __deprecated_msg("Use NSArray");
typedef NSArray NSURLArray __deprecated_msg("Use NSArray");
typedef NSArray RCTFileURLArray __deprecated_msg("Use NSArray");
typedef NSArray NSNumberArray __deprecated_msg("Use NSArray");
typedef NSArray UIColorArray __deprecated_msg("Use NSArray");

 

  枚舉類型用RCT_ENUM_CONVERTER(type, values, default, getter)導出 例如:

RCT_ENUM_CONVERTER(UIActivityIndicatorViewStyle, (@{
@"large": @(UIActivityIndicatorViewStyleWhiteLarge),
@"small": @(UIActivityIndicatorViewStyleWhite),
}), UIActivityIndicatorViewStyleWhiteLarge, integerValue)

 

  values為字典類型,default在js傳遞數據為空時候使用         
方法導出

  RCT_EXPORT_METHOD(<#method#>)可導出方法,js通過注冊到全局數組RCTModuleClasses中的實例來調用方法

RCT_EXPORT_METHOD(getCurrentVersion:(RCTResponseSenderBlock)callback)
{

    NSString *events = [self appVersion];
    callback(@[events]);

}

 

  
Nativ模塊實現

  React Native 在一個單獨的串行 GCD 隊列中調用 native 模塊方法,如果 native 模塊需要調用 main-thread-only iOS API例如刷新UI,push viewController 應該在主隊列操作:

-(void)whatYouWantTodo{
  dispatch_async(dispatch_get_main_queue(), ^{
    coding......
  }
}

 

Block回調

  對應屬性導出,native也可以向js傳遞數據,RCTBridgeMethod中定義了一個block typedef void (^RCTResponseSenderBlock)(NSArray *response);用來返回數據給js。   

發送事件到 JavaScript

RCTEventDispatcher類中定義了一些事件處理方法

例如Native 模塊可以在不被直接調用的情況下向 JavaScript 發送事件信號

- (void)calendarEventReminderReceived:(NSNotification *)notification
{
    NSString *eventName = notification.userInfo[@"name"];
    [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder"
                                           body:@{@"name": eventName}];
}
@end

 

JavaScript 代碼可以訂閱這些事件:

var subscription = DeviceEventEmitter.addListener(

    'EventReminder',
    (reminder) => console.log(reminder.name)
);

 

tips:js需要移除訂閱事件

subscription.remove();

 

五、簡單提提JavaScriptCore

OC調用js
-(void)webViewDidFinishLoad:(UIWebView *)webView  
{  
    //網頁加載完成調用此方法     
    //首先創建JSContext 對象(此處通過當前webView的鍵獲取到jscontext)  
    JSContext *context=[webView valueForKeyPath: @"documentView.webView.mainFrame.javaScriptContext"];  
    NSString *alertJS=@"alert('超哥你好,大河向東流')"; //准備執行的js代碼  
    [context evaluateScript:alertJS];//通過oc方法調用js的alert
}

 

  效果如下: 

block中JSContext注意防止循環引用,

  用[JSContext currentContext]

  JSContext *context1 = [[JSContext alloc] init];
context1[@"callback"] = ^{
    JSValue *object = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
    object[@"one"] = @(1);
    object[@"two"] = @(2);
    return object;
};

 

JSValue內存泄漏

  JS中對象為弱類型,OC中為強類型直接保存會內存泄漏

- (void)setOnClickHandler:(JSValue *)handler
{
    _onClickHandler = [JSManagedValue managedValueWithValue:handler];
    [_context.virtualMachine addManagedReference:_onClickHandler
                                    withOwner:self]
}

 

資料參考:
  1. JavaScriptCore的使用和消息傳遞
  2. JavaScriptCore與內存管理
  3. React Native官方文檔中文
  4. 通訊以及消息循環

知識淺薄,如有錯漏請指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM