深入了解 React Native(初學者教程)

已發表: 2022-03-11

當 React Native 發佈時,第一反應是非常積極的。 傳統上,當我們考慮移動領域的 Web 技術時,會想到 Apache Cordova 之類的東西,它允許我們將網站或 Web 應用程序打包為移動平台的應用程序。 在本初學者教程中,我們將了解 React Native 的架構、React Native 背後的理念,以及它與同一領域的其他解決方案有何不同。 在本文的最後,我們將把一個 React “Hello World” 應用程序轉換成一個 React Native 應用程序。

讓我們首先說 Rea​​ct Native 是一項相對較新的技術。 它自 2015 年 3 月起正式上市,自當年年初以來一直處於內測階段,並在此之前在 Facebook 內部使用了一段時間。 “羅馬不是一天建成的”這句話通常也適用於技術。 像grunt這樣的工具和像 Node.js 這樣的平台需要數年時間才能成熟。 在網絡世界中,事情發展得很快,每天都有大量的框架、包和工具出現,開發人員往往會變得更加懷疑,不想趕上每一個炒作的潮流,只是意識到這一點他們最終陷入了供應商鎖定的境地。 我們將深入探討 React Native 的特殊之處,為什麼它是一項值得研究的技術,並涵蓋一些並非都是獨角獸和彩虹的例子。

引擎蓋下

在談論移動網絡技術時,可用的解決方案通常屬於以下類別之一。

在移動 Web 瀏覽器中捆綁 Web 應用程序

Web 應用程序存在於移動瀏覽器中,通常稱為 WebView。 無需任何重大重構,網站或 Web 應用程序就可以在移動設備上運行。 我們可能需要考慮移動瀏覽器事件,例如點擊或收聽設備方向變化和較小的屏幕以獲得完整的用戶體驗,但我們有一個工作量很小的移動版本。 Cordova/PhoneGap 是該類別中最受歡迎的選項。 不幸的是,這個選項有一個很大的缺點:在某些情況下,使用 Cordova 開發的應用程序比本機應用程序慢得多,尤其是對於圖形密集型應用程序。 在其他情況下,移動操作系統實際上並未提供移動瀏覽器中可用的所有 WebView 功能。 用戶體驗也可能不同於原生應用程序; 這可能是由於應用程序或平臺本身造成的。 這個問題的範圍可能從滾動條感覺不一樣到點擊元素時有明顯的延遲。

編譯為原生技術

一個完全不同的解決方案是最終創建一個本機代碼庫。 這是通過將原始源代碼轉換為另一種編程語言來實現的。 我們用原生性能換取具有一些不確定性的抽象層。 在閉源解決方案的情況下,我們甚至不確定引擎蓋下發生了什麼以及我們正在處理什麼樣的黑匣子。 在其他情況下,我們不確定下一次移動操作系統更新將破壞我們的代碼的程度,以及修復或更新何時可用。 此類的一個流行示例是 Haxe。

使用 JavaScript 層

在這裡,我們使用移動環境的 JavaScript 引擎並在那裡執行我們的 JavaScript。 原生控件映射到 JavaScript 對象和函數,所以當我們調用一個名為fancyButtonRightHere()的函數時,屏幕上會出現一個按鈕。 NativeScript 或 Appcelerator Titanium 是此類的知名示例。

React Native 可以歸為第三類。 對於 iOS 和 Android 版本,React Native 在底層使用 JavaScriptCore,這是 iOS 上的默認 JavaScript 引擎。 JavaScriptCore 也是 Apple Safari 瀏覽器中的 JavaScript 引擎。 如果願意,OS X 和 iOS 開發人員實際上可以直接與它交互。

一個很大的區別是 React Native 在單獨的線程中運行 JavaScript 代碼,因此用戶界面不會阻塞,動畫應該是絲滑的。

React 是關鍵特性

值得注意的是,React Native 中的“React”並不是偶然出現的。 對於 React Native,我們需要了解 React 到底提供了什麼。 以下概念在 React 和 React Native 中的工作方式相同,儘管這些代碼示例是為在瀏覽器中運行而定制的。

單一渲染入口點

