寫給移動開發者的 React Native 指南

版權聲明:本文原創發佈於公衆號 wingjay,轉載請務必註明出處! https://www.jianshu.com/p/b88944250b25

281665-226ddf6afab5f41b.png

前言

React Native 誕生於 2015 年,名副其實的富二代,主要使命是爲父出征,與 Apple 和 Google 抗衡,爲開發者帶去一套跨平臺、動態更新的 Javascript 框架,口號是:Learn once, write anywhere:Build mobile apps with React。在試圖推翻 Android 和 iOS 壓制的同時,還提攜了一把自家兄弟:React。

從誕生之日 React Native 就充滿了期待和爭議。期待是無數開發者希望不用忍受頻繁發版的噩夢,也不用同時爲兩個平臺開發業務邏輯幾無差別的兩個 App;爭議是 React Native 真的能以一己之力救大衆於水火嗎?React Native 在跨平臺時還能保持良好的用戶體驗嗎?

當然我們知道,這種問題向來都是仁者見仁,智者見智。比起一味的疑惑、爭論,還不如來好好看看這貨究竟是個啥?甚至自己動手來玩一把。

本文主要針對兩類讀者:

  • 想要入門 RN 的人,在閱讀官方文檔前先對 RN 形成一個整體的印象
  • 對 RN 心存好奇,在猶豫是否要入坑的開發者,可以通過本文對 RN 更客觀全面的認識

目錄

  • React Native 好在哪
    • 跨平臺+動態更新
    • 代碼複用
    • RN vs Weex
    • RN vs Hybrid
    • RN 劣勢
  • React Native 運行機制
  • RN 開發環境搭建
  • 引入 React Native
    • Build from Scratch
    • 集成到已有項目
  • Javascript、React 及 ES6、JSX 語法
  • UI 層
  • 網絡請求層
  • Debugging 調試
    • In-App 報錯
    • Console.log
    • 大殺器:Chrome 逐行調試
  • 從 JS 調用 Native 方法或顯示自定義 Native View
    • Native Modules:JS 裏直接調用 Native(Java/Swift) 方法
    • Native UI Component:JS 裏直接調用自定義的 Native View
  • React Native 適合你嗎?
  • 爲什麼要寫這篇文章

一、React Native 好在哪

下面我們來看下 Hybrid 及 React Native 等開發模式包含了哪些常規移動開發所不具備的優勢。

1. 跨平臺+動態更新

傳統的客戶端開發模式是怎樣的呢?

Android 與 iOS Team 分別編寫客戶端代碼,打包,分發到 Play Store 和 Apple Store,通過更新 JSON 數據來更新頁面。

不過,當客戶端發生嚴重問題而服務器上無法 quick fix 時,就不得不重新發版。

對國外 Android 市場而言還好,因爲能通過 Play Store 快速更新;國內 Android 市場則由於分發渠道太雜,很難及時把新版本立即推送給所有用戶,當然這也是爲何熱修復技術在國內盛行而國外冷清的原因;而 Apple Store 則需要一定的審覈時間,而且最近又在抓 iOS 熱修復框架如 JsPatch、Rollout 等。

相比而言,Hybrid 和 RN/Weex 模式除了能下發 Json 數據來刷新界面內容,更能直接下發業務邏輯代碼,直接實現整體 App 的更新。而且,它們不用在乎 Android 和 iOS 兩個平臺,因爲一份 JS 代碼寫好後,把 JS Bundle 放在服務器上,所有的客戶端立即更新。

2. 代碼複用

一般而言,同一款產品的 Android 和 iOS 兩端除 UI 有些許不同外,多數業務邏輯幾乎完全一致,這便造成了人力的浪費。

而最近 Instagram 的官博 React Native at Instagram 一文中已經提到,利用 RN (React Native 縮寫,下同) 開發的 feature 可以實現 85% - 99% 的代碼複用率。這意味着我們可以用更少的人力成本來達到相同的效果。

3. RN vs Weex

實現上面的效果有兩種開發框架:混合開發框架 Cordova 和基於 Javascript 的 React-Native、Weex 框架。

下面我從自己的實踐經驗出發做些比較,也歡迎讀者提出自己看法。

