決鬥:React Native 與 Cordova
已發表: 2022-03-11由於智能手機和移動應用程序變得如此流行,Web 開發人員一直在尋找使用 JavaScript 創建移動應用程序的方法。 這種流行導致了許多 JavaScript 框架的開發,這些框架能夠在移動設備上運行類似本機的應用程序。 目前,Cordova 和 React Native 是最受歡迎的選擇。 Cordova 支持 iOS、Android 和 Windows Phone 移動平台。 另一方面,使用 React Native,Android、iOS 和 UWP 是開發人員的目標。 (UWP 代表通用 Windows 平台,這是微軟的平台,允許相同的應用程序在 Windows Phone 10 Mobile、Xbox One 和 Windows 10 上運行。)
從表面上看,React Native 和 Cordova 似乎佔據了相同的空間。 然而,與所有技術一樣,在某些方面,有一個亮點,而另一個則不足。 因此,為了更好地了解每種技術,並了解它們的優缺點,我們將深入研究每種技術的細節,並在不同學科之間進行比較。
哲學差異
重要的是要記住,React Native 的標語“一次學習,隨處編寫”不同於通常的跨平台口號“編寫一次,隨處運行”。 這導致了兩件事:首先,我們不能只從我們的 Web 項目中獲取現有的 React 代碼庫,然後只需點擊幾下就可以將其變成移動應用程序。 然而,React 和 React Native 確實共享了許多關鍵概念,其中一個例子就是它們的組件系統,因此,React Native 立即讓人感到熟悉。 儘管 React 與 React Native 有很多相似之處,但也存在一些核心差異,從處理樣式表的方式到我們可以使用的組件類型。
其次,在針對不同平台時,我們可能會發現自己無法共享 React Native 代碼。 當我們希望用戶界面元素在其特定平台上以本機方式運行時,就會發生這種情況,從而為用戶提供更好的體驗和對應用程序更原生的感覺。 一個明顯的例子是 Android 應用程序中的抽屜側邊菜單,這在 iOS 應用程序中非常少見。
科爾多瓦不認同這種理念。 開始開發純 Web 應用程序,然後將其捆綁為 Cordova 應用程序,並為我們想要定位的所有(移動)平台重用盡可能多的代碼並不少見。
發展自由
在移動設備上,Cordova 在集成的移動 Web 瀏覽器中運行一個單頁應用程序,稱為 WebView,然後將其包裝為原生應用程序。 雖然它從外部看起來像是一個原生應用程序,但我們的 Web 代碼是在移動瀏覽器引擎中運行的。 對我們來說,這意味著我們不依賴於特定的庫或框架。 如果我們使用原生 JavaScript、jQuery、Angular 或其他任何東西,這些選項中的任何一個都可以與 Cordova 捆綁到移動應用程序中。 Cordova 不會強加於我們的技術堆棧。 只要我們有一個index.html
文件,我們就可以開始了。 一個簡單的例子是下面的代碼片段:
<html> <head> <title>My Cordova App</title> </head> <body> <div>Tap me</div> <script> // Select our element var element = document.getElementById('tapme'); // Send an alert once it was tapped/clicked element.addEventListener('click', function() { alert('Hello there!'); }); </script> </body> </html>
這個例子意味著我們可以使用幾乎任何我們想要的東西,比如使用像 NPM 或 Bower 這樣的包管理器,使用像 Babel、CoffeeScript 或 TypeScript 這樣的轉譯器,像 Webpack 或 Rollup 這樣的捆綁器,或者其他完全不同的東西。 沒關係,只要結果是一個index.html
文件,它可以加載我們需要的所有 JavaScript 和样式表。
顧名思義,React Native 建立在 React 之上。 重要的是要理解 React Native 中的 React 部分是它的核心特性之一。 如果你不喜歡 React 的聲明性特性,包括 JSX、它的組件化和數據流,那麼你可能不會對 React Native 感到滿意。 雖然 React Native 對 React 開發人員來說立即感覺很熟悉,但乍一看,還是有一些差異需要記住。 使用 React Native,我們沒有任何 HTML 或 CSS。 相反,這項技術專注於 JavaScript 方面。 作為 CSS 的替代方案,樣式是內聯編寫的,而 Flexbox 是默認樣式模型。
最簡單的 React Native 應用程序看起來類似於這個例子:
// Import the React module for JSX conversion import { React } from 'react'; // Import React Native's components import { View, Text, AppRegistry, TouchableOpacity, } from 'react-native'; // Create an App component const App = () => { // Define our press handler const onPress = () => alert('Hello there!'); // Compose the components we are going to render return ( <View> <TouchableOpacity onPress={onPress} /> <Text>Tap me!</Text> </TouchableOpacity> </View> ); }; // Registers the `App` component as our main entry point AppRegistry.registerComponent('App', () => App);
React Native 有自己的打包器。 它將所有 JavaScript 文件捆綁到一個巨大的文件中,然後由 Apple 的 JavaScript 引擎 JavaScriptCore 使用和執行。 JavaScriptCore 正在 iOS 和 Android 上使用,而 ChakraCore 正在為 React Native UWP 應用程序提供支持。 默認情況下,React Native 使用 JavaScript 轉譯器 Babel,允許我們使用 ECMAScript 2015+ (ECMAScript 6) 語法。 雖然沒有必要使用 ECMAScript 2015+ 語法,但絕對鼓勵使用它,因為所有官方示例和第三方模塊都包含它。 由於 React Native 負責打包和轉譯過程,我們的應用程序代碼和第三方模塊可以利用這些功能,而無需自己配置工具。
總而言之,React Native 是一種以 React 為中心的固執己見的移動開發方法,而 Cordova 允許我們將 Web 技術捆綁在 WebView shell 中。
原生外觀
對用戶來說重要的一件事是擁有應用程序的原生外觀。 由於 Cordova 應用程序通常是簡單的 Web 應用程序,因此有一些事情起初可能會讓人感到奇怪。 問題的範圍可能從缺少點擊區域的視覺反饋,到感覺不像在本機應用程序中那樣流暢的滾動,再到點擊事件有 300 毫秒的延遲。 雖然所有這些問題都有解決方案,但我們應該記住,如果我們希望我們的 Cordova 應用程序感覺盡可能接近原生應用程序,我們可能需要付出一些額外的努力。 在 Cordova 中,我們無法訪問任何本機控件。 如果我們想要擁有原生的外觀和感覺,我們有兩個選擇:要么使用 HTML 和 CSS 重新創建原生控件,例如按鈕和輸入元素,要么實現直接訪問這些原生控件的原生模塊。 我們可以自己完成,也可以使用第三方庫,如 Ionic 或 Onsen UI。 請注意,隨著操作系統更新的出現,及時更新它們很重要。 有時,移動操作系統的外觀會煥然一新,就像 iOS 7 推出時那樣。 擁有一個無法適應的應用程序會使用戶失去體驗。 我們還可以求助於包含 Cordova 插件,將我們連接到事物的本機方面。 最完整的原生控件之一是 Microsoft 的 Ace 庫。
另一方面,使用 React Native,我們可以訪問原生控件和開箱即用的交互。 Text
、 TextInput
或Slider
等組件映射到其原生對應項。 雖然某些組件適用於所有平台,但其他組件僅適用於特定平台。 我們越是希望我們的應用程序具有原生的外觀和感覺,我們就越需要使用僅適用於該特定平台的組件,因此我們的代碼庫就越不同。 心靈觸摸交互和手勢也是 React Native 的一部分。
比較性能
由於 Cordova 只有一個 WebView 可供使用,我們必然會受到 WebView 的限制。 例如,在 4.0 版本之後,Android 終於開始使用(速度更快的)Chrome 引擎作為默認的 WebView。 在使用 iOS 時,在默認 WebView 引擎中運行的應用程序很長時間以來都比 Safari 移動瀏覽器中的相同應用程序慢得多。 此外,由於 JavaScript 是單線程的,如果我們的應用程序代碼中發生的事情太多,我們可能會遇到問題。 這些限制導致動畫遲緩,我們的應用程序可能感覺不像我們希望的那樣響應。 雖然我們可以在這里和那裡使用一些技巧,但最終,我們會受到移動瀏覽器的限制。