當我們看一個簡單的 React 組件時,我們可能會注意到的第一件事是該組件具有render功能。 事實上,如果組件內部沒有定義渲染函數,React 會拋出錯誤。

 var MyComponent = function() { this.render = function() { // Render something here }; };

特殊之處在於,我們不會在這里處理 DOM 元素,而是返回一個基於 XML 的構造,該構造表示將在 DOM 中呈現的內容。 這種基於 XML 的結構稱為 JSX。

 var MyComponent = function() { this.render = function() { return <div className="my-component">Hello there</div>; }; };

一個特殊的 JSX 轉換器獲取所有看起來像 XML 的代碼並將其轉換為函數。 這是轉換後的組件的樣子:

 var MyComponent = function() { this.render = function() { return React.createElement("div", { className: "my-component" }, "Hello there"); }; };

最大的優勢是通過快速查看組件,我們總是知道它應該做什麼。 例如,一個<FriendList />組件可能會渲染多個<Friend />組件。 除了在render函數內部之外,我們無法在其他任何地方渲染我們的組件,因此我們永遠不會擔心我們不知道渲染組件的確切來源。

單向數據流

為了構建組件的內容,React 提供了屬性或 props 的簡稱。 與 XML 屬性類似,我們將 props 直接傳遞給組件,然後可以在構造的組件內使用 props。

 var Hello = function(props) { this.render = function() { return <div className="my-component">Hello {props.name}</div>; }; }; var Greeter = function() { this.render = function() { return <Hello name="there" /> } };

這導致我們的組件處於樹狀結構中,並且我們只允許在構造子元素時傳遞數據。

重新渲染更改

除了 props,組件還可以有一個內部狀態。 這種行為最突出的例子是點擊計數器,它會在按下按鈕時更新其值。 點擊次數本身將保存在狀態中。

每個 prop 和 state 更改都會觸發組件的完整重新渲染。

虛擬 DOM

現在,當 props 或 state 發生變化時,一切都被重新渲染,React 本身為什麼表現得那麼好? 神奇的成分是“虛擬 DOM”。 每當需要重新渲染某些東西時,都會生成更新後的 DOM 的虛擬表示。 虛擬 DOM 由在組件樹之後建模的元素的輕量表示組成,使得生成它們的過程比生成真實 DOM 元素的過程更加高效。 在將更改應用到真實 DOM 之前,會進行檢查以確定更改發生在組件樹中的確切位置,創建差異,並且僅應用那些特定的更改。

本 React Native 教程入門

為了開發 React Native,初學者需要設置一些先決條件。 由於 iOS 是第一個受支持的平台,也是我們在本教程中介紹的平台,因此我們需要 macOS 和 Xcode,至少版本 6.3。 還需要 Node.js。 使用brew install watchman watchman 通過 Brew 包管理器安裝 Watchman 有幫助。 雖然這不是必需的,但在處理我們的 React Native 項目中的大量文件時它會有所幫助。

React Native:最健全的移動應用程序開發框架。
鳴叫

要安裝 React Native,我們只需要使用npm install -g react-native-cli安裝 React Native 命令行應用程序。 然後調用react-native命令幫助我們創建一個新的 React Native 應用程序。 運行react-native init HelloWorld會創建一個名為HelloWorld的文件夾,可以在其中找到樣板代碼。

終端動畫展示瞭如何設置 React Native “Hello World” 應用程序。

轉換 React 應用程序

鑑於 React 是來自 React 庫的關鍵特性和核心原則,讓我們來看看將最小的 React “Hello World” 應用程序轉換為 React Native 應用程序需要什麼。

我們在這個代碼示例中使用了一些 ES2015 特性,特別是類。 堅持使用React.createClass或者使用類似於流行的模塊模式的函數形式是完全可行的。

 var React = require('react'); class HelloThere extends React.Component { clickMe() { alert('Hi!'); } render() { return ( <div className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</div> ); } } React.render(<HelloThere name="Component" />, document.getElementById('content'));

第 1 步:擁抱 CommonJS 模塊

在第一步中,我們需要更改要求 React 模塊使用react-native

 var React = require('react-native'); class HelloThere extends React.Component { clickMe() { alert('Hi!'); } render() { return ( <div className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</div> ); } } React.render(<HelloThere name="Component" />, document.getElementById('content'));

在開發 React Web 應用程序時,工具管道的一部分通常是 React Native 的一個組成部分。

第 2 步:沒有 DOM

毫不奇怪,移動設備上沒有 DOM。 在我們之前使用<div />的地方,我們需要使用<View />並且在我們使用<span />的地方,我們這裡需要的組件是<Text />

 import React from 'react'; import {View, Text, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert('hi!'); } render() { return ( <View className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</View> ); } } React.render(<HelloThere name="Component" />, document.getElementById('content'));

雖然將文本直接放在<div />元素中非常方便,但在原生世界中,文本不能直接放在<View />中。 為此,我們需要插入一個<Text />組件。

 import React from 'react'; import {View, Text, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert('hi!'); } render() { return ( <View className="box" onClick={this.clickMe.bind(this)}> <Text>Hello {this.props.name}. Please click me.</Text> </View> ); } } React.render(<HelloThere name="Component" />, document.getElementById('content'));

