近期和一些朋友聊到了 React-Native 的官方重構狀態,而剛好近期發布的 0.59.x 系列版本中,上層設計出現了比較大的調整,結合體驗之後的狀態,就想聊聊 React-Native 的現狀、新版本的升級體驗、還有新支持的 React Hook 等特性。

本篇並不是源碼解析和教程,更多是討論和記錄描述。筆者一直致力於 AndroidReact-NativeFlutter 等大前端開發,有時也會寫寫 ReactVue,本篇文章也是希望能夠和大家交流,可以的話歡迎提出問題或者建議,最後同樣希望文章能對你有所啟發。

皮一下,React-Native 項目發布4年多了,還沒有 1.0 版本么(?_?)

一、現狀

相信大家對於 React-Native 「要涼」 的第一印象,應該是來自於 Aribnb 的 「為什麼 Airbnb 放棄了 React Native」 ,如文中描述的 React-Native 確實會遇到一些性能瓶頸,但這取決於和誰對比,個人認為 代碼是服務於業務的,拋開場景比性能的做法其實並不嚴謹。 關鍵還是在於你如何使用,並且官方與社區是否還活躍和優化。

先說我對跨平台的理解: 一套邏輯可以在多個平台運行,更多是避免各平台業務邏輯不統一,而對工作量的減輕是不明顯!不明顯!不明顯的! 同時一個企業項目大了之後,一般也不會局限於一個框架之內。

事實上 Facebook 也並沒有放棄 React-Native ,在經歷 《Facebook 正在重構 React Native,將重寫大量底層》 的官宣之後,「四捨五入」將近一年後的今天,底層重構雖然還沒有正式發布,但是近期的新版本 0.59.x 也給出了不錯的答卷。

新版本中主要有以下幾點:

  • 1、減輕了 React-Native 自身框架,將 webView 、viewPager、netinfo、async-storage 等內置包拆分,通過社區獨立維護,並逐步模糊 ReactReact-Native 的界限。
  • 2、更新 JavaScriptCore 、upgrade 和 CLI 工具。
  • 3、支持 React Hooks
  • 4、修復了 FlatList 等列表控制項中的諸多問題。

未來版本的重構主要目標有:

  • 1、減輕 JSBridge 的依賴。
  • 2、通過 Fabric UI架構,將 Shadow 層、 UIManagerNativeModule 從 Java 移到 C++ 中,從而支持 雙向的同步和非同步渲染與調用

可以看出 0.59 版本中的重構和拆分,都是在為了下一步的重構做準備,更多具體的下一代重構內容分析,可以在京東的 《庖丁解牛!深入剖析 React Native 下一代架構重構》 中查閱,這裡就不多贅述了。

同樣在攜程的項目中: 《攜程開源RN開發框架CRN》 文章也表示在第一時間更新到了 0.59.x 版本,現在還會覺得 React-Native 「要涼」 了嘛?

題外話

如今的編程界里存在各種「黨爭」,比如前端中 VueReactAngular ,跨平台的 CordovaWeexReact-NativeFlutter 等,而我在考慮選擇框架時,一般會從以下幾點優先順序先後排序: - 1、框架的活躍度。 - 2、你的業務需求複雜度。 - 3、團隊配置和團隊成員技術風格。 - 4、個人對框架的舒適度。

二、React-Native 0.59.x

在選擇升級版本之前,我們需要了解 React-Native 中版本是有 0.A.B 的大 A 小 B 版本號設定,而在 React-Native 使用過程中我的一個感受就是:

在做 React-Native 的版本選擇或升級時,最好不要選用 0.A.0 版本,比如 0.59.0;我一般會選擇大版本之後的小版本迭代,如 0.59.4 版本去升級更新,這樣的版本相對更穩定,可以少躺一些問題。

然後 React-Native 的版本升級一直是個頭大的問題,我一般會先在自己的開源項目中躺坑,本次在我的開源項目 GSYGithubAPP 中,是從 0.57.8 直接升級到 0.59.4 版本,結果如預期一般並不順利,而一般 React-Native 的版本升級,帶來的問題主要有三類:

1、官方 API 的調整

一般這類問題都比較好解決,官方的更新文檔也有詳細說明,這次升級中主要是將原本 React-Native 自帶的 webViewnetinfoasync-storage 等插件替換到 react-native-community 下提供,並替換一些棄用 API 。

2、第三方庫不兼容 :

這也是 React-Native 中比較頭疼的問題,因為第三方包的維護參差不齊,基本上如果作者不維護或維護不及時,那就只能自己苦笑動手了,就像本次 GSYGithubAPP 在升級過程中就遇到有:

  • 升級後遇到 realm 庫在 Xcode 上的編譯錯誤錯誤,詳細可見 GSYGithubAPP#66 ,雖然問題不大,可自行通過簡單本地改庫解決,這也是目前項目的升級還未合併到 master 的原因之一。
  • react-native-router-fluxreact-navigation 的升級版本需要相互對應,同時需要增加 react-native-gesture-handler 依賴,並且在 index.js 入口處提前導入來解決一些問題。
  • 各類第三方插件的 Android targetSdksupportSdk 等版本和依賴方式問題。

3、node_module 「黑洞」

這類問題屬於看人品,比如 GSYGithubAPP 項目是從 0.57 升級到 0.59 的,而 BackAndroid0.58 已經被完全棄用,其中項目剛好存在一個 modal 插件使用了 BackAndroid ,雖然作者也更新了插件做兼容,但是····

