React Native 從入門到原理

動態配置

因爲 AppStore 審覈週期的限制,如何動態的更改 app 成爲了永恆的話題。不管採用何種方式,咱們的流程老是能夠歸結爲如下三部曲:「從 Server 獲取配置 --> 解析 --> 執行native代碼」。javascript

不少時候,咱們自覺或者不自覺的利用 JSON 文件實現動態配置的效果,它的核心流程是:html

  1. 經過 HTTP 請求獲取 JSON 格式的配置文件。
  2. 配置文件中標記了每個元素的屬性,好比位置,顏色,圖片 URL 等。
  3. 解析完 JSON 後,咱們調用 Objective-C 的代碼,完成 UI 控件的渲染。

經過這種方法,咱們實現了在後臺配置 app 的展現樣式。從本質上來講,移動端和服務端約定了一套協議,可是協議內容嚴重依賴於應用內要展現的內容,不利於拓展。也就是說,若是業務要求頻繁的增長或修改頁面,這套協議很難應付。前端

最重要的是,JSON 只是一種數據交換的格式,說白了,咱們就是在解析文本數據。這就意味着它只適合提供一些配置信息,而不方便提供邏輯信息。舉個例子,咱們從後臺能夠配置顏色,位置等信息,但若是想要控制 app 內的業務邏輯,就很是複雜了。java

記住,咱們只是在解析字符串,它徹底不具有運行和調試的能力。react

React

不妨暫時拋棄移動端的煩惱,來看看前端的「新玩意」。android

背景

做爲前端小白,我之前對前端的理解是這樣的:git

  • 用 HTML 建立 DOM,構建整個網頁的佈局、結構
  • 用 CSS 控制 DOM 的樣式,好比字體、字號、顏色、居中等
  • 用 JavaScript 接受用戶事件,動態的操控 DOM

在這三者的配合下,幾乎全部頁面上的功能都能實現。但也有比較不爽地方,好比我想動態修改一個按鈕的文字,我須要這樣寫:github

<button type="button" id="button" onclick="onClick()">old button</button>

而後在 JavaScript 中操做 DOM:objective-c

<script>
function onClick() {
  document.getElementById('button').innerHTML='new button';
}
</script>

能夠看到,在 HTML 和 JavaScript 代碼中,id 和 onclick 事件觸發的函數必須徹底對應,不然就沒法正確的響應事件。若是想知道一個 HTML 標籤會如何被響應,咱們還得跑去 JavaScript 代碼中查找,這種原始的配置方式讓我以爲很是不爽。算法

初識 React

隨着 FaceBook 推出了 React 框架,這個問題獲得了大幅度改善。咱們能夠把一組相關的 HTML 標籤,也就是 app 內的 UI 控件,封裝進一個組件(Component)中,我從阮一峯的 React 教程中摘錄了一段代碼:

var MyComponent = React.createClass({
  handleClick: function() {
    this.refs.myTextInput.focus();
  },
  render: function() {
    return (
      <div>
        <input type="text" ref="myTextInput" />
        <input type="button" value="Focus the text input" onClick={this.handleClick} />
      </div>
    );
  }
});

若是你想問:「爲何 JavaScript 代碼裏面出現了 HTML 的語法」,那麼恭喜你已經初步體會到 React 的奧妙了。這種語法被稱爲 JSX,它是一種 JavaScript 語法拓展。JSX 容許咱們寫 HTML 標籤或 React 標籤,它們終將被轉換成原生的 JavaScript 並建立 DOM。

在 React 框架中,除了能夠用 JavaScript 寫 HTML 之外,咱們甚至能夠寫 CSS,這在後面的例子中能夠看到。

理解 React

前端界老是喜歡創造新的概念,彷彿誰說的名詞更晦澀,誰的水平就越高。若是你和當時的我同樣,聽到 React 這個概念一臉懵逼的話,只要記住如下定義便可:

React 是一套能夠用簡潔的語法高效繪製 DOM 的框架

上文已經解釋過了何謂「簡潔的語法」,由於咱們能夠暫時放下 HTML 和 CSS,只關心如何用 JavaScript 構造頁面。

所謂的「高效」,是由於 React 首創了 Virtual DOM 機制。Virtual DOM 是一個存在於內存中的 JavaScript 對象,它與 DOM 是一一對應的關係,也就是說只要有 Virtual DOM,咱們就能渲染出 DOM。

當界面發生變化時,得益於高效的 DOM Diff 算法,咱們可以知道 Virtual DOM 的變化,從而高效的改動 DOM,避免了從新繪製 DOM。

固然,React 並非前端開發的所有。從以前的描述也能看出,它專一於 UI 部分,對應到 MVC 結構中就是 View 層。要想實現完整的 MVC 架構,還須要 Model 和 Controller 的結構。在前端開發時,咱們能夠採用 Flux 和 Redux 架構,它們並不是框架(Library),而是和 MVC 同樣都是一種架構設計(Architecture)。

若是不從事前端開發,就不用深刻的掌握 Flux 和 Redux 架構,但理解這一套體系結構對於後面理解 React Native 很是重要

React Native

分別介紹完了移動端和前端的背景知識後,本文的主角——React Native 終於要登場了。

融合

前面咱們介紹了移動端經過 JSON 文件傳遞信息的不足之處:只能傳遞配置信息,沒法表達邏輯。從本質上講,這是由於 JSON 畢竟只是純文本,它缺少像編程語言那樣的運行能力。

而 React 在前端取得突破性成功之後,JavaScript 佈道者們開始試圖一統三端。他們利用了移動平臺可以運行 JavaScript 代碼的能力,而且發揮了 JavaScript 不只僅能夠傳遞配置信息,還能夠表達邏輯信息的優勢。

當痛點趕上特色,二者一拍即合,因而乎:

一個基於 JavaScript,具有動態配置能力,面向前端開發者的移動端開發框架,React Native,誕生了!

看到了麼,這是一個面向前端開發者的框架。它的宗旨是讓前端開發者像用 React 寫網頁那樣,用 React Native 寫移動端應用。這就是爲何 React Native 自稱:

Learn once,Write anywhere!

而非不少跨平臺語言,項目所說的:

Write once, Run anywhere!

React Native 但願前端開發者學習完 React 後,可以用一樣的語法、工具等,分別開發安卓和 iOS 平臺的應用而且不用一行原生代碼。

若是用一個詞歸納 React Native,那就是:Native 版本的 React

原理概述

React Native 不是黑科技,咱們寫的代碼老是以一種很是合理,能夠解釋的方式的運行着,只是絕大多數人沒有理解而已。接下來我以 iOS 平臺爲例,簡單的解釋一下 React Native 的原理。

首先要明白的一點是,即便使用了 React Native,咱們依然須要 UIKit 等框架,調用的是 Objective-C 代碼。總之,JavaScript 只是輔助,它只是提供了配置信息和邏輯的處理結果。React Native 與 Hybrid 徹底沒有關係,它只不過是以 JavaScript 的形式告訴 Objective-C 該執行什麼代碼。

其次,React Native 可以運行起來,全靠 Objective-C 和 JavaScript 的交互。對於沒有接觸過 JavaScript 的人來講,很是有必要理解 JavaScript 代碼如何被執行。

咱們知道 C 系列的語言,通過編譯,連接等操做後,會獲得一個二進制格式的可執行文,所謂的運行程序,實際上是運行這個二進制程序。

而 JavaScript 是一種腳本語言,它不會通過編譯、連接等操做,而是在運行時才動態的進行詞法、語法分析,生成抽象語法樹(AST)和字節碼,而後由解釋器負責執行或者使用 JIT 將字節碼轉化爲機器碼再執行。整個流程由 JavaScript 引擎負責完成。