最開始覺得 RN 的學習成本比較大,所以首先考慮了 Weex 框架,據說是阿里巴巴良心出品。不過在嘗試後不得不選擇了放棄,原因有這幾點:

  • Bug 較多。我們最先測試了最基本的 ListView,在 iOS 運行良好,而同樣的 Demo 代碼到了 Android 這邊的下拉刷新就出現了問題,這使得我們開始警惕;
  • 社區、文檔弱,GitHub Issue 基本是中文。當然我毫無歧視中文之意。我認爲,一套項目開源是真正意義是希望藉助開源社區的力量,一起來完善改進,因此要優先推崇英文,使項目國際化,得到全世界開發者的共同支持,這樣纔是可持續的模式。而 Weex 的 Issue 裏放眼望去基本 90% 都是中文,無論提問者還是項目維護者。這一點直接把國外優秀的開發者拒之門外,也很難讓我看到多麼長遠的未來。
    下面是摘取的 RN 裏的一則中文 issue:
281665-fdd239b44898ea4c.png
**Issue is for bug report, not for Q&A**
  • Contributor 差別。因爲上面一點,Weex 的 Contributor 只有 91 個人,而 React-Native 的 Contributor 有 1214 人。Contributor 是用來幹嘛的?除了支持新功能,還有就是修復 bug 啊。Weex/RN 都是希望一統 Android + iOS 的,這麼偉大的目標,這麼艱鉅的工程,不是幾個人可以輕輕鬆鬆搞定的。
  • 公司背景(來自YY)。大家都知道 RN 來自 Facebook,Weex 來自阿里巴巴。如果想一窺它們的未來,需要先想一下這種技術對他們各自的意義。大家都清楚,Facebook、Google、Apple 是當今當之無愧的巨頭,在移動互聯網這波浪潮裏,Google 掌握了 Android 法器,Apple 控制了 iOS 神器,Facebook 呢?並沒有這些系統級入口。當然 Windows 的經歷也讓 Facebook 並不那麼傾向去開發一個新的移動操作系統來競爭。那怎麼辦?React Native 應運而生,打出的口號就是: Learn once, write anywhere。什麼意思,沒錯,就是明確告訴你學一次就可以同時開發兩個平臺了。這一點可一直都是移動端開發人員和創業公司的理想。有人說了,Apple 這麼強勢,RN 要是太囂張,分分鐘把你禁掉。這時我們就要來看看 RN 的 Showcase 了,哪些 App 應用了 RN 呢?Facebook, Instagram, Airbnb, Walmart, QQ, 京東等,這回 Apple 要禁 RN 就要稍微掂量下這些大廠的意見吧。

當然,我是很希望國內也能推出優質的開源項目來和國外大廠抗衡的,不過真正優質的大型開源項目往往除了開發者的個人能力,和公司的戰略和制度關係也很大。

4. RN vs Hybrid

這裏的 Hybrid 開發主要針對 Cordova 框架,其實在放棄 Weex 之後我們還是沒考慮 RN,而是轉過去了解 Cordova,不過做了大致瞭解後也放棄了。主要硬傷有兩點:

  • 性能短板。大家知道 Hybrid 是基於 WebView 的,在 Android 上的性能缺陷非常明顯;而 RN 是利用 JSCore 轉化成 Native 運行的,性能相對而言好不少;
  • 用戶體驗。瞭解移動產品的人都知道用戶體驗的重要性,RN 的體驗和原生的幾乎沒有差別,而 Webview 的實現是網頁開發思路,體驗會相差很大。

性能和用戶體驗是移動 App 的命根子。

因此,綜合考慮下來,我們還是決定相信 Facebook 並採用 RN。

5. RN 劣勢

