React Native學習 認識Recat Native

weixin_34050427發表於2017-07-03

React Native學習<一> 認識Recat Native

部落格原文:http://www.jianshu.com/p/b88944250b25

2761423-4645cd15aeabdf04.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 好在哪

1.跨平臺+動態更新
2.程式碼複用
3.RN vs Weex
4.RN vs Hybrid
5.RN 劣勢

  • React Native 執行機制

  • RN 開發環境搭建

  • 引入 React Native

1.Build from Scratch
2.整合到已有專案

  • Javascript、React 及 ES6、JSX 語法

  • UI 層

  • 網路請求層

  • Debugging 除錯

1.In-App 報錯
2.Console.log
3.大殺器:Chrome 逐行除錯

  • 從 JS 呼叫 Native 方法或顯示自定義 Native View

1.Native Modules:JS 裡直接呼叫 Native(Java/Swift) 方法
2.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:

2761423-a4b436f26fd83958.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 工程專案了,結構如下:

2761423-9709c64d5101891f.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 的大致用法了。

關於 ES6、ES7、JSX等,感興趣的可以看一下 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, 還有我們用的最多的 ScrollView 和 ListView。

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

2761423-f99ed3800142254a.png
Text Component

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

2761423-9d919c8ee18a95f4.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:Accept和Content-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 執行過程中會彈出全屏的報錯資訊呈現給你,而你也可以通過閱讀具體的錯誤資訊快速找到錯誤原因。通過點選這個錯誤資訊裡的某一行,會立即自動開啟對應的程式碼。

2761423-95b9fbb68aa58133.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 裡就有我們傳進去的變數值。

2761423-c53a0752e242dad7.png
Chrome Debug

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

九、從 JS 呼叫 Native 方法或顯示自定義 Native View

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

很多人覺得 RN 限制太多,只能支援有限的 View 元件和有限的方法,難以發揮 Client 的最大效能。簡單點說,在 Client 可以繪製複雜的 View,可以呼叫高效能 C++ 等底層程式碼,但 RN 卻做不到。

於是,RN 裡提出了 Native Modules 和 Native UI Component 兩種技術。

Native Modules:JS 裡直接呼叫 Native(Java/Swift) 方法

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

具體的實現方法可以參考文件 iOS Native Modules 和 Android 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 Component 和 Android 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 並不完美,但值得一試。

謝謝。

相關文章