第 3 步:內聯樣式是必經之路

React Native 允許我們使用 Flexbox 建模,而不是使用我們在網絡世界中非常熟悉的floatinline-block 。 有趣的是 React Native 不使用 CSS。

 import React from 'react'; import {View, Text, StyleSheet, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert('hi!'); } render() { return ( <View style={styles.box} onClick={this.clickMe.bind(this)}> <Text>Hello {this.props.name}. Please click me.</Text> </View> ); } } var styles = StyleSheet.create({ box: { borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100 } }); React.render(<HelloThere name="Component" />, document.getElementById('content'));

使用內聯樣式對初學者來說似乎很困惑。 這類似於 React 開發人員在面對 JSX 和之前使用 Handlebars 或 Jade 等模板引擎時必須經歷的過渡。

這個想法是我們在使用 CSS 的方式中沒有全局樣式表。 我們直接在組件級別聲明樣式表,因此我們擁有查看組件功能、它創建的佈局以及它應用的樣式所需的所有信息。

 import React from 'react'; import {Text} from 'react-native'; var Headline = function(props) { this.render = () => <Text style={headlineStyle.text}>{props.caption}</Text>; }; var headlineStyles = StyleSheet.create({ text: { fontSize: 32, fontWeight: 'bold' } }); module.exports = Headline;

第 4 步:處理事件

相當於點擊網頁是點擊移動設備上的一個元素。 讓我們更改我們的代碼,以便在我們點擊元素時彈出“警報”。

 import React from 'react'; import {View, Text, StyleSheet, TouchableOpacity, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert("Hi!") } render() { return ( <TouchableOpacity onPress={this.clickMe()}> <View style={styles.box}> <Text>Hello {this.props.name}. Please click me.</Text> </View> </TouchableOpacity> ); } } var styles = StyleSheet.create({ box: { borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100 } }); React.render(<HelloThere name="Component" />, document.getElementById('content'));

我們需要顯式地使用觸發事件的元素,而不是直接在<View />組件上可用的事件,在我們的例子中是按下視圖時的觸摸事件。 有不同類型的可觸摸組件可用,每一種都提供不同的視覺反饋。

第 5 步:跨平台自定義行為

通過訪問Platform.OS的值,可以檢測 React Native 應用程序在哪個平台上運行。 假設在上面的示例中,我們希望根據我們運行的平台顯示不同的警報消息。 我們可以這樣做:

 ... clickMe() { var message = ''; if(Platform.OS == 'ios') { message = 'Welcome to iOS!'; } else if(Platform.OS == 'android') { message = 'Welcome to Android!'; } Alert.alert(message); } ...

或者,也可以使用select方法,它提供類似 switch 的語法:

 … clickMe() { Alert.alert(Platform.select({ ios: 'Welcome to iOS!', android: 'Welcome to Android!' }) ); } ...

第 6 步:自定義字體和react-native link

為了添加自定義字體,我們需要跳過一些環節。 首先,確保字體全名和字體文件名相同:iOS 將使用字體全名來選擇字體,而 Android 使用文件名。