上面我提到了 RN 的一些優勢,不過作爲開發者更加需要明確其劣勢,我總結了下大概有以下幾點劣勢:

  • 學習成本。Weex 的寫法就是類似常規的 Html/JS,對於前端人員來說很容易上手,就算了非前端人員來說也花不了多久。而 RN 是在 React.js 上進行改進形成的一套語法,和常規前端差別較大,因此需要好幾天的學習適應。當然我覺得優秀的程序員的基本素質之一就是能快速學習、練習並熟練一種新語言的。我個人的話大概花了兩三天的時間已經能完成一套涵蓋網絡、JS與Native通信的頁面了,對於 React.js 語法也上手很快。
  • 安裝包 Size。對於 iOS 而言影響不算很大,對於 Android 來說,我嘗試後發現引入 RN 會給 apk 帶來 6MB 左右的增幅,不過利用 split apk 的技術就能縮小到到 1MB 左右的增幅。
  • 首次加載耗時。大家知道 RN 需要從服務器下載 JS bundle,然後在本地轉化成 Native code 運行的,所以在第一次打開 App 時需要花費一些時間進行下載和刷新。當然我們可以在發佈 client 時內置一個寫好的 js 文件在本地作緩存用。

二、React Native 運行機制

對於一個用 RN 搭建的移動 App,在啓動後會從服務器下載最新的 JS Bundle 文件,然後由本地 JavascriptCore 引擎對 JS 文件進行解析,並利用 Bridge 映射到對應的 Native 方法和 UI 控件。得到的效果是:

  1. 同樣的 RN 代碼,下發到 Android 和 iOS 不同平臺中,會自動調用對應 Native 的 UI 控件,保證了各平臺用戶體驗的連貫性;
  2. 開發者就算是移動端小白,只要有 Web 基礎,通過編寫一套 RN 端代碼就可以同時完成 Android 與 iOS App 的開發;
  3. 由於可以利用 JS bundle 同時下發數據和業務邏輯,這意味着你可以像 Web 開發一樣,實時迭代更新你的移動端 App,無需在瞭解各自平臺的熱修復技術;
  4. Native Modules,這是 RN 強大的一個擴展性,允許你通過簡單的代碼就能實現在 JS 裏直接調用你自己的 Native 方法;
  5. Native Components,如果你自己實現了一些複雜的 Native UI 組件,而這些組件尚未被 RN 支持,你可以利用 Native Components 快速把原生組件引入到 RN 中並可以直接在 JS 裏更新這些組件的狀態。

三、RN 開發環境搭建

首先 IDE 方面,RN 推薦了一些工具:

  • Nuclide 是 Facebook 內部用來開發 RN 的工具,Debug 功能強大。只不過這是一款 Atom 的插件,意味着你必須先安裝 Atom,再來安裝這款開發插件;
  • Deco 是專爲開發 RN 誕生的工具,可以快速搜索開源的第三方 RN 組件並直接插入到代碼中,用 MacOS 的同學可以嘗試下。我本人最開始也是試用這個,上手簡單、小巧簡潔。不足的是功能有點簡單,無論是 Debug 功能還是代碼檢查之類的都不具備;
  • Sublime 可以通過第三方包來達到不錯的開發效率,各方面還算可圈可點;
  • Visual Studio 這款也是蠻強大的 IDE,之前有用過的小夥伴可以試一下。

本人的話目前採用的是 Sublime,因爲個人常用 Sublime,而且第三方插件很豐富,輕量方便。下面簡單說下配置,感興趣的小夥伴可以看下。

  1. Babel 用來高亮 React JSX 語法,支持 ES6,而 React-Native 就是搭建在 React.js 基礎上的;
  2. React-Native-Snippets 可以快速生成 RN 的一些模版代碼;
  3. ESLint 超級強大的 Lint 工具,支持 ES6、JSX 語法檢查,而且還有 React 和 RN 的插件,比純粹的 JSXHint/JSLint 都強大;

當然,用 Atom 的小夥伴自然要首先考慮 Nuclide

四、引入 React Native

引入 RN 有兩種方法:從零構建;集成到已有項目。

1. Build from Scratch

先說第一種,從零開始構建,比較簡單,遵循官方文檔 Getting Started 基於你自己的操作系統和平臺一步步安裝相關的依賴,然後利用如下命令:

react-native init AwesomeProject

你就創建好一個 RN 工程項目了,結構如下:

281665-8691544719a34a57.png
RN 目錄結構