蘋果提供了一個叫作 JavaScript Core 的框架,這是一個 JavaScript 引擎。經過下面這段代碼能夠簡單的感覺一下 Objective-C 如何調用 JavaScript 代碼:

JSContext *context = [[JSContext alloc] init];
JSValue *jsVal = [context evaluateScript:@"21+7"];
int iVal = [jsVal toInt32];

這裏的 JSContext 指的是 JavaScript 代碼的運行環境,經過 evaluateScript 便可執行 JavaScript 代碼並獲取返回結果。

JavaScript 是一種單線程的語言,它不具有自運行的能力,所以老是被動調用。不少介紹 React Native 的文章都會提到 「JavaScript 線程」 的概念,實際上,它表示的是 Objective-C 建立了一個單獨的線程,這個線程只用於執行 JavaScript 代碼,並且 JavaScript 代碼只會在這個線程中執行。

Objective-C 與 JavaScript 交互

提到 Objective-C 與 JavaScript 的交互,不得不推薦 bang神的這篇文章:React Native通訊機制詳解。雖然其中很多細節都已通過時,可是總體的思路值得學習。

本節主要分析 Objective-C 與 JavaScript 交互時的整理邏輯與流程,下一節將經過源碼來分析具體原理。

JavaScript 調用 Objective-C

因爲 JavaScript Core 是一個面向 Objective-C 的框架,在 Objective-C 這一端,咱們對 JavaScript 上下文知根知底,能夠很容易的獲取到對象,方法等各類信息,固然也包括調用 JavaScript 函數。

真正複雜的問題在於,JavaScript 不知道 Objective-C 有哪些方法能夠調用。

React Native 解決這個問題的方案是在 Objective-C 和 JavaScript 兩端都保存了一份配置表,裏面標記了全部 Objective-C 暴露給 JavaScript 的模塊和方法。這樣,不管是哪一方調用另外一方的方法,實際上傳遞的數據只有 ModuleIdMethodId 和 Arguments 這三個元素,它們分別表示類、方法和方法參數,當 Objective-C 接收到這三個值後,就能夠經過 runtime 惟一肯定要調用的是哪一個函數,而後調用這個函數。

再次重申,上述解決方案只是一個抽象概念,可能與實際的解決方案有微小差別,好比實際上 Objective-C 這一端,並無直接保存這個模塊配置表。具體實現將在下一節中隨着源碼一塊兒分析。

閉包與回調

既然說到函數互調,那麼就不得不提到回調了。對於 Objective-C 來講,執行完 JavaScript 代碼再執行 Objective-C 回調毫無難度,難點依然在於 JavaScript 代碼調用 Objective-C 以後,如何在 Objective-C 的代碼中,回調執行 JavaScript 代碼。

目前 React Native 的作法是:在 JavaScript 調用 Objective-C 代碼時,註冊要回調的 Block,而且把 BlockId 做爲參數發送給 Objective-C,Objective-C 收到參數時會建立 Block,調用完 Objective-C 函數後就會執行這個剛剛建立的 Block。

Objective-C 會向 Block 中傳入參數和 BlockId,而後在 Block 內部調用 JavaScript 的方法,隨後 JavaScript 查找到當時註冊的 Block 並執行。

圖解

好吧,若是你是新手,而且堅持讀到了這裏,估計已經懵逼了。不要擔憂,與 JavaScript 的交互確實不是一會兒可以徹底理清楚的,你能夠先參考這個示意圖:

交互流程

注:

  1. 本圖由 bang 的文章中的圖片修改而來
  2. 本圖只是一個簡單的示意圖,不建議當作時序圖使用,請參考下一節源碼分析。
  3. Objective-C 和 JavaScript 的交互老是由前者發起,本圖爲了簡化,省略了這一步驟。

React Native 源碼分析

要想深刻理解 React Native 的工做原理,有兩個部分有必要閱讀一下,分別是初始化階段和方法調用階段。

