1. 程式人生 > >載入一個react native 頁面

載入一個react native 頁面

要在原生app中載入js檢視,首先要先將js資原始檔載入到app中,然後使用一個原生類的例項作為容器承接js定義的檢視,最後將這個容器類例項新增到原生檢視中。

載入JS Bundle

js程式碼是以JS Bundle的形式儲存的,在app中使用js中定義的元件、方法之前,首先要載入JS Bundle。

1.獲取JS bundle的資源路徑

如果url已知那就跳過這一步,如果不知道url應該是怎樣的可以看看。

JS bundle放在遠端伺服器上,當遠端伺服器沒有開啟的時候,使用本地預先打包好的JS bundle。(這個思路就只是為了除錯,實際生產中一般不會用這個思路,應該是有更新的時候才會載入遠端伺服器上的js bundle,然後平時都是載入下載好的本地js bundle)

通過url來訪問JS bundle資源。可以使用RCTBundleURLProvider類來獲取JS bundle路徑。它會檢測遠端伺服器是否開啟,如果開啟,則返回遠端伺服器的資源路徑,如果未開啟則返回本地資源路徑。

對於遠端伺服器上的bundle資源路徑一般是這樣的形式http://255.255.255.255:8081/index.ios.bundle?platform=ios&...
在上面的路徑中,255.255.255.255部分為伺服器主機地址,8081部分為埠,index.ios.bundle部分是JS bundle的名稱,platform=ios&...部分是一些引數。RCTBundleURLProvider類分別把以上三部分稱為,host

portbundleRootquery
對於本地資源,RCTBundleURLProvider是通過NSBundle來查詢資源的URL的,因此只要提供本地預先打包的JS bundle的檔名(不帶.bundle字尾),它就可以正確返回本地資源的URL。
RCTBundleURLProvider提供了方法來獲取url:

NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil
];

這個方法使用了預設的配置,如果需要修改port可以通過修改RCTBundleURLProvider.m中的常量kRCTBundleURLProviderDefaultPort。修改host可以將想要的host作為值存放在UserDefaults裡對應鍵@"RCT_jsLocation",例如:

[[NSUserDefaults standardUserDefaults]setValue:@“localhost" forKey:@"RCT_jsLocation"];

當然,也可以不用這個類,自己去實現路徑選擇的邏輯。

2.載入JS Bundle

使用RCTBridge類來載入JS Bundle。這個類就是用來做橋接的,用它可以載入JS Bundle,呼叫這個JS Bundle中用js定義的方法、元件。

初始化RCTBridge例項:

RCTBridge *rctBridge = [[RCTBridge alloc]initWithBundleURL:jsCodeLocation
         moduleProvider:nil
          launchOptions:launchOptions];

這裡的launchOptions一般會傳入appdelegate裡面的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions中的launchOptions。其實對於JS Bundle一般也是在這個方法中完成,也就是在app剛啟動的時候就載入JS Bundle。
這裡的Bundle URL就是在上一節中講到的JS Bundle路徑。

使用JS Bundle中已註冊的元件

對於JS Bundle中已註冊的元件,RN是採用懶載入的方式,也就是在使用RCTBridge載入了JS Bundle以後,其中的元件並沒有被初始化,而是在元件被使用時才會被初始化。在原生程式碼中載入js元件有兩種目的,一種想要使用js中定義的方法,另一種想要展示js中定義的檢視(可以是一個小的檢視也可以是整個頁面)。下面分別介紹針對這兩種目的來載入js元件的方法。

1.載入JS Bundle中已經註冊的元件

RCTBridge兩個方法可以獲取JS Bundle中已註冊元件的例項:

- (id)moduleForName:(NSString *)moduleName;
- (id)moduleForClass:(Class)moduleClass;

這兩個方法分別通過模組(元件)名稱和元件在OC中對於的類來獲取元件的例項,如果該元件已經有例項存在會直接返回,如果這個元件從未被例項化過,就會建立一個例項並返回。如果只是想獲取當前已存在的例項,如果沒有也不建立的話,可以使用以下方法先判斷是否存在相應例項:

- (BOOL)moduleIsInitialized:(Class)moduleClass;

2.在原生檢視中展示JS Bundle中定義的元件(檢視)

在js的元件中,render()方法返回了這個元件的檢視,在OC中要展示一個元件檢視,需要使用RCTRootView類。RCTRootView繼承於UIView,作為一個容器,承接js中定義的元件檢視。在使用上,RCTRootView與一般的UIView最大的不同在於初始化的時候是載入JS Bundle中定義的元件的檢視,一旦初始化以後,就可以像使用一般的UIView一樣使用它。
初始化一個RCTRootView,需要指定所載入的元件的名稱以及定義該元件的JS Bundle檔案,通常有兩種辦法,一種是指定bundleURL和moduleName,另一種是指定bridge和moduleName
對應的方法如下:

  • 指定bundleURL+moduleName:
RCTRootView *rootView = [[RCTRootView alloc]initWithBundleURL:jsCodeLocation moduleName:@"NameOfAJSModule" initialProperties:nil launchOptions:launchOptions];
  • 指定bridge+moduleName:
RCTRootView *rootView = [[RCTRootView alloc]initWithBridge:rctBridge moduleName:@"dealingWithTouch" initialProperties:nil];

這兩種方式是有區別的,使用第二種方法可以實現對同一個RCTBridge例項的複用,而使用第一種方法,每次都會建立一個新的RCTBridge例項。RCTBridge例項越多,意味著JavaScript上下文的越多。如果多個RCTRootView例項所載入的元件都來自於同一個js bundle,通常沒有必要建立多個RCTBridge例項,這時應該使用第二種方法。