裏面有四個文件夾:

  • android / ios:各自存放了一個相關平臺的工程 project,可以直接下拉 JS Bundle 並運行,對於移動端小白而言可以不用管裏面的具體實現;
  • node_modules:裏面是自動生成的 node 依賴之類的文件,通過讀取 package.json 裏的配置來生成;
  • js:這個文件夾最爲重要,我們的開發都在這個文件夾裏,把寫好的 js 文件打包下發給 client 就會自動生效。
2. 集成到已有項目

有很多公司是希望在現有 App 的基礎上集成 RN 來開發一些特定的 Feature,這種情況就不能參考上面的方法了。在 RN 的官方文檔裏有一節 Integration with Existing Apps , 只需要按照一步步做即可。

以 Android 爲例,大概要做以下幾步:

  1. 添加 gradle 依賴:compile "com.facebook.react:react-native:+" // From node_modules.
  2. 創建空的 Activity ,指定 JS bundle 和入口 Component 名字即會自動在這個 Activity 裏去加載 JS bundle 文件;
  3. 在 Activity 裏監聽 onBackPressed 事件,用來與 JS 端協作處理返回鍵點擊事件。
  4. 啓動 server,運行 App 即可。

總之需要說明的是,即使是移動端小白,也可以遵循文檔裏的指示完成這一步。接下來的大部分時間只要關心 JS 端開發就行了。

五、Javascript、React 及 ES6、JSX 語法

我們知道 RN 採用了 React 和 ES6 的語法,所以我們必須先對這些語法有一定了解才能去讀 RN 的代碼。

關於 Javascript,我推薦 W3School 裏的 JS語法 和 MDN 裏的 JS手冊,大家只要對一些基礎語法做些瞭解就可以。

關於 React,我推薦 阮一峯 寫的 React 入門實例教程,基本上把文章讀一遍,再自己動手寫一遍,就能理會到 React 的大致用法了。

關於 ES6ES7JSX等,感興趣的可以看一下 RN 文檔中 Javascript Environment 裏提到的支持的方法,需要時再來查詢也可以。也可以看 Babel 出的 Learn ES2015 手冊

這裏有一個很不錯的 GitHub 項目,幫助你通過交互性的例子來快速上手語法知識:React Native Express

六、UI 層

簡單熟悉了 React 語法後,基本能正常閱讀 RN 的示例代碼了。

正式開發 App 的第一步當然就是寫 UI 界面了,由於 RN 已經封裝好了一套 JS 的 UI 組件,這些組件會自動在 Android/iOS 端調用對應的原生 UI 組件,因此我們只需要熟悉這些 UI 組件的用法及屬性、回調方法即可。

我們可以在文檔的 Components 看到不少組件,比如View, Text, Button, Image, Switch, 還有我們用的最多的 ScrollViewListView

在讀文檔時,我們可以先通過一邊寫代碼一邊讀文檔的方式進行,RN 非常貼心,直接在 Web 裏嵌入了模擬器,我們只要修改編輯框裏的代碼,立即就能在右邊的模擬器看到效果。這極大的降低了我們的學習成本。


281665-09965625af9b3a49.png
Text Component

另外,在學習一個組件時,我們要區分哪個屬性是某個平臺特有的。比如下面兩個 Text 的屬性:textBreakStrategy 只會在 Android 上生效,而 adjustsFontSizeToFit 只可以用在 iOS 上。

281665-c8f4990d8334f3ca.png
Platform Specific Properties

然後,如果你希望在 Android 和 iOS 裏顯示不同的內容怎麼辦呢?RN 裏有一節是Platform Specific Code,可以有如下幾種形式來進行區分:

if (Platform.OS === 'ios') {
  // stuff for ios
} else {
  // stuff for android
}

除此之外,UI 組件的用法學習就很類似常規的 Html 標籤了,只要知道其使用方式即可,甚至需要用的時候再來查文檔也行。

七、網絡請求層

學完上面的我們已經能夠寫出 UI 界面了,而且這套界面已經能夠在不同平臺上轉化成各自平臺的 Native UI 了。然後,我們就需要去網絡層請求真實數據了。

RN 裏提供了 Fetch API 來進行實現。舉個例子,你想要通過 GET 方法去請求數據並轉化成 JSON,可以通過如下代碼實現:

fetch('https://facebook.github.io/react-native/movies.json')
      .then((response) => response.json())
      .then((responseJson) => {
        return responseJson.movies;
      })
      .catch((error) => {
        console.error(error);
      });

熟悉 Reactive 編程的夥伴應該對這樣的語法不陌生,比如 Android 上的 RxJava; iOS 上的 RxSwift;Web 上的 RxJS。上面 function 的功能就是:請求網址 https://facebook.github.io/react-native/movies.json,把返回的 Response 轉化成 JSON object,取出 JSON object 裏的 movies 字段。同時,如果發生 error 會被 catch 住。

當然,上面是最基本的 GET 請求,Fetch API 還支持自定義 Headers,更換 Method,添加 Body 等。

fetch('https://mywebsite.com/endpoint/', {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    firstParam: 'yourValue',
    secondParam: 'yourOtherValue',
  })
})

上面構建了一個基本的 POST 請求,添加了自己的 Headers:AcceptContent-Type,添加了 Body。

因此看下來,RN 裏的網絡請求不僅具備了 Reactive 編程的簡潔,也能自定義常規的 Http 請求,寫法簡單。

除了 Fetch API 之外,RN 還內置了 XMLHttpRequest API(俗稱 AJAX),而且支持TCP 全雙工通信方式 WebSocket

八、Debugging 調試

調試是很多程序員非常關注的一個環節,因爲 RN 是用 JS 寫完後到 Native 解釋成 Native 方法來執行的,因此如果能快速調試 JS 代碼是非常重要的一環。

最開始 RN 的調試功能比較弱,不過現在的 Debugging 功能在我看來還是很不錯的。一般來講可以有以下幾個調試方式:

1. In-App 報錯

RN 裏默認集成了 In-App 的錯誤提示方式,即在 App 運行過程中會彈出全屏的報錯信息呈現給你,而你也可以通過閱讀具體的錯誤信息快速找到錯誤原因。通過點擊這個錯誤信息裏的某一行,會立即自動打開對應的代碼。


281665-6531d24c41982690.png
In-App Error
2. Console.log

在開發 Client 時,我們一般都會用 Log.log() 來打印一些運行時變量的值,然後實時查看打印出來的 log 來調試,在 RN 也一樣,你只要在 JS 裏寫一句 console.log('this is log data'),就會自動在 Client 的常規 log 裏能看到,比如 Android 的 adb logcat 裏就會自動打印出'this is log data'一行。

3. 大殺器:Chrome 逐行調試

這個殺器的最牛逼之處就是可以像 Client 一樣,逐行調試代碼!

我們來看下面一張圖。從左往右。先是文件目錄,我們選中了 index.js 文件夾,然後第二個 Tab,是 index.js 的內容。這裏關鍵的是我可以直接選中某一行代碼設斷點。當 Client 運行到這一行時,就會在第三個 Tab 裏打印出運行時環境及變量。我們可以看到 props 裏就有我們傳進去的變量值。

281665-d2fb4470d8ae27b2.png
Chrome Debug

有了以上幾種調試方式,我們幾乎可以和常規的 Native 開發一樣來調試 RN 代碼了,不得不說 RN Team 確實牛 x 啊!

九、從 JS 調用 Native 方法或顯示自定義 Native View

這又是另一個牛 x 之處啊。

很多人覺得 RN 限制太多,只能支持有限的 View 組件和有限的方法,難以發揮 Client 的最大性能。簡單點說,在 Client 可以繪製複雜的 View,可以調用高性能 C++ 等底層代碼,但 RN 卻做不到。

於是,RN 裏提出了 Native ModulesNative UI Component 兩種技術。

Native Modules:JS 裏直接調用 Native(Java/Swift) 方法

所謂 Native Modules,就是自己在 Client 寫好了某些方法,由於某些原因這些方法不太方便或者無法搬到 RN 裏面,那麼,我們可以在 Client 把這些方法暴露出來給 RN,然後在 JS 裏可以像 import 普通的 module 一樣把這些 Native Modules 引入進去,直接調用。

具體的實現方法可以參考文檔 iOS Native ModulesAndroid Native Modules

Native UI Component:JS 裏直接調用自定義的 Native View

