一、簡單介紹
不同於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類
- Module類需要導入頭文件#import "RCTBridgeModule.h"並遵守RCTBridgeModule協議
- 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]
}
資料參考:
知識淺薄,如有錯漏請指正。