因此,如果您的字體的全名是myCustomFont ,請確保字體的文件名是myCustomFont.ttf

之後,我們需要創建一個 assets 文件夾並將 npm 指向它。 我們可以通過首先在應用程序根目錄的assets/fonts下創建文件夾來做到這一點。 任何其他目錄都可以,但這是用於字體目錄的常規名稱。

我們可以通過在 React 的 npm 集成部分 rnpm 下添加Assets屬性來告訴 npm 我們在哪裡擁有我們的資產:

 "rnpm": { "Assets": [ "./assets/fonts/" ] }

完成所有這些之後,我們終於可以運行react-native link 。 這會將字體複製到正確的目錄,並將必要的 xml 添加到 iOS 上的 info.plist。

完成後,我們可以通過在任何樣式表中通過其全名引用它來使用我們的字體。 讓我們在Text元素上使用它:

 import React from 'react'; import {View, Text, StyleSheet, TouchableOpacity, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert("Hi!") } render() { return ( <TouchableOpacity onPress={this.clickMe()}> <View style={styles.box}> <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text> </View> </TouchableOpacity> ); } } var styles = StyleSheet.create({ box: { borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100 }, message: { fontFamily: 'myCustomFont' } }); React.render(<HelloThere name="Component" />, document.getElementById('content'));

第 7 步:四處移動

React Native 使用與 Flexbox 相同的規則來佈局組件。 假設我們想將按鈕放置在屏幕底部:讓我們用容器View包裝TouchableOpacity

 <View style={styles.container}> <TouchableOpacity onPress={this.clickMe.bind(this)}> <View style={styles.box}> <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text> </View> </TouchableOpacity> </View>

現在讓我們定義container樣式以及其他已定義的樣式:

 container: { flex: 1, justifyContent: 'center', alignItems: 'center' }

讓我們關注justifyContentalignItems 。 這兩個屬性分別控制組件如何沿其主軸和輔助軸對齊。 默認情況下,主軸是垂直軸,次軸是水平軸(您可以通過將flexDirection屬性設置為row來更改它)。

justifyContent有六個可能的值,可以設置為:

  • flex-start會將所有元素放在一起,在組件邊界框的開頭。
  • flex-end會將所有元素定位在末尾。
  • center會將所有元素定位在邊界框的中心。
  • space-around將均勻分佈組件,並將組件在其創建的邊界框中居中。
  • space-evenly也會均勻分佈組件,但它會嘗試在組件和其他邊界之間留出等量的空間。
  • space-between將通過保持相鄰組件之間的間距相等來分散組件。

alignItems可以設置為四個可能的值: flex-startflex-endcenterstretch 。 前三個的行為與justifyContent ,而stretch將設置組件沿軸佔據所有可用空間,以便完全填充軸。

因此,由於我們希望TouchableOpacity顯示在底部並沿水平軸居中,我們可以像這樣更改樣式:

 container: { flex: 1, justifyContent: 'flex-end', alignItems: 'center' }

有關justifyContentalignItems值的更多信息,請參見此處和此處。

第 8 步:註冊應用程序

在瀏覽器上使用 React 進行開發時,我們只需要定義一個掛載點,調用React.render ,然後讓 React 發揮它的魔力。 在 React Native 中,這有點不同。

 import React from 'react'; import {View, Text, StyleSheet, TouchableOpacity, Alert, Platform} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert(Platform.select({ ios: 'Welcome to iOS!', android: 'Welcome to Android!' })); } render() { return ( <View style={styles.container}> <TouchableOpacity onPress={this.clickMe.bind(this)}> <View style={styles.box}> <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text> </View> </TouchableOpacity> </View> ); } } var styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'flex-start', alignItems: 'center' }, box: { borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100 }, message: { fontFamily: 'myCustomFont' } }); var MainComponent = function() { this.render = function() { return <HelloThere name="Component" />; } }; AppRegistry.registerComponent('MainComponent', function() { return MainComponent; });

我們必須為 Objective-C 方面的事物註冊組件,這是使用AppRegistry對象完成的。 我們提供的名稱必須與 Xcode 項目中的名稱相匹配。