爲了提煉出代碼的核心含義,我會在不改變代碼意圖的基礎上對它作一些刪改,以便閱讀。

寫這篇文章是,React Native 還處於 0.27 版本,因爲在 1.0 以前的變更幅度相對較大,所以下面的源碼分析極可能隨着 React Native 的演變而過期。但無論什麼時候,把下面的源碼讀一遍都有助於你加深對 React Native 原理的理解。

初始化 React Native

每一個項目都有一個入口,而後進行初始化操做,React Native 也不例外。一個不含 Objective-C 代碼的項目留給咱們的惟一線索就是位於 AppDelegate文件中的代碼:

RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                    moduleName:@"PropertyFinder"
                                             initialProperties:nil
                                                 launchOptions:launchOptions];

用戶能看到的一切內容都來源於這個 RootView,全部的初始化工做也都在這個方法內完成。

在這個方法內部,在建立 RootView 以前,React Native 實際上先建立了一個 Bridge 對象。它是 Objective-C 與 JavaScript 交互的橋樑,後續的方法交互徹底依賴於它,而整個初始化過程的最終目的其實也就是建立這個橋樑對象。

初始化方法的核心是 setUp 方法,而 setUp 方法的主要任務則是建立 BatchedBridge

BatchedBridge 的做用是批量讀取 JavaScript 對 Objective-C 的方法調用,同時它內部持有一個 JavaScriptExecutor,顧名思義,這個對象用來執行 JavaScript 代碼。

建立 BatchedBridge 的關鍵是 start 方法,它能夠分爲五個步驟:

  1. 讀取 JavaScript 源碼
  2. 初始化模塊信息
  3. 初始化 JavaScript 代碼的執行器,即 RCTJSCExecutor 對象
  4. 生成模塊列表並寫入 JavaScript 端
  5. 執行 JavaScript 源碼

咱們逐個分析每一步完成的操做:

讀取 JavaScript 源碼

這一部分的具體代碼實現沒有太大的討論意義。咱們只要明白,JavaScript 的代碼是在 Objective-C 提供的環境下運行的,因此第一步就是把 JavaScript 加載進內存中,對於一個空的項目來講,全部的 JavaScript 代碼大約佔用 1.5 Mb 的內存空間。

須要說明的是,在這一步中,JSX 代碼已經被轉化成原生的 JavaScript 代碼。

初始化模塊信息

這一步在方法 initModulesWithDispatchGroup: 中實現,主要任務是找到全部須要暴露給 JavaScript 的類。每個須要暴露給 JavaScript 的類(也成爲 Module,如下不做區分)都會標記一個宏:RCT_EXPORT_MODULE,這個宏的具體實現並不複雜:

#define RCT_EXPORT_MODULE(js_name) \
RCT_EXTERN void RCTRegisterModule(Class); \
+ (NSString *)moduleName { return @#js_name; } \
+ (void)load { RCTRegisterModule(self); }

這樣,這個類在 load 方法中就會調用 RCTRegisterModule 方法註冊本身:

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

  [RCTModuleClasses addObject:moduleClass];
}

所以,React Native 能夠經過 RCTModuleClasses 拿到全部暴露給 JavaScript 的類。下一步操做是遍歷這個數組,而後生成 RCTModuleData 對象:

for (Class moduleClass in RCTGetModuleClasses()) {
    RCTModuleData *moduleData = [[RCTModuleData alloc]initWithModuleClass:moduleClass                                                                      bridge:self];
    [moduleClassesByID addObject:moduleClass];
    [moduleDataByID addObject:moduleData];
}

能夠想見,RCTModuleData 對象是模塊配置表的主要組成部分。若是把模塊配置表想象成一個數組,那麼每個元素就是一個 RCTModuleData 對象。

這個對象保存了 Module 的名字,常量等基本信息,最重要的屬性是一個數組,保存了全部須要暴露給 JavaScript 的方法。