在更新了插件之後,重新運行後卻依舊報錯?WTF,而明插件源碼已經沒有 BackAndroid 的痕迹,那錯誤哪裡來的?

通過 Chrome 的 Debug 查看當前 bundle 源碼,最後發現居然真的有BackAndroid 的存在,當時就判斷妥妥的緩存問題。

在執行了無數遍的卸載 APP,關閉CLI,刪除 node_module 重裝後,最終還是通過刪除緩存 rm -rf ~/.rncacherm -rf $TMPDIR/* ,再重新安裝node_module 運行才解決問題。

  • 總結

其實這也是為什麼我說 React-Native 等跨平台開發,其實並沒有降低工作量的原因。跨平台解決的是邏輯統一維護,而開發中過程中,很多時候會遇到兼容開發的問題,並且平台之間的適配同樣消耗時間。

我相信每個 React-Native 開發人員都十分討厭滿屏幕的紅色,所以不知哪一版開始, React-Native 把錯誤增加了紅黑相間的效果(?_?)。

三、React Hooks

React Hooks 其實也是我升級到 0.59 的目的之一,因為它確實是一個很有意思的設定。

事實上我並非嚴格意義上的前端人員,大部分時候我對 CSSES 的了解也不深入,但在 JS 的使用過程中有幾個讓我印象深刻的:

  • ReduxRedux 的狀態管理設計,且由它衍生出的一系列後續和第三方插件,我個人覺得這是 React 當初能快速的風靡的助力之一。

  • HOCES7 Decorators :事實上這應該也包含在 Redux 里, 但是 HOC + Decorators 快速實現類似切面編程的效果,這無疑讓 Java 開發的我感到親切。

最後就是本文主角 React Hooks 了,React Hooks 也算是比較新的概念,關於 React Hooks 的我推薦這篇文章: 《【React深入】從Mixin到HOC再到Hook》 ,文中很好的描述了 React 開發風格的發展和對比。

而對於 React Hooks 能在這麼早就引入到 React-Native 中,給我的感覺就是 Facebook 團隊在致力於模糊 React 開發者在 Web 和 App 之間的邊界,同時這也是為了豐富 React 開發者的生態吧。

而對於 React Hooks ,在我的理解上而言,函數式編程可能更貼近「未來」的形態(雖然我並不特別確定),而 React Hooks 確實有著明顯的優勢:

  • 可以更好的減少我們的代碼量。
  • 同時降低代碼在生命周期執行過程中造成的阻塞。
  • 自定義 Hooks 可以在一定程度上解耦,增加復用,減少嵌套。
  • 函數式編程的風格讓函數功能獨立,代碼簡潔更好閱讀。

回歸到具體使用, React Hooks 其中最常用默認介面有 :

  • useState 可以讓你在函數中快速添加狀態
  • useEffect 讓你快速添加生命周期處理
  • useImperativeHandle 快速對外暴露介面

這些內置 Hook 可以在一定程度上節省你的代碼量,並且提供清晰的狀態管理邏輯,同時利用官方的 useReducer ,如下方代碼,更可以快速寫出一個偽 Redux

import React, {Component, useReducer, useRef, useImperativeHandle, forwardRef} from react;
import {Text, View, TouchableOpacity,} from react-native;

const initialState = {count: 0};

function reducer(state, action) {
switch (action.type) {
case reset:
return initialState;
case increment:
return {count: state.count + 1};
case decrement:
return {count: state.count - 1};
default:
return state;
}
}

export function DemoCounter({initialCount}) {
const [state, dispatch] = useReducer(reducer, {count: initialCount});
return (
<View>
<Text>Count: {state.count}</Text>
<TouchableOpacity onPress={() => dispatch({type: reset})}>
<Text>Reset</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => dispatch({type: increment})}>
<Text>+</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => dispatch({type: decrement})}>
<Text>-</Text>
</TouchableOpacity>
</View>
)
}

對於 React Hooks ,結合查閱源碼和文章簡單理解,就在渲染之前利用系列的鉤子,而 Hooks 內部利用了數組 ,實現狀態數據的順序更新。

所以官方也表示了,Hooks 不能在循環或者條件判斷中使用,這屬於一種約定,因為 Hooks 內的數組每次都是順序的調用的,如果在條件判斷中打亂了順序,將導致游標無法匹配到正確的數據,所以約定了不要在 if 或者 for 中使用 useState 等行為。

關於 React Hooks 相關更詳細的乾貨,推薦查閱:

  • 《react hook的初步研究》
  • 《React hook 不是魔法,是數組》

最後說說編碼風格:

無論是 HOCReact HooksRedux 等,其實我覺得都不存在所謂最優解,具體選擇使用還是得看業務場景,過度為了設計而設計,殺雞用牛刀的後果就是很不順手,而且還容易誤傷

如果是個人開發,show 代碼亮逼格這無可厚非,但如果是實際團隊開發,最好還是需要考慮團隊的合作選型,不然你寫的代碼只有你能維護,估計最後哭的還是自己。

好了,本篇到此結束!(///▽///)

跨平台完整項目與文章:

  • Flutter 開源項目與文章
  • React Native 開源項目與文章
  • Weex 開源項目與文章

完整文章目錄在項目首頁 ReadMe

其他文章

《移動端跨平台開發的深度解析》

推薦閱讀:

相关文章