不知從什麼時候起,移動端App開發,採用Native仍是使用Web的爭論不絕於耳。兩者的優缺點再也不贅述。Web App固然是開發者期待的理想結果,可是因爲Native App在用戶體驗上的絕對碾壓,大部分移動端App仍是採用Native的方式,少數架構複雜、對Web依賴較多的App,會採用一種稱爲Hybrid(Web + Native)的開發方式,在iOS上,Native經過-[UIWebView stringByEvaluatingJavaScriptFromString:]調用Web,而Web則是經過設置WebView iframe的src,搭建JSBridge進行通信。因爲微信和手機支付寶的的成功,Hybrid App這種開發方式確實引發了關注,但從我這麼一個最底層iOS開發者的技術角度來講,這種JSBridge的通信方式,實在不是特別高明,能解決的場景也十分有限。css
前幾天FB正式推出了React Native。因爲慣性思惟,我總想着往它身上貼個「Web」或者「Native」或者「Hybrid」的標籤,但是貼上去扯下來,並無一個適合的標籤。事實上,React Native從新定義了一種新的模式。html
在說React Native以前,讓咱們以WebKit爲例,先扯一扯一個瀏覽器引擎的工做流程。從下圖能夠看出,一個網頁的生命週期,大體經歷了加載、解析、排版、繪製(JS引擎暫時不提)。前端
接觸過iOS平臺上的簡易的瀏覽器引擎,大體的工做流程,也是如此。因爲加載流程涉及網絡模塊,部分排版和渲染流程涉及Native UI控件,爲解決不一樣平臺的差別性,通常是抽象接口,由不一樣平臺實現各自的網絡模塊和網頁的繪製。react
簡單來講,一個瀏覽器渲染引擎,其實就是將網頁從服務器或者本地load下來,用一套規則解釋這個網頁,最後用平臺最舒服的方式,展示到屏幕上去。git
因爲對瀏覽器印象深入,這是React Native給個人第一印象。因爲我對前端的瞭解,只停留在html和Javascript的簡單語法上,徹底不知ReactJS爲什麼物,因此我只能嘗試着從開源的iOS React Native的OC端代碼,解釋一下。github
得益於JavascriptCore,React Native可以拋棄WebView直接運行JS,在React Native,OC層只負責控制程序生命週期,以及提供平臺Native控件的工做;而JS層則負責提供數據,響應交互事件,充當了DataSource和Delegate的角色。react-native
這裏的通訊,是指JS和OC之間的通訊。瀏覽器
前面已經提到,OCBridge是利用JavascriptCore直接調用JS代碼的。OC層實現這個類的是RCTBridge,目前的代碼是使用RCTContextExecutor做爲具體的執行者。JavascriptCore是iOS7纔開放的接口,不過目前的代碼還有另一套RCTWebViewExecutor,裏面用的是經過UIWebView調用JS,多是爲了之後兼容舊版本的iOS。使用JavascriptCore最顯而易見的優點就是,整個執行過程均可以在後臺線程執行,事實上RCTContextExecutor單獨開了一個名爲「com.facebook.React.JavaScript」的線程,供本身使用。服務器
上面只提到OCBridge,那JSBridge呢?微信
答案是,沒有JSBridge。前面提到,OC層提供Native控件,JS層更多地是扮演DataSource和Delegate的角色。回想一下UITableview的使用,爲UITableview設置DataSource和Delegate以後,使用者並不須要關心UITableview是如何被建立繪製,以及如何監聽點擊長按之類的交互事件。同理,JS層做爲使用者,並不須要關心Native事件是如何觸發的,須要關心的是,當事件觸發時該如何響應。因此,一個本來須要雙向通訊的機制,被簡化成單向通訊。
這個機制,能夠經過查看 -[RCTBridge enqueueJSCall:args:]這個函數的Callers來驗證(這個函數是OC層調用JS的入口函數),它的 Callers包括了:Device Event(如先後臺切換)、Input State(如控件Value改變)、Timer回調、Touch事件回調等等。
那JS層是如何實現調用OC層的呢?是經過返回值。在事件觸發OC層調用JS以後,會得到一段JSON數據做爲返回值,OC層只須要按照協議,解析這段JSON數據,依次調用Native代碼便可。
JS調用OC的協議,是-[RCTBridge setUp]的時候,經過 RCTRemoteModulesConfig()建立並傳給JS層的。 RCTRemoteModulesConfig()主要作了幾個事情:
當JS返回JSON數據時,實際上返回了一段包含了moduleID和methodID的隊列,OC層按照協議的約定,執行對應方法。
至於OC調用JS的協議,也是經過module、method來標識的。不過這些module、method都是OC層寫死的字符串,應該是和JS強綁定的,沒有啥特殊之處。
瀏覽器引擎,離不開的就是dom tree 和render tree。簡單來講,dom tree 是根據源數據解析而來的,包含了原始的節點信息;而render tree 則是dom tree + css。排版的目的,就是生成render tree,肯定每一個節點在屏幕上的大小位置。
在React Native中,解析過程是在JS層完成的,原理未知。在OC層,RCTUIManager負責將JS層的解析結果,映射到OC層的視圖層級,它自己不作任何的解析操做,只是提供方法,讓JS層調用而已。最終dom tree映射到OC層的結果,是一棵「RCTShadowView tree」。RCTShadowView這個名字也起得頗有意思,它不是真正展示的視圖,只是一個映射結果而已,每個RCTShadowView對應一個真正的視圖。RCTShadowView的另外一個意義在於,它擁有一個成員變量cssNode,能夠經過FB的開源項目css-layout(代碼裏面可貴一見的兩個C文件),完成排版。剩下的細節工做,就交給RCTShadowView對應的真實視圖了。
其實一開始並無打算看源碼的,只是由於Demo中一張圖片沒法顯示,讓我不得不調試圖片下載模塊來肯定問題 -_-|||(圖片下載使用的是NSURLSession,這貨也是iOS7纔有的接口,看來React Native還沒打算支持舊版本的iOS)。時間匆忙,水平有限,確定錯誤連篇,還望指正。