React Native 的未來與React Hooks

戀貓de小郭發表於2019-04-14

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

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

皮一下,React-Native 專案釋出4年多了,還沒有 1.0 版本麼(¬_¬)

React Native 的未來與React Hooks

一、現狀

相信大家對於 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 的痕跡,那錯誤哪裡來的?

React Native 的未來與React Hooks

通過 Chrome 的 Debug 檢視當前 bundle 原始碼,最後發現居然真的有BackAndroid 的存在,當時就判斷妥妥的快取問題。

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

  • 總結

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

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

React Native 的未來與React Hooks

三、React Hooks

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

React Native 的未來與React Hooks

事實上我並非嚴格意義上的前端人員,大部分時候我對 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 Native 的未來與React Hooks

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

最後說說編碼風格:

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

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

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

跨平臺完整專案與文章:

完整文章目錄在專案首頁 ReadMe

其他文章

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

我們還會再見嗎?

相關文章