暴露給 JavaScript 的方法須要用 RCT_EXPORT_METHOD 這個宏來標記,它的實現原理比較複雜,有興趣的讀者能夠自行閱讀。簡單來講,它爲函數名加上了 __rct_export__ 前綴,再經過 runtime 獲取類的函數列表,找出其中帶有指定前綴的方法並放入數組中:

- (NSArray<id<RCTBridgeMethod>> *)methods{
    unsigned int methodCount;
    Method *methods = class_copyMethodList(object_getClass(_moduleClass), &methodCount); // 獲取方法列表
    for (unsigned int i = 0; i < methodCount; i++) {
        RCTModuleMethod *moduleMethod = /* 建立 method */
        [_methods addObject:moduleMethod];
      }
    }
    return _methods;
}

所以 Objective-C 管理模塊配置表的邏輯是:Bridge 持有一個數組,數組中保存了全部的模塊的 RCTModuleData 對象。只要給定 ModuleId 和 MethodId 就能夠惟一肯定要調用的方法。

初始化 JavaScript 代碼的執行器,即 RCTJSCExecutor 對象

經過查看源碼能夠看到,初始化 JavaScript 執行器的時候,addSynchronousHookWithName 這個方法被調用了屢次,它其實向 JavaScript 上下文中添加了一些 Block 做爲全局變量:

- (void)addSynchronousHookWithName:(NSString *)name usingBlock:(id)block {
    self.context.context[name] = block;
}

有些同窗讀源碼時可能會走進一個誤區,若是在 Block 中打一個斷點就會發現,Block 實際上是被執行了,但卻找不到任何可以執行 Block 的代碼。

這實際上是由於這個 Block 並不是由 Objective-C 主動調用,而是在第五步執行 JavaScript 代碼時,由 JavaScript 在上下文中獲取到 Block 對象並調用,有興趣的讀者能夠自行添加斷點並驗證。

這裏咱們須要重點注意的是名爲 nativeRequireModuleConfig 的 Block,它在 JavaScript 註冊新的模塊時調用:

get: () => {
    let module = RemoteModules[moduleName];
    const json = global.nativeRequireModuleConfig(moduleName); // 調用 OC 的 Block
    const config = JSON.parse(json); // 解析 json
    module = BatchedBridge.processModuleConfig(config, module.moduleID); // 註冊 config
    return module;
},

這就是模塊配置表可以加載到 JavaScript 中的原理。

另外一個值得關注的 Block 叫作 nativeFlushQueueImmediate。實際上,JavaScript 除了把調用信息放到 MessageQueue 中等待 Objective-C 來取之外,也能夠主動調用 Objective-C 的方法:

if (global.nativeFlushQueueImmediate &&
    now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
    global.nativeFlushQueueImmediate(this._queue); // 調用 OC 的代碼
}

目前,React Native 的邏輯是,若是消息隊列中有等待 Objective-C 處理的邏輯,並且 Objective-C 超過 5ms 都沒有來取走,那麼 JavaScript 就會主動調用 Objective-C 的方法:

[self addSynchronousHookWithName:@"nativeFlushQueueImmediate" usingBlock:^(NSArray<NSArray *> *calls){
    [self->_bridge handleBuffer:calls batchEnded:NO];
}];

這個 handleBuffer 方法是 JavaScript 調用 Objective-C 方法的關鍵,在下一節——方法調用中,我會詳細分析它的實現原理。

通常狀況下,Objective-C 會定時、主動的調用 handleBuffer 方法,這有點相似於輪詢機制:

// 每一個一段時間發生一次:
Objective-C:嘿,JavaScript,有沒有要調用個人方法呀?
JavaScript:有的,你從 MessageQueue 裏面取出來。

