寫點關於RN的熱更新和RN版本升級后的強制更新。以及優化白屏問題
在APPDelegate中加載RN,一般的加載方式是:RCTRootView *rootView= [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"authen_native" initialProperties:nil launchOptions:nil];
1 |
- (instancetype)initWithBundleURL:(NSURL *)bundleURL |
但在調試中發現兩個現象:
1.重復進入react-native頁面、退出react-native頁面的操作,RCTBridge對象會被重復創建、銷毀。有時候RCTBridge對象未能及時創建還會crash
2.在原生頁面和react-native頁面相互跳轉是RCTBridge也會被重復創建,造成很大的內存開銷
閱讀RCTRootView.h發現一些細節:
1 |
|
initWithBundleURL與initWithBridge的區別
對於項目中只有一個RCTRootView的時候建議initWithBundleURL的方法,這個方法內部創建了一個RCTBridge.
而有多個RCTRootView的情況,建議initWithBridge的方法.開發者直接創建RCTBridge,多個RCTRootView可共用一個RCTBridge。
項目中使用多個RCTRootView,推薦使用以下方法initWithBridge初始化:
1 |
_bridge = [[RCTBridge alloc] initWithBundleURL:[SDRrectFileOption SetFileWithOption:self.luanchOption] |
在SDRrectFileOption中返回的是jsbundle的地址。在這個文件中可以使用NSFileManager來把jsbundle緩存到本地。但是如果是新版本的RN比如0.57要替換老版本的比如0.54的APP覆蓋更新的話,記得要對比版本號,然后把緩存里面的jsbundle清除掉再返回新的jsbundle地址。不然會導致crash。
RN的熱更新
在APPdelegate的didFinishLaunchingWithOptions方法中來判斷是否需要Update。在Update方法中如果需要強制更新的話就就把RCTBridge調用reload方法進行熱更新—和初始化使用的是同一個bridge。
1 |
- (void)checkUpdate { |
在patchClass中使用的是單例,在這個里面通過接口判斷是否需要熱更還是強制更新,是只更新jsbundle還是整包更新,下載文件,把下載的壓縮文件解壓縮,如果緩存里面有文件先刪除舊的jsbundle再保存,
大專欄 關於RN的熱更新ink" title="解決白屏問題">解決白屏問題
使用單例初始化一個bridge對象解決上述問題:
1 |
//.h |
單例在程序啟動時初始化。
測試驗證可以發現:內存得到優化,白屏問題得到解決。
橋接原生模塊
首先我們需要創建一個類,然后導入頭文件 #import <RCTBridgeModule.h> ,這個類需要實現 RCTBridgeModule 協議。
1 |
|
模塊名字
在類的實現部分,需要包含 RCT_EXPORT_MODULE() 宏,這個宏也可以添加一個參數用來指定在 JS 中訪問這個模塊的名字。如果你不指定,默認就會使用這個 OC 類的名字。
導出方法
RCT_EXPORT_METHOD(),導出到 JS 的方法名是 OC 的方法名的第一個部分,橋接到 JS 的方法返回值類型必須是 void。RN 的橋接操作是異步的,所以如果要返回結果給 JS,你必須通過回調或者觸發事件來進行。傳入的參數類型有以下幾種:
string (NSString)
number (NSInteger, float, double, CGFloat, NSNumber)
boolean (BOOL, NSNumber)
array (NSArray) 包含本列表中任意類型
object (NSDictionary) 包含string類型的鍵和本列表中任意類型的值
function (RCTResponseSenderBlock)
回調函數
RCT_EXPORT_METHOD(RNInvokeOCPromise:(NSDictionary *)dictionary resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject){
}
橋接原生方法的最后兩個參數是RCTPromiseResolveBlock 和RCTPromiseRejectBlock的話,則對應的JS方法就會返回一個Promise對象。
設置原生模塊執行操作的線程
如果你在原生模塊中需要更改 UI 或者必須在主線程的話,可以實現
- (dispatch_queue_t)methodQueue 方法
- (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); }
RN使用原生的View
import入UIView+React.h文件,,原生視圖都需要被一個RCTViewManager的子類來創建和管理。這些管理器在功能上有些類似“視圖控制器”,但它們本質上都是單例 —— React Native只會為每個管理器創建一個實例。步驟:
- 創建一個子類
- 添加 RCT_EXPORT_MODULE()標記宏
- 實現 -(UIView *)view 方法
創建一個子類
該組件有回調需要處理,這里即必須用到 RCTDirectEventBlock 或者 RCTBubblingEventBlock,而且命名的時候要特別注意,需要已 on 開頭,熟悉 JS 的朋友應該會反應過來,這很像 JS 的事件命名規范。
@property (nonatomic, copy) RCTBubblingEventBlock onValueChange;
@property (nonatomic, copy) RCTBubblingEventBlock onSlidingComplete;
在自定義ViewManager中
- 初始化子View
- 添加 RCT_EXPORT_MODULE()標記宏
添加TYRCBarChartViewManager 來管理TYRCBarChartView。這個TYRCBarChartViewManager : 繼承自RCTViewManager。 RCTViewManager 實現 RCTBridgeModule 協議。 - 自定義屬性RCT_CUSTOM_VIEW_PROPERTY
- 自定義方法RCT_EXPORT_METHOD(refresh){
[_barView refreshData];
}
RCT_CUSTOM_VIEW_PROPERTY(name, type, viewClass),完整的屬性定義為
給JS發送事件使用 eventDispatcher
[self.rootView.bridge.eventDispatcher sendAppEventWithName:@”deviceLocalStateChange”
body:@{@”state”:state}];
參考資料
ios2.1大禮包被拒經驗分享https://zhuanlan.zhihu.com/p/54042709