很多時候我們在寫 Client 時,爲了實現 Designer 天馬行空的設計,常常需要自定義 View,即自己繪製某些系統並不提供的特定 UI。可想而知,這些 View 肯定不會出現在 RN 的 UI Component 裏。

那麼,我們就需要首先在 Native 層自己寫好一個自定義 View,然後利用Native UI Component 技術把這個 View 及其中某些 public 方法暴露給 RN,那麼 RN 就能直接 import 進來並顯示了。

具體的實現方法可以參考文檔 iOS Native UI ComponentAndroid Native UI Component

如果讀過文檔不是很理解的小夥伴可以留言,我再 post 一些 demo 代碼上來

十、React Native 適合你嗎?

這裏借鑑下前段時間舊金山的 React Native 會議上的一些優劣總結給讀者以參考。當然不一定對,僅供參考。

RN 的優點:

  • 跨平臺
  • 原生的用戶體驗
  • 開發者體驗好
  • 動態更新代碼邏輯
  • 社區強大
  • 有個好爹

RN 的缺點:

  • 不夠成熟
  • 不夠穩定
  • 生態系統在搭建中
  • 優質的 App 需要時間打磨
  • 偶爾需要寫 Native 代碼(也就是 JS + Swift + Java)

適合下面這些人/公司:

  • 你對 JS/React 有一定了解
  • Web 開發人員比 Mobile 開發人員多
  • 有意願投資精力給 RN
  • App 設計不是特別區分 Android 和 iOS
  • 希望熱更新

下面這些人要稍微考慮下:

  • 完全不瞭解 JS/React
  • 已經有現成的 Android/iOS team
  • App 設計嚴格遵守 Android、iOS各自設計規範
  • 不想要投入時間/金錢給 RN

十一、爲什麼要寫這篇文章

幾個月前我對 React Native 也非常不看好,當然現在也沒有說非常看好。或者說,寫這篇文章毫無爲 React Native 佈道之意。

接觸 React Native 主要是因爲業務需要,PM 希望能夠隨時改動某塊變化較大的模塊,常規的開發提交流程往往需要較長的時間,而熱修復技術本身並未得到 Google 和 Apple 的官方認可,也就是隨時可能因破壞生態安全之名被取締。

因此才考慮去了解 Hybrid 開發和 JS Native 開發模式,在瞭解過程中,又由於性能差、用戶體驗不好而放棄 Hybrid,由於社區不完善、Bug 較多等原因放棄 Weex,最終才選擇了 React Native,開始學習 React、JSX等語法。

目前使用下來對 React Native 的一些個人感受:

  1. 學習門檻並沒有開始想象那麼高。大概只花了兩三天時間就熟悉了 Javascript、React 框架、JSX語法,然後就開始着手業務開發。
  2. 對 Android App 的影響。React Native 會給 Android 端帶來 6MB 左右的 size 增幅,不過在採用了 split apk 後就只有 1MB 左右增幅。
  3. Debug 功能比較完善,至少不用擔心發生問題後不知從哪下手。
  4. 性能還行。最初擔心的是 React Native 性能不好,但自己上手後,並沒有明顯感覺到很明顯的 React Native 對 App 性能的負面影響,無論是 iOS 還是 Android,當然,這一點還在繼續考察中。
  5. 動態部署真的很不錯。以前每次寫好代碼都要花不少時間來編譯運行,而現在只要寫一份代碼,就可以同時在 Android 和 iOS 實時更新了,這無疑節省了生命。
  6. 有待完善。當然,React Native 中確實還存在着不少問題,生態系統也還不夠完善。不過我相信,這只是時間問題。

關於React Native一直以來都有很多爭議。

不過我想說的是,React Native 所代表的跨平臺、動態更新技術已經引起了全世界開發者關注,而且這種技術勢必會是未來的需求和潮流。React Native 不一定會成功,但至少目前 React Native 已經是這一領域的領跑者。

而寫這篇文章的目的,就是希望告訴更多開發者,React Native 並不完美,但值得一試。

謝謝。

wingjay

281665-fee3f3c3da3e5d15.png
發佈了45 篇原創文章 · 獲贊 12 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章