我們的 Hello World React Native 應用程序的代碼行數明顯多於其 Web 應用程序,但另一方面,React Native 將關注點分離更進一步,特別是因為樣式是使用組件定義的。

附帶說明一下,我們不應該在render方法中將clickMe方法重新綁定到this上下文,尤其是當我們的 React (Native) 應用程序變得更加複雜時。 它會在每次渲染調用時重新綁定方法,這可能會變得很多。 另一種方法是在構造函數中綁定方法。

運行應用程序

要運行應用程序,我們需要將index.ios.js文件的內容替換為上一步轉換後的應用程序的代碼。 然後我們只需要打開 Xcode 項目並按下大運行按鈕。 首先,會打開一個帶有 React Native 服務器的終端,然後會出現模擬器窗口。 React Native 服務器創建一個包,然後本機應用程序將獲取該包。 這允許類似 Web 開發的快速開發週期,其中更改將幾乎立即反映在模擬器中。

對於 Android,將以下內容添加到您的package.json文件中的scripts下就足夠了:

 "android-linux": "react-native bundle --platform android --dev false --entry-file index.ios.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/ main/res && react-native run-android"

然後運行npm run android-linux 。 確保事先存在android/app/src/main/assets目錄。

終端彈出後,我們的應用程序將顯示在模擬器中。 按 CMD+D 將顯示開髮菜單。 單擊該框將顯示警報。 iOS版本:

帶有“嗨”的警報彈出窗口的 Apple 手機。

Android 呈現如下內容:

帶有“嗨”的警報彈出窗口的 Android 手機。

對於分發,擁有一個指向本地開發服務器的應用程序對我們來說是行不通的。 出於這個原因,我們可以在 React Native 服務器沒有運行時使用命令react-native bundle創建包。 在這種情況下,我們需要更新AppDelegatedidFinishLaunchingWithOptions方法以使用離線包。

這個示例應用程序也可以在 Github 上找到。

使用 React Native

另一件值得一提的事情是,我們不僅將 React 概念和 JavaScript 用於我們的移動應用程序,而且一些 Web 開發人員習慣使用的工作流也可用於 React Native。 當來自 Web 開發時,我們習慣於開發工具、檢查元素和實時重新加載。

React Native 的工作方式是將我們所有的 JavaScript 文件放在一個包中。 此捆綁包由服務器提供或與應用程序捆綁在一起。 第一個對於模擬器中的開發非常有用,因為我們可以啟用實時重新加載。 React 提供的開發者菜單絕不像 Chrome 開發者工具那樣強大,但它提供了一種非常類似於 web 的開發者體驗,可以使用 Chrome(或 Safari)開發者/調試器工具進行實時重新加載和調試。

Web 開發人員熟悉 JSFiddle 或 JSBin,這是一個用於快速 Web 測試的在線遊樂場。 有一個類似的環境允許我們在 Web 瀏覽器中試用 React Native。

React Native:可靠的現代選擇

我最初建議對 React Native 採取更謹慎的方法。 今天,它是一個成熟而可靠的選擇。

React 的一大優勢是它不會強加於您的工作流程,因為它只代表視圖層。 您想定義自己的 Grunt 管道嗎? 還是您更願意使用 Webpack? 你會使用 Backbone.js 來滿足你的模型需求嗎? 還是您想使用純 JavaScript 對象? 所有這些問題的答案完全取決於你,因為 React 對這些選擇沒有任何限制。 正如官方網站所說:“由於 React 對您的技術堆棧的其餘部分不做任何假設,因此很容易在現有項目中的一個小功能上進行嘗試。”

在某種程度上,React Native 也是如此。 移動開發人員可以將 React Native 集成為其應用程序的一部分,利用受 Web 啟發的開發工作流程,並在需要時選擇更大規模地集成該庫。

無論如何,有一件事是肯定的:React Native 不會消失。 Facebook 在其應用商店中擁有多個基於 React Native 的應用程序中佔有很大的份額。 圍繞 React Native 的社區非常龐大,並且還在繼續增長。

相關:構建 QR 掃描儀:React Native 相機教程