然而因爲卡頓或某些特殊緣由,Objective-C 並不能老是保證可以準時的清空 MessageQueue,這就是爲何 JavaScript 也會在必定時間後主動的調用 Objective-C 的方法。查看上面 JavaScript 的代碼能夠發現,這個等待時間是 5ms。

請緊緊記住這個 5ms,它告訴咱們 JavaScript 與 Objective-C 的交互是存在必定開銷的,否則就不會等待而是每次都馬上發起請求。其次,這個時間開銷大約是毫秒級的,不會比 5ms 小太多,不然等待這麼久就意義不大了。

生成模塊配置表並寫入 JavaScript 端

複習一下 nativeRequireModuleConfig 這個 Block,它能夠接受 ModuleName 而且生成詳細的模塊信息,但在前文中咱們沒有提到 JavaScript 是如何知道 Objective-C 要暴露哪些類的(目前只是 Objective-C 本身知道)。

這一步的操做就是爲了讓 JavaScript 獲取全部模塊的名字:

- (NSString *)moduleConfig{
    NSMutableArray<NSArray *> *config = [NSMutableArray new];
    for (RCTModuleData *moduleData in _moduleDataByID) {
      [config addObject:@[moduleData.name]];
    }
}

查看源碼能夠發現,Objective-C 把 config 字符串設置成 JavaScript 的一個全局變量,名字叫作:__fbBatchedBridgeConfig

執行 JavaScript 源碼

這一步也沒什麼技術難度能夠,代碼已經加載進了內存,該作的配置也已經完成,只要把 JavaScript 代碼運行一遍便可。

運行代碼時,第三步中所說的那些 Block 就會被執行,從而向 JavaScript 端寫入配置信息。

至此,JavaScript 和 Objective-C 都具有了向對方交互的能力,準備工做也就所有完成了。

畫了一個簡陋的時序圖以供參考:

初始化過程

方法調用

如前文所述,在 React Native 中,Objective-C 和 JavaScript 的交互都是經過傳遞 ModuleIdMethodId 和 Arguments 進行的。如下是分狀況討論:

調用 JavaScript 代碼

也許你在其餘文章中曾經屢次據說 JavaScript 代碼老是在一個單獨的線程上面調用,它的實際含義是 Objective-C 會在單獨的線程上運行 JavaScript 代碼:

- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
{
  if ([NSThread currentThread] != _javaScriptThread) {
    [self performSelector:@selector(executeBlockOnJavaScriptQueue:)
                 onThread:_javaScriptThread withObject:block waitUntilDone:NO];
  } else {
    block();
  }
}

調用 JavaScript 代碼的核心代碼以下:

- (void)_executeJSCall:(NSString *)method
             arguments:(NSArray *)arguments
              callback:(RCTJavaScriptCallback)onComplete{
    [self executeBlockOnJavaScriptQueue:^{
        // 獲取 contextJSRef、methodJSRef、moduleJSRef
        resultJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)methodJSRef, (JSObjectRef)moduleJSRef, arguments.count, jsArgs, &errorJSRef);
        objcValue = /*resultJSRef 轉換成 Objective-C 類型*/
        onComplete(objcValue, nil);
    }];
}

須要注意的是,這個函數名是咱們要調用 JavaScript 的中轉函數名,好比 callFunctionReturnFlushedQueue。也就是說它的做用實際上是處理參數,而非真正要調用的 JavaScript 函數。

這個中轉函數接收到的參數包含了 ModuleIdMethodId 和 Arguments,而後由中轉函數查找本身的模塊配置表,找到真正要調用的 JavaScript 函數。

在實際使用的時候,咱們能夠這樣發起對 JavaScript 的調用:

[_bridge.eventDispatcher sendAppEventWithName:@"greeted"
                                         body:@{ @"name": @"nmae"}];

這裏的 Name 和 Body 參數分別表示要調用的 JavaScript 的函數名和參數。

JavaScript 調用 Objective-C

