React Native 是一個 JavaScript 的框架,用來撰寫實時的、可原生呈現 iOS 和 Android 的應用。其是基於 React的,而 React 是 Facebook 的用於構建用戶界面的 JavaScript 庫,但是這里不是給瀏覽器解釋的,而是為移動平台。換句話說:如果你是一名 web 開發者,你可以使用熟悉的框架和單一的 JavaScript 代碼庫,即 React Native來撰寫清晰的、高效的移動應用。
我們以前都聽說過那些個通用的 app 開發,比如框架 Cordova 和 Titanium,那實際使用 React Native 是一種什么樣的情況了呢?在本文中,我們會解釋 React Native 到底是什么,以及其是如何工作的,再然后我們以撰寫實際的 iOS 和 Android 應用來一探 React Native 究竟。在最后,希望讀者能夠看到有足夠的理由在下一個移動應用中選擇使用 React Native。
那么什么是 React Native?
在我們深入到開發的經歷之前,我們先來探討下 React Native是什么,並花一些時間來講述下它是如何工作的。
也只是 React
React 是用於構建用戶界面,通常是基於 web 的 JavaScript 程序庫。由 Facebook 開發並在2013年將其開源,React 已經得到了頗為廣泛的使用。但是其使用的范圍比較狹窄,它僅是用於渲染用戶的應用程序的界面,而不是更大的 MVC 框架。
React 讓開發者們紛涌而至的原因有很多,諸如輕量級、驕人的性能、尤其是可以快速的變更數據。這均與它的組件結構密切相關,它也同樣鼓勵人們去撰寫更加模塊化的、可重用的代碼。
React Native 也只是 React,但是是針對移動設備的。也有一些少許的不一樣的地方,比如開發者需要使用<View>
組件而不是<div>
,<Image>
代替<img>
。開發者的體驗多數是一致的。當然,有一些 Objective-C 和 Java 知識是非常有用的,移動開發有其自身的一些頗為棘手的問題(我在多個移動設備上測試過沒有?觸摸屏的目標是否夠大?)。即使是這樣,若是開發者曾經有過在瀏覽器下大 React 經驗的話,開發 React Native 會感覺非常的熟悉,並且不會感到不適應。
它是真正的原生
關於 React Native 最為讓人們稱奇的第一件事就是它是”真正的“原生。其它的用於移動設備的 JavaScript 均是以某種包裝后的 Web 視角封裝了用戶的 JavaScripts 代碼。他們也許也重新實現了一些本地 UI 的行為,諸如動畫之類的,但是用戶寫的仍然是基於 Web 的應用。
在React中,一個組件能夠描述自身的外觀;React 處理后再呈現給用戶。一個非常清晰的抽象層將兩個功能給隔離開來。為了呈現組件給 web,React 使用的是標准的 HTML 標簽。這和抽象層是一個道理,即是“橋”的概念,去讓 React Native 調用實際的 iOS 和 Android 呈現的 API。在 iOS 中,這意味着 React Native 組件呈現給真正的 UI 視圖;在 Android 下,他們也呈現給了本地的視圖。
你將寫出看起來差不多就是標准的 JavaScript、CSS、HTML 代碼。取代編譯為本地代碼,React Native 會將你的應用運行在本地平台的 JavaScript 引擎中,毋需屏蔽主要的 UI 線程。你獲得了本地的性能、動畫、行為,還不用去寫 Object-C 或 Java。其它諸如 Cordova 或 Titanium 之類的跨平台應用開發,是無法達到如此級別的本地性能和表現的。
更好的開發者體驗
相比較於 iOS 和 Android 原生的開發,React Native 提供更好的開發者體驗。因為你的程序大多數都是 JavaScript,你可以從 web 開發中汲取大量的經驗,比如能夠立即“刷新”你的應用來查看你代碼的修改。相比於在傳統的應用開發中花很長的時間去等待構建的過程,會讓人感覺這簡直是天賜之物。
另外,React Native 還為開發者提供了智能的錯誤報告和標准的 JavaSript 調試工具,這些讓移動開發更加的順手。
掌控多平台
React Native 可以平滑的掌控多個平台。絕大多數的 React Native API 都是跨平台的,所以開發者只需要去寫 React Native 的組件即可,它可以無縫的運行在 iOS 和 Android 下,臉書聲稱他們的廣告管理應用跨兩個平台有87% 的代碼可重用,還有,我自己所寫的 flashcard 的代碼沒有任何平台有關的代碼。
如果開發者打算撰寫特定平台的代碼,對於 iOS 和 Android 有不同的交互向導,或者干脆點說,開發者想要獲得特定平台 API 的獨特優勢,也是很容易就可以辦到的。React Native 允許開發者為每個組件指定特定平台的版本,可以隨意集成到其它 React Native 的應用當中。
使用 React Native
使用單一的 JavaScript 庫就想寫出真正的原生的 iOS 和 Android 的應用來,無異於異想天開。那么 React Native 又是如何做到的了呢?
上手入門
要開始開發 React Native 應用程序,需要為 iOS 和 Android 的開發安裝一些常見的依賴,即使是 React Native。在 React Native 官方站點上有非常好的指南。設置 React Native 其實蠻簡單,如果開發者已經安裝了最新版的 Node.js 的話,只需要執行命令 npm install -g react-native-cli
就可以安裝 React Native了。
一旦這些相關的依賴安裝完成后,運行 react-native init ProjectName
,此命令會自動生成開發者開始一個項目所需要的所有樣板。
這里有一點需要特別注意:開發 React Native 的話需要使用 OS X 操作系統。開發 iOS 應用,蘋果公司強制要求使用 Mac,這一點對於大多數開發人員來說這個限制已經不可避免。如果開發者是專門撰寫 Android 應用的話,React Native 還支持 Windows 和 Linux 平台,不過目前尚處於試驗階段,開發者不妨嘗試一下。
常見 React 組件
一旦准備好了開發環境,那么就可以開始撰寫一些真正的應用程序了。
正如在文章前面所提到的那樣,React Native 真的也只是 React,只是有少許不同罷了。React Native 的組件在瀏覽器中看的話和 React 的組件及其相似,但是基本的構建塊已經變了。諸如<div>
、<img>
、<p>
等標簽均被替代了,React Native 提供給開發者一些諸如<Text>
、<View>
等基本的組件,在下面的例子中,基本的組件使用的是<ScrollView>
、<TouchableHighlight
、以及<Text>
,所有這些都會映射到 iOS 和 Android 特定的視圖,使用它們創建帶有觸摸控制屬性的滾動視圖是非常直接的:
// iOS & Android var React = require('react-native'); var { ScrollView, TouchableHighlight, Text } = React; var TouchDemo = React.createClass({ render: function() { return ( <ScrollView> <TouchableHighlight onPress={() => console.log('pressed')}> <Text>Proper Touch Handling</Text> </TouchableHighlight> </ScrollView> ); }, });
如果你沒有處理過雜亂無章的 HTML-esque 語法以及 JSX 以前的 JavaScript 的話,這可能對你有一些困惑。React 強烈推薦開發者使用 JSX,那么基於 React Native 的話,其實沒有什么可以選擇的機會。你渲染的標記是和 JavaScript 所控制的行為共存的。就這一點來說常會引起新接觸者強烈的反感,但是我還是強烈建議不妨一試。
因為 React Native 的組件和常見的 React 的組件極為類似,所以切換到 React Native相對是很容易的。
樣式表
為了讓渲染更容易、更加的有效,同樣鼓勵維護樣式代碼,React Native 實現了一個嚴格的 CSS 的子集。這同時也意味着開發者無需去學習特定平台的方法來從而設計視圖,當然,開發者還是要花點時間去學習如何使用 React Native 的樣式表的。
最大的不同就在於開發者不需要去擔心特定的規則,因為樣式的繼承是被嚴重削弱過的,而且 React Native 使用的內聯樣式的語法。
這里是一個如何使用 React Native 來創建樣式表的實例:
var styles = StyleSheet.create({ container: { flex: 1, marginTop: 30 } });
然后,此種樣式是由內聯語法所應用的:
<View style={styles.container}> ... </View>
語法是相當的容易閱讀,但是如果開發者是來自於 Web 開發的背景的話,可能會發現有些不妥。(會有一個好的理由讓你繼續下去的,我保證!)即進一步去閱讀關於 CSS,就會發現問題,若是以 React 的方法來解決它們的話,我這里強烈推薦 Christopher Chedeau 演示文稿:JS 中的 CSS。
設置移動開發環境
React Native 的其中一個比較復雜的部分就是開發環境的設置。當基於 React Native 工作時,開發者針對移動開發需要所有的通常的工具,以及 JavaScript 編輯工具:一個文本編輯器,可能還需要 Chrome 的開發者調試工具。
對於 iOS 來說,這也就意味着需要開着 Xcode,以及 iOS 模擬器。對於 Android 來說,也需要開着的有 Android Studiom、將會用到的命令行工具。最終,開發者還需要運行着的 React Native 包,當然,開發者可以自行決定使用自己喜歡的文本編輯器來編輯 JavaScript 代碼。
這么做的后果就是你需要在自己的本地開好多窗口,如此之多的工具,桌面雜亂到着實讓人煩惱,但是,這樣的 React Native 不會隱藏任何標准的移動開發過程。
跳轉到原生代碼
React Native 工作在已有平台的上 API 所提供的 JavaScript 接口上。在實踐中,這意味着開發者可以撰寫原先自己熟悉的 React 代碼,React Native 的“橋”會自動完成相關的轉換,但是若轉換的功能不夠完善沒有徹底搞定的時候該如何處理?
不可避免的是,基於諸如 React Native 這樣的新框架來開發的話,總會有一些 API 是不被支持的。若發生了這樣的事情,你就需要撰寫“本地的模塊”來提供主機平台和所開發的 JavaScript 代碼之間的通信。下面是一個關於 Objective-C 模塊的 “Hello,World”的簡易實例:
// Objective-C #import "RCTBridgeModule.h" @interface MyCustomModule : NSObject <RCTBridgeModule> @end @implementation MyCustomModule RCT_EXPORT_MODULE(); // Available as NativeModules.MyCustomModule.processString RCT_EXPORT_METHOD(processString:(NSString *)input callback:(RCTResponseSenderBlock)callback) { callback(@[[input stringByReplacingOccurrencesOfString:@"Goodbye" withString:@"Hello"]]); } @end
然后,要使用開發者本地的 JavaScript 模塊的話,和需要其它的庫是一樣的:
// JavaScript var React = require('react-native'); var { NativeModules, Text } = React; var Message = React.createClass({ getInitialState() { return { text: 'Goodbye World.' }; }, componentDidMount() { NativeModules.MyCustomModule.processString(this.state.text, (text) => { this.setState({text}); }); }, render: function() { return ( <Text>{this.state.text}</Text> ); } });
開發者這樣做可能是因為所需要的 API 還沒有得到支持,又或者是集成 React Native 組件到現有的 Objective-C 或 Java 代碼中,又或者是需要寫一些高性能的功能來處理一些密集的圖形處理。非常值得慶幸的是,React Native 已經能夠提供非常靈活的撰寫和使用這些稱之為“原生模塊”,只要開發者需要,而且流程也是很順溜的。哪怕是開發者從來沒有 Objective-C 或 Java 的開發經驗,撰寫“橋”的代碼是在和本地的移動開發相當舒服的情況下的一種很爽的體驗。
部署應用程序
部署 React Native 的應用工作原理和傳統的移動應用部署方式及其的類似,這並不是說這會非常的容易,眾所周知,移動應用的發布流程是非常令人煩惱的。
要創建一個准備好部署的包,開發者需要使用打包好的 JavaScript 來代替在線重載的開發版本。在 iOS 中,這意味着需要在 AppDelegate.m
文件中更改一行代碼,然后運行 react-native bundle --minify
。對於 Android來說,開發者需要運行 ./gradlew assembleRelease
,在這之后,打包的流程就和原來的移動開發過程沒有任何的區別了,且最終的打包結果會提交到相關的應用程序商店中。
我的 flashcard 應用在 iOS 應用商店花了兩周的時間,在 Google Play 商店花了不到一天的時間。換句話說,批准的時間完全符合我的預期,即和“傳統的”移動應用沒有任何區別,對於 React Native 所寫的程序沒有作任何的處罰。
有趣的是,蘋果允許應用自行更新--因此繞過了令人頭疼的部署過程--如果只是更新 JavaScript的話。微軟最近釋出了CodePush SDK,其允許 React Native 開發者立即更新。這是一個非常好的特性,我希望在接下來的幾個月當中能夠看到更多的應用程序享用此特性所帶來的便捷。
結論和建議
如果你的背景是基於 web 開發的 JavaScript 工程師的話,我以為你會為 React Native 而高興的。React Native 可以將任何背景的 web 開發者轉變為潛在的移動開發者,而且對於現有的移動開發流程是得到了有效的改進。
當然,React Native 也不是完美無缺的,畢竟她還是一款較新的軟件項目,要經受哪些不夠成熟的程序庫的折磨:有一些特性還是缺失的。而且最佳實踐之類的還尚待挖掘。版本之間的變化太過於跳躍,雖然很少發生而且范圍也比較有限,但還是偶爾會發生。
然而,React Native 已經足夠的成熟,總而言之,好處是遠遠大於瑕疵的。基於 React Native,開發者可以使用單一的 JavaScript 代碼庫來創建 iOS 和 Android 的應用,而且還沒有質量和性能上的損耗。即使開發者沒有 JavaScript 的背景,也很難不被更加快速的開發周期和幾乎完全的代碼重用這樣的好處所吸引。而且因為 React Native 允許開發者在需要的時候切換到“常見”的開發狀態,你還不會受限於框架。總而言之,React Native 交付了一款高質量的、跨平台的移動開發工具,如果讀者你要加入到移動開發的項目中來,你需要慎重的考慮下使用 React Native。
如果你想更進一步的閱讀,我已經出版了一本專門、詳細的圖書:《React Native 學習》,電子版和紙版均有售賣,可以到奧姆萊 和亞馬遜上找到。你也可以在Twitter上關注我,我的賬號是:@brindellev,我非常期望聽到大家使用 React Native 的經歷以及疑問。
國內:
Facebook在3.26 F8大會上開源了React Native,本文是對React Native的技術背景、規划和風險的概述。
組里的同學於4.2完成了天貓iPad客戶端“猜你喜歡”業務的React Native改造(4月中發版)。本周開始陸續放出性能/體驗、穩定性、擴展性、開發效率等評估結果。
圖1 - 4.2已完成React Native改造的業務
背景
為什么需要 React Native
What we really want is the user experience of the native mobile platforms, combined with thedeveloper experience we have when building with React on the web.
摘自3.26 React Native的發布稿,加粗的關鍵字傳達了React Native的設計理念:既擁有Native的用戶體驗、又保留React的開發效率。這個理念似乎迎合了業界普片存在的痛點,開源不到1周github star破萬,目前是11000+。
圖1 - React Native github
React Native項目成員Tom Occhino發表的React Native: Bringing modern web techniques to mobile詳細描述了React Native的設計理念。Occhino認為盡管Native開發成本更高,但現階段Native仍然是必須的,因為Web的用戶體驗仍無法超越Native:
- Native的原生控件有更好的體驗;
- Native有更好的手勢識別;
- Native有更合適的線程模型,盡管Web Worker可以解決一部分問題,但如圖像解碼、文本渲染仍無法多線程渲染,這影響了Web的流暢性。
Occhino沒提到的還有Native能實現更豐富細膩的動畫效果,歸根結底是現階段Native具有更好的人機交互體驗。筆者認為這些例子是有說服力的,也是React Native出現的直接原因。
圖2 - Occhino在F8分享了React Native(Keynote)
“Learn once, write anywhere”
“Learn once, write anywhere”同樣出自Occhino的文章。因為不同Native平台上的用戶體驗是不同的,React Native不強求一份原生代碼支持多個平台,所以不提“Write once, run anywhere”(Java),提出了“Learn once, write anywhere”。
圖3 - “Learn once, write anywhere”
這張圖是筆者根據理解畫的一張示意圖,自下而上依次是:
- React:不同平台上編寫基於React的代碼,“Learn once, write anywhere”。
- Virtual DOM:相對Browser環境下的DOM(文檔對象模型)而言,Virtual DOM是DOM在內存中的一種輕量級表達方式(原話是lightweight representation of the document),可以通過不同的渲染引擎生成不同平台下的UI,JS和Native之間通過Bridge通信。
- Web/iOS/Android:已實現了Web和iOS平台,Android平台預計將於2015年10月實現。
前文多處提到的React是Facebook 2013年開源的Web開發框架,筆者在翻閱其2013年發布稿時,發現這么一段:
圖4 - 摘自React發布稿(2013)
- 加亮文字顯示2013年已經在開發React Native的原型,現在也算是厚積薄發了。
- 最近另一個比較火的項目是Flipboard React Canvas(詳見 @rank),渲染層使用了Web Canvas來提升交互流暢性,這和上圖第一個嘗試類似。
React本身也是個龐大的話題不再展開,詳見React wiki。
筆者認為“Write once, run anywhere”對提升效率仍然是必要的,並且和“Learn once, write anywhere”也沒有沖突,我們內部正在改造已有的組件庫和HybridAPI,讓其適配(補齊)React Native的組件,從而寫一份代碼可以運行在iOS和Web上,待成熟后開源出來。
規划
下圖展示了業務和技術為React Native所做的改造:
圖5 - 業務和技術改造
自下而上:
- React Node:React支持服務端渲染,通常用於首屏服務端渲染;典型場景是多頁列表,首屏服務端渲染翻頁客戶端渲染,避免首次請求頁面時發起2次http請求。
- React Native基礎環境:
- Framework集成:盡管React Native放出了Integration with Existing App文檔,集成到現有復雜App中仍然會遇到很多細節問題,比如集成到天貓iPad客戶端就花了組里iOS同學2天的時間。
- Networking改造:主要是重新建立session,而session通常存放於http header cookie中,React Native提供的網絡IO fetch和XMLHttpRequest不支持改寫cookie。所以要不在保證安全的條件下實現fetch的擴展,要么由native負責網絡IO(已有session機制)再通過HybridAPI由JS調用,暫時選擇了后者。
- 緩存/打包方案:只要有資源從服務器端加載就避免不了這個話題,React Native也是如此,緩存用於解決資源二次訪問時的加載性能,打包解決的是資源首次訪問時的加載性能。
- MUI是一套組件庫,目前會采用向React Native組件補齊的思路進行改造。
- HybridAPI是阿里一組Hybrid API,此前也在多個公開場合分享過不再累述,React Native建立了自己的通信機制(@bang),看起來更高效(未驗證),改造成本不大。
- 最快的一個業務將於4月中上線,通過最初幾個業務改造推動整體系統的改造,如果效果如預期則會啟動更大規模的業務改造。
更多詳細規划和進展,以及性能、穩定性、擴展性的數據隨后放出。
風險
- 盡管Facebook有3款App(Groups、Ads Manager、F8)使用了React Native,隨着React Native大規模應用,Appstore的政策是否有變不得而知,我們只能往前走一步。
- React Native Android 預計2015年10月才發布,這對希望三端(Web/iOS/Android)架構一致的用戶而言也算個風險。
- iOS6 JavascriptCore為私有API,如在iOS6上使用有拒審風險。
- ListView 性能問題需要持續關注(react-native github issue)
React Native相對於Webview和Native的優勢和劣勢 @berg 也給出了較詳細的描述,可以相互參照。