React Native 利用多個線程,因此渲染 UI 元素在它們自己的線程中運行。 因為 React 組件鏈接到原生視圖,所以 JavaScript 並沒有在 React Native 中做繁重的工作。
開發人員工作流程
Cordova 提供了一個命令行實用程序來創建新的項目模板、在模擬器中啟動應用程序並在生產模式下為實際設備構建應用程序。 大多數時候,我們在桌面瀏覽器上開發應用程序,以後可能會將其捆綁為移動應用程序。 有了 Cordova 提供的自由,我們需要自己處理開發工作流程。 如果我們想在設備上實時重新加載,我們需要自己實現它。 為了調試 Cordova 應用程序,我們應用了用於調試網站的相同原則。 例如,在 iOS 中,我們將通過 USB 連接我們的移動設備,打開 Safari 及其開發者工具。
React Native 提供了類似的命令行界面,並提供了 Web 開發人員熟悉的開發工作流程。 我們開箱即用地進行實時重新加載。 一旦我們更改了一個 React 組件,我們的應用程序就會重新加載我們所做的更改。 最令人興奮的功能之一是熱模塊替換,它部分地重新加載我們所做的組件中的更改,而不會改變應用程序的狀態。 我們甚至可以連接到實際設備,看看我們的更改是否像我們期望的那樣在真實設備上工作。 我們的 React Native 應用程序可以使用桌面版 Chrome 進行遠程調試。 錯誤處理在 React Native 中很明顯; 如果我們遇到錯誤,我們的應用程序會顯示紅色背景,並顯示堆棧跟踪。 感謝 sourcemaps,我們可以看到錯誤的確切位置。 當我們點擊它時,我們選擇的編輯器會在代碼的精確位置打開。
可擴展性和對本機功能的訪問
在 JavaScript 方面,我們可以自由使用任何 JavaScript 庫,包括來自 NPM 的包。 但是,由於 React Native 不是瀏覽器環境,我們可能會發現很難使用依賴於 DOM 的代碼。 React Native 包含 CommonJS 和 ES2015 模塊,因此使用這些格式的任何庫都易於集成。
Cordova 和 React Native 都能夠創建和使用連接到本機端的插件。 Cordova 提供了一個低級 API 來創建我們自己的 API,這給了我們很多控制權,但會導致使用更多的原生和 JavaScript 樣板。
如果我們假設用 Objective-C 編寫一個 Cordova iOS 插件,它可能看起來像下一個代碼片段。 我們的插件只會記錄輸入參數。
#import <Cordova/CDVPlugin.h> // Create a class that inherits from CDVPlugin @interface Log : CDVPlugin - (void)log:(CDVInvokedUrlCommand*)command; @end // The actual implementation of the class we just defined @implementation Log - (void)log:(CDVInvokedUrlCommand*)command { CDVPluginResult* pluginResult = nil; // We are getting all parameters and taking the first one NSString* echo = [command.arguments objectAtIndex:0]; // We are checking for the validity of the parameters if (echo != nil && [echo length] > 0) { // We are just printing the parameter using the native log method NSLog(echo); // Let's create a result for the plugin pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:echo]; } // Let's send a signal back with the plugin's result [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } @end
為了使用該模塊,這段 JavaScript 代碼將有助於:
window.log = function(str, callback) { cordova.exec(callback, function(err) { callback('Nothing to echo.'); }, "Log", "log", [str]); };
要使用該插件,我們只需要調用log
函數:
window.log('Hello native!');
另一方面,React Native 遵循不同的理念。 在編寫插件時,它會自動將 JavaScript 類型映射到其本機對應類型,這使得將本機代碼與 JavaScript 連接起來更加容易。 讓我們看一下使用 React Native 創建原生模塊所必需的一段代碼:
#import "RCTBridgeModule.h" @interface Log : NSObject <RCTBridgeModule> @end @implementation Log RCT_EXPORT_MODULE(); // This makes this method available NativeModules.Log.log RCT_EXPORT_METHOD(log:(NSString *)message) { NSLog(message); } @end
React Native 通過調用RCT_EXPORT_MODULE
和RCT_EXPORT_METHOD
為我們綁定模塊。 我們現在可以使用NativeModules.Log.log
訪問它,如下所示:
import { React } from 'react'; import { View, Text, AppRegistry, NativeModules TouchableOpacity, } from 'react-native'; // Create an App component const App = () => { // Log with our module once we tap the text const onPress = () => NativeModules.Log.log('Hello there'); return ( <View> <TouchableOpacity onPress={onPress} /> <Text>Tap me!</Text> </TouchableOpacity> </View> ); }; // Registers the `App` component as our main entry point AppRegistry.registerComponent('App', () => App);
雖然我們只仔細研究了使用 Objective-C 在 iOS 中創建模塊,但同樣的原則也適用於使用 Java 為 Android 創建模塊。
我們需要在每個平台的項目文件中鏈接本機插件。 以 iOS 為例,這意味著我們必須將編譯後的原生部分與我們的應用程序鏈接並添加相應的頭文件。 這可能是一個漫長的過程,尤其是在有很多本機模塊的情況下。 幸運的是,通過使用名為 rnpm 的命令行實用程序大大簡化了這一點,該實用程序已成為 React Native 本身的一部分。
結論:React Native 還是 Cordova?
React Native 和 Cordova 有不同的用途,因此可以滿足不同的需求。 因此,很難說一種技術在所有學科中都優於另一種技術。
通過使用 Cordova,您可以快速將現有的單頁應用程序轉變為適用於不同平台的移動應用程序,代價是交互不一定具有對特定平台的原生感覺。
使用 React Native,應用程序具有更原生的外觀和感覺,但代價是為某些目標平台重新實現代碼片段。 如果你已經涉足 React 並且對開發移動應用程序感興趣,那麼 React Native 感覺就像是一個自然的擴展。