近期和一些朋友聊到了 React-Native
的官方重構狀態,而剛好近期釋出的 0.59.x 系列版本中,上層設計出現了比較大的調整,結合體驗之後的狀態,就想聊聊 React-Native
的現狀、新版本的升級體驗、還有新支援的 React Hook 等特性。
本篇並不是原始碼解析和教程,更多是討論和記錄描述。筆者一直致力於 Android
、React-Native
、Flutter
等大前端開發,有時也會寫寫 React
和 Vue
,本篇文章也是希望能夠和大家交流,可以的話歡迎提出問題或者建議,最後同樣希望文章能對你有所啟發。
皮一下,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 等內建包拆分,通過社群獨立維護,並逐步模糊React
和React-Native
的界限。 - 2、更新 JavaScriptCore 、upgrade 和 CLI 工具。
- 3、支援 React Hooks 。
- 4、修復了
FlatList
等列表控制元件中的諸多問題。
未來版本的重構主要目標有:
- 1、減輕 JSBridge 的依賴。
- 2、通過 Fabric UI架構,將
Shadow
層、UIManager
、NativeModule
從 Java 移到 C++ 中,從而支援 雙向的同步和非同步渲染與呼叫 。
可以看出 0.59 版本中的重構和拆分,都是在為了下一步的重構做準備,更多具體的下一代重構內容分析,可以在京東的 《庖丁解牛!深入剖析 React Native 下一代架構重構》 中查閱,這裡就不多贅述了。
同樣在攜程的專案中: 《攜程開源RN開發框架CRN》 文章也表示在第一時間更新到了 0.59.x 版本,現在還會覺得 React-Native
“要涼” 了嘛?
題外話 :
如今的程式設計界裡存在各種“黨爭”,比如前端中
Vue
、React
、Angular
,跨平臺的Cordova
、Weex
、React-Native
、Flutter
等,而我在考慮選擇框架時,一般會從以下幾點優先順序先後排序:
- 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
自帶的 webView
、netinfo
、 async-storage
等外掛替換到 react-native-community 下提供,並替換一些棄用 API 。
2、第三方庫不相容 :
這也是 React-Native
中比較頭疼的問題,因為第三方包的維護參差不齊,基本上如果作者不維護或維護不及時,那就只能自己苦笑動手了,就像本次 GSYGithubAPP
在升級過程中就遇到有:
-
升級後遇到
realm
庫在 Xcode 上的編譯錯誤錯誤,詳細可見 GSYGithubAPP#66 ,雖然問題不大,可自行通過簡單本地改庫解決,這也是目前專案的升級還未合併到master
的原因之一。 -
react-native-router-flux
與react-navigation
的升級版本需要相互對應,同時需要增加react-native-gesture-handler
依賴,並且在index.js
入口處提前匯入來解決一些問題。 -
各類第三方外掛的 Android
targetSdk
和supportSdk
等版本和依賴方式問題。
3、node_module “黑洞” :
這類問題屬於看人品,比如 GSYGithubAPP
專案是從 0.57 升級到 0.59 的,而 BackAndroid
在 0.58 已經被完全棄用,其中專案剛好存在一個 modal
外掛使用了 BackAndroid
,雖然作者也更新了外掛做相容,但是····
在更新了外掛之後,重新執行後卻依舊報錯?WTF,而明外掛原始碼已經沒有 BackAndroid
的痕跡,那錯誤哪裡來的?
通過 Chrome 的 Debug 檢視當前 bundle
原始碼,最後發現居然真的有BackAndroid
的存在,當時就判斷妥妥的快取問題。
在執行了無數遍的解除安裝 APP,關閉CLI,刪除 node_module
重灌後,最終還是通過刪除快取 rm -rf ~/.rncache
和 rm -rf $TMPDIR/*
,再重新安裝node_module
執行才解決問題。
-
總結
其實這也是為什麼我說 React-Native
等跨平臺開發,其實並沒有降低工作量的原因。跨平臺解決的是邏輯統一維護,而開發中過程中,很多時候會遇到相容開發的問題,並且平臺之間的適配同樣消耗時間。
我相信每個
React-Native
開發人員都十分討厭滿螢幕的紅色,所以不知哪一版開始,React-Native
把錯誤增加了紅黑相間的效果(¬_¬)。
三、React Hooks
React Hooks
其實也是我升級到 0.59 的目的之一,因為它確實是一個很有意思的設定。
事實上我並非嚴格意義上的前端人員,大部分時候我對 CSS 和 ES 的瞭解也不深入,但在 JS 的使用過程中有幾個讓我印象深刻的:
-
Redux :
Redux
的狀態管理設計,且由它衍生出的一系列後續和第三方外掛,我個人覺得這是React
當初能快速的風靡的助力之一。 -
HOC 和 ES7 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
相關更詳細的乾貨,推薦查閱:
最後說說編碼風格:
無論是 HOC
、 React Hooks
、Redux
等,其實我覺得都不存在所謂最優解,具體選擇使用還是得看業務場景,過度為了設計而設計,殺雞用牛刀的後果就是很不順手,而且還容易誤傷。
如果是個人開發,show 程式碼亮逼格這無可厚非,但如果是實際團隊開發,最好還是需要考慮團隊的合作選型,不然你寫的程式碼只有你能維護,估計最後哭的還是自己。
好了,本篇到此結束!(///▽///)
跨平臺完整專案與文章:
完整文章目錄在專案首頁 ReadMe