- 原文地址:Things about React Native I found the hard (but rewarding) way
- 原文做者:Christos Sotiriou
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:jerryOnlyZRJ
- 校對者:xilihuasi,WangLeto
React Native 已經存在了一段時間了。當它支持 Android 的版本(iOS 以後大約一年)發佈後,我便使用它進行專業級開發了,我決定投入時間在 RN 上進行跨平臺開發。當我發現 React Native 時,我從事 iOS 開發工做已經六年了,並且不只僅是 Mac OS X 的開發人員。css
我已經在 App Store 和 Play Store 爲個人客戶開發了四個中等大小(一萬到兩萬左右行代碼,不包括依賴項)的項目。我還在一個使用 React Native 編寫的代碼超過五萬行(除了本機代碼)的大型項目中參與監督和貢獻,如今已經部署上線並運行順利。我已經積累了足夠的經驗來找出 React(和 React Native)閃耀的地方,和解決它短處的方案。前端
注意:我知道大家中的一些人在閱讀這篇文章時會提到 Flutter。但因爲它的成熟度遠不及它的競爭對手,因此我尚未深刻了解它。react
在撰寫本文時,React Native 的當前穩定版本爲 0.57,即將到達 0.58RC。android
這是個人想法:ios
React Native 最廣爲人知的特性是跨平臺,但這並非它引發我注意的緣由。React Native 最重要的特性是它使用 React,從而支持通用的聲明式佈局。跨平臺支持排在重要性的第二位。做爲一名 iOS 開發人員,我一直在嘗試那些不那麼直觀的用戶界面設計方式的 Auto Layout 系統(指 IOS 開發使用的自動佈局系統)。git
若是你的系統具備高度動態性,而且屏幕上的元素相互依賴(好比一些側邊欄和動畫),那麼使用 Apple 的 Autolayout 是管理屏幕上內容的最佳方式。可是,對於大多數 Android 和 iOS 應用程序,狀況並不是如此。大多數 Android 和 iOS 應用程序都會使用咱們常常看到的標準元素:文本、按鈕、列表、通用視圖和圖像並以最相似於 Web 的方式佈局。github
在 AutoLayout 出現的同時,flex 佈局也被髮明出來,併成爲了屏幕上元素佈局約定俗成的標準。除了標準的 Web 使用以外,還有一些佈局系統旨在利用 FlexBox 原則進行原生開發:數據庫
這只是比較出名的一些,還有不少 UI 庫。他們有一個共同點,就是他們都使用了聲明式 UI。編程
建立移動開發的聲明式 UI 是爲了解決傳統佈局系統所具備的問題。你聲明瞭你的意圖,系統會根據它們生成結果。後端
聲明式 UI 解決的一些移動開發挑戰以下:
組件化。iOS 使用 ViewControllers 中嵌套 ViewControllers 或者 views 中嵌套 views,而 Android 使用 Fragments。二者都具備 XML 接口聲明,而且都容許運行時實例化和編輯視圖。當涉及將它們分解爲較小的模塊或複用它們時,你就是在進行一個小型的重構。在聲明式 UI 中,你隨時均可以對模塊或者組件進行復用。
開發人員的生產力。聲明式 UI 負責爲你調整組件大小。看看這段代碼(React Native 示例):
class TestTextLabel extends React.Component {
render() {
return (
<view>
<text>This is a small text</text>
<text>{this}</text>
</view>
);
}
}
複製代碼
上面的代碼渲染了一個只包含兩個文本組件的 Component。注意 this.props.sampleText
,若是此變量太長(例如 10000 個字符左右的長度)會發生什麼?結果將會是組件將調整大小以適合整個文本。若是文本達到了可用空間的極限(好比說屏幕大小),那麼視圖將被剪切,用戶將沒法看到整個文本,你須要一個滾動視圖。
class TestTextLabel extends React.Component {
render() {
return (
<ScrollView style={{flex : 1}}>
<Text>This is a small text</Text>
<Text>{this}</Text>
</ScrollView>
);
}
}
複製代碼
惟一改變的是添加 <ScrollView>
元素,這將須要在 iOS 上進行更多工做。
協做 —— 配合 Git 的友好體驗。我看到的每一個聲明式 UI 都能更好。
在 iOS 和 Android 上,若是你有大的單片 UI,那你就作錯了。可是,大型 XML 文件在大多數狀況下是不可避免的(請注意 iOS:XIB 其實是 XML 文件)。它們的變化對代碼審查者(或你)沒有任何意義,若是你不一樣意以前的版本(你的更改或其餘開發人員)完整保留,則發起 Pull Request 幾乎是不可能的。
使用 React 和其餘聲明性 UI 庫,這些問題在很大程度上被最小化,由於佈局是實際代碼,你能夠更新、刪除、合併、對比差別以及執行全部你你平時對其餘軟件執行的操做。
你可能須要成爲移動開發人員才能掌握性能的概念,並管理有效的內存和數據處理器使用。
Web 開發人員能夠在不瞭解原生的狀況下使用 React Native 開發,這種說法僅適用於小型項目。一旦應用程序開始增加而且 Redux 的 store 的計算開始對應用程序的性能形成影響時,你將須要瞭解原生端如何工做的,才能理解爲何會這樣。你還須要意識到 React Native 中的 Redux 的 Store 致使的從新渲染與 DOM 中發生的從新渲染並不徹底相同,尤爲是應用中的原生組件。
同時,在 React Native 上從新渲染應用程序的組件會變得代價昂貴。因爲 React Native 本質是使用 bridge,所以你在 render()
函數內部提供的任何指令都將從 JavascriptCore 傳遞到 Java 或者 Objective C++。原生端將獲取 render()
給出的 JSX 標籤,並將它們轉換爲其原生對應部分,例如視圖、標籤和圖像。若是轉換每秒進行數百次,那就須要不可忽略的 cpu 時間。
在性能方面,彷佛 React Native 是更好的跨平臺解決方案之一。可是,在某些關鍵領域 React Native 仍然存在性能問題。
一個這樣的例子是大型數據集(和列表)。在存在大型列表和網格視圖的狀況下,Android 和 iOS 都提供了一個出色且極其靈活的解決方案 —— 回收視圖。想象一下,當使用大型列表視圖(不管是 iOS 或是 Android)時,只渲染在任何給定時間顯示的單元格。其餘單元格被標記爲可重複使用,以便在即將顯示新單元格時能夠重複使用它們。更改數據集時,操做系統只需更新顯示的單元格。
React Native 爲大型數據集提供 VirtualizedList 及其派生的(FlatList 和 SectionList)。然而,即便這樣也有不少不足之處。存在性能開銷,尤爲是在 SectionList 中渲染複雜組件並嘗試更新一百多個對象的大型數據集時。更新機制會使低端或中端移動設備緩慢運行。
爲了解決這個問題,我已經從 Redux 切換到 MobX,它爲個人組件提供了更可預測的更新。此外,在大型列表的狀況下,MobX 能夠更新特定單元格而無需從新呈現整個列表。一般這也能夠經過 Redux 實現,可是你須要重寫 componentShouldUpdate()
方法並編寫更多樣板文件以免沒必要要的從新渲染。在將其他變量複製到新狀態時,你的 reducer 仍會執行一些沒必要要的工做。
寫在最後:總之要當心。若是你正在使用 React Native,要想讓你的應用程有最好的表現效果,就要對 React 和原生的最佳實踐都很熟悉。
能夠經過將調試信息發送到 Chrome 對 React Native 進行調試。這意味着在設備中運行實際代碼的過程與你調試代碼的過程不一樣。
Android 和 iOS 上的 React Native 使用 JavascriptCore 執行 Javascript。可是,調試工具在 V8(Chrome)上運行。爲了使系統有更廣泛適用性,在撰寫本文時,React Native 在 iOS 上使用 Apple 的 Javascript Core,而在 Android 上,他們使用的是已經發布三年的 JS Core 來構建腳本(由於 Android 沒有提供任何像 iOS 這樣現成的 JS 運行時,Facebook 也必須本身構建)。這就致使缺少了不少 JS 新特性,好比 Proxy 對象只在 Android 上和 iOS 64 位上支持。所以,若是你想使用 MobX 5+,那你必須使用升級的 Javascript 運行時(繼續閱讀以瞭解如何作到這一點)。
運行時差別一般會致使錯誤只能在生產模式中重現。更糟糕的是,甚至有些錯誤會變得難以調試。
例如,當涉及 React Native 時,移動端數據庫的最佳解決方案是 Realm。可是,當進入調試模式時,會發生這種狀況:github.com/realm/realm…。雖然 Realm 的研發人員已經解釋了爲何會這樣,但最重要的是,若是咱們想要一個更穩定的調試解決方案,必須改進 React Native 的調試架構。好消息是我一直在使用 Haul 做爲個人捆綁包,它容許我直接從個人 iOS 設備進行調試,而無需經過 Chrome Dev Tools(不幸的是,你須要 Mac、iOS 模擬器和 Safari)。
請注意,Facebook 上的人已經發現了這個問題,他們正在從新設計 React Native 的核心,以便原生和 React Native 部分能夠共享相同的內存。完成此操做後,可能能夠直接在設備的 JavaScript 運行時上進行調試。(參照文章:React Native Fabric (UI-Layer Re-architecture))
不只如此,React Native 社區如今提供了 JS android 構建腳本,它可以構建針對較新版本的 JavascriptCore 的腳本並將其嵌入到 React Native 應用程序中。這使 Android 上的 React Native 的 Javascript 功能能與 iOS 相提並論,也爲在 Android 上運行的 React Native 增長了 64 位支持奠基了基礎。
你是否開發過帶有身份驗證的移動應用程序?若是用戶收到一條推送通知,而且只有先在登陸界面登陸後才能看到推送通知內容界面,會怎麼樣?或者,若是你當前已經在一個應用程序中的深層次界面並但願跳轉到另外一個應用程序中的徹底不一樣的區域做爲對用戶操做的響應,又該怎麼辦?
使用原生的方法能夠解決這一問題,但須要花費一些努力。而使用 React Navigation,它們甚至都不是問題。深層的連接和導航跳轉能讓用戶感受天然而流暢。雖然還有其餘導航庫,但 React Navigation 被認爲是事實上的標準。你應該試一試,這是 React Native 比 iOS 和Android 更好的地方。
與任何其餘技術同樣,你須要在投入使用前瞭解它能作什麼不能作什麼。如下是 RN 在哪些類別的應用上具備優點:
這裏還列出了一些對於 RN 來講表現不算太好的一些類別的應用:
確實,對於 React 沒法作到的事情,你能夠在原生中編寫所需的全部內容,而後從 React Native 調用相應的代碼。但這意味着你須要爲每一個平臺(iOS、Android)編寫一次代碼,而後爲 Javascript 接口編寫額外的代碼。
React Native 的內部組件目前正在經歷一個主要的重構,以讓 RN 能夠並行執行更多同步操做,以便它能夠與原生共享公共代碼:facebook.github.io/react-nativ…。所以,在此以前,你應該在決定是否使用它以前進行一些研究。
React Native 是一個通過深思熟慮且發展良好的平臺。它爲你的應用打開了 NodeJS 的世界,讓你在最好的佈局系統中進行編程。它還爲你提供了與原生方面的良好 bridge,以便你能夠充分利用這兩個世界。
然而,它也屬於另外一個奇怪的類別,有時候你只須要一個團隊來開發你的應用程序,但有時候也須要三個!在某些時候,你須要一些 iOS 和 Android 開發人員來構建 React Native 默認狀況下沒有的組件。一旦你的團隊開始成長,你將不得不決定是否將你的應用程序設爲 100% 原生。所以,你爲下一個項目是否選擇 React Native,取決於你擁有多少原生代碼(Java、Kotlin、Swift、ObjC)。
個人我的建議:若是你意識到你須要三個團隊來開發一個應用程序的三個不一樣層面(一個 iOS 團隊、一個 Android 團隊和一個 React 團隊),那麼你應該能夠一直使用原生 iOS 和 Android 並拋棄 React Native 。僅維護兩個代碼庫而不是開發三個代碼庫,你將節省時間和金錢。
可是,若是你擁有一個由熟練的開發人員組成的小團隊並但願構建內容應用程序或相似的軟件,那麼 React Native 是一個很好的選擇。
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。