在調用 Objective-C 代碼時,如前文所述,JavaScript 會解析出方法的 ModuleIdMethodId 和 Arguments 並放入到 MessageQueue 中,等待 Objective-C 主動拿走,或者超時後主動發送給 Objective-C。

Objective-C 負責處理調用的方法是 handleBuffer,它的參數是一個含有四個元素的數組,每一個元素也都是一個數組,分別存放了ModuleIdMethodIdParams,第四個元素目測用處不大。

函數內部在每一次方調用中調用 _handleRequestNumber:moduleID:methodID:params 方法。,經過查找模塊配置表找出要調用的方法,並經過 runtime 動態的調用:

[method invokeWithBridge:self module:moduleData.instance arguments:params];

在這個方法中,有一個很關鍵的方法:processMethodSignature,它會根據 JavaScript 的 CallbackId 建立一個 Block,而且在調用完函數後執行這個 Block。

實戰應用

俗話說:「思而不學則神棍」,下面舉一個例子來演示 Objective-C 是如何與 JavaScript 進行交互的。首先新建一個模塊:

// .h 文件
#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"

@interface Person : NSObject<RCTBridgeModule, RCTBridgeMethod>

@end

Person 這個類是一個新的模塊,它有兩個方法暴露給 JavaScript:

#import "Person.h"
#import "RCTEventDispatcher.h"
#import "RCTConvert.h"

@implementation Person
@synthesize bridge = _bridge;

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(greet:(NSString *)name)
{
  NSLog(@"Hi, %@!", name);
  [_bridge.eventDispatcher sendAppEventWithName:@"greeted"
                                           body:@{ @"name": @"nmae"}];
}

RCT_EXPORT_METHOD(greetss:(NSString *)name name2:(NSString *)name2 callback:(RCTResponseSenderBlock)callback)
{
  NSLog(@"Hi, %@! %@!!!", name, name2);
  callback(@[@[@12,@23,@34]]);
}

@end

在 JavaScript 中,能夠這樣調用:

Person.greet('Tadeu');
Person.greetss('Haha', 'Heihei', (events) => {
  for (var i = 0; i < events.length; i++) {
    console.log(events[i]);
  }
});

有興趣的同窗能夠複製以上代碼並自行調試。

React Native 優缺點分析

通過一長篇的討論,其實 React Native 的優缺點已經不難分析了,這裏簡單總結一下:

優勢

  1. 複用了 React 的思想,有利於前端開發者涉足移動端。
  2. 可以利用 JavaScript 動態更新的特性,快速迭代。
  3. 相比於原平生臺,開發速度更快,相比於 Hybrid 框架,性能更好。

缺點

  1. 作不到 Write once, Run everywhere,也就是說開發者依然須要爲 iOS 和 Android 平臺提供兩套不一樣的代碼,好比參考官方文檔能夠發現很多組件和API都區分了 Android 和 iOS 版本。即便是共用組件,也會有平臺獨享的函數。

  2. 不能作到徹底屏蔽 iOS 端或 Android 的細節,前端開發者必須對原平生臺有所瞭解。加劇了學習成本。對於移動端開發者來講,徹底不具有用 React Native 開發的能力。

  3. 因爲 Objective-C 與 JavaScript 之間切換存在固定的時間開銷,因此性能一定不及原生。好比目前的官方版本沒法作到 UItableview(ListView) 的視圖重用,由於滑動過程當中,視圖重用須要在異步線程中執行,速度太慢。這也就致使隨着 Cell 數量的增長,佔用的內存也線性增長。

綜上,我對 React Native 的定位是:

利用腳本語言進行原平生臺開發的一次成功嘗試,下降了前端開發者入門移動端的門檻,必定業務場景下具備獨特的優點,幾乎不可能取代原平生臺開發。

參考資料

  1. React Native 官方文檔React Native 官方文檔中文版
  2. React Native通訊機制詳解
  3. React 入門實例教程
相關文章
相關標籤/搜索