數月前,Facebook 對外宣佈了正在開發的 React Native 框架,這個框架允許你使用 JavaScript 開發原生的 iOS 應用——就在今天,Beta 版的倉庫釋出了!
基於 PhoneGap 使用 JavaScript 和 HTML5 開發 iOS 應用已經有好幾年了,那 React Native 有什麼牛的?
React Native 真的很牛,讓大家興奮異常的主要原因有兩點:
- 可以基於 React Native使用 JavaScript 編寫應用邏輯,UI 則可以保持全是原生的。這樣的話就沒有必要就 HTML5 的 UI 做出常見的妥協;
- React 引入了一種與眾不同的、略顯激進但具備高可用性的方案來構建使用者介面。長話短說,應用的 UI 簡單透過一個基於應用目前狀態的函式來表達。
React Native 的關鍵就是,以把 React 程式設計樣式的能力帶到移動開發來作為主要標的。它的標的不是跨平臺一次編寫到處執行,而是一次學習跨平臺開發。這個是一個非常大的區別。這篇教程只介紹 iOS 平臺,不過你一旦掌握了相關的概念,就可以應用到 Android 平臺,快速構建 Android 應用。
如果之前只用過 Objective-C 或者 Swift 寫應用的話,你很可能不會對使用 JavaScript 來編寫應用的願景感到興奮。儘管如此,作為一個 Swift 開發者來說,上面提到的第二點應該可以激起你的興趣!
你透過 Swift,毫無疑問學習到了新的更多有效的編碼方法和技巧,鼓勵轉換和不變性。然而,構建 UI 的方式還是和使用 Objective-C 的方式一致。仍然以 UIKit 為基礎,獨斷專橫。
透過像 virtual DOM 和 reconciliation 這些有趣的概念,React 將函式式程式設計直接帶到了 UI 層。
這篇教程將帶著你一路構建一個 UK 房產搜尋應用:
如果你之前一點 JavaScript 都沒寫過,別擔心。這篇教程帶著你進行一步一步進行編碼。React 使用 CSS 屬性來定義樣式,一般比較容易讀也比較容易理解。但是如果你想瞭解更多的話,可以去看看 Mozilla Developer Network reference,很不錯的。
想要學習更多,繼續往下讀!
準備工作
React Native 框架託管在 GitHub 上。你可以透過兩種方式獲取到它:使用 git 克隆倉庫,或者下載一個 zip 壓縮包檔案。如果你的機器上已經安裝了 React Native,在著手編碼前還有其他幾個因素需要考慮。
- React Native 藉助 Node.js,即 JavaScript 執行時來建立 JavaScript 程式碼。如果你已經安裝了 Node.js,那就可以上手了。
首先,使用 Homebrew 官網提供的指引安裝 Homebrew,然後在終端執行以下命令:
brew install node
接下來,使用 homebrew 安裝 watchman,一個來自Facebook 的觀察程式:
brew install watchman
透過配置 watchman,React 實現了在程式碼發生變化時,完成相關的重建的功能。就像在使用 Xcode 時,每次儲存檔案都會進行一次建立。
React Native 有很多的依賴,需要在執行之前安裝好。在 React Native 檔案目錄下開啟一個終端,執行下麵程式碼:
npm install
這裡透過 Node 包管理器抓取到專案的所有依賴;功能上和 CocoaPods 或者 Carthage 類似。成功執行該命令後,你會發現一個 node_modules 檔案夾被建立,包含了各種外部依賴。
最後,啟動開發伺服器。在剛才開啟的終端中,執行下麵命令:
npm start
執行上面命令,你會看到:
$ npm start
> react-native@0.1.0 start /Users/colineberhardt/Projects/react-native
> ./packager/packager.sh
===============================================================
| Running packager on port 8081.
| Keep this packager running while developing on any JS
| projects. Feel free to close this tab and run your own
| packager instance if you prefer.
|
| https://github.com/facebook/react-native
|
===============================================================
React packager ready.
就這樣簡單,準備開始!指令碼在終端繼續執行,我們繼續。
至此,我推薦嘗試一個 React Native 示例來測試配置項。在 react-native/Examples/Movies 檔案夾下開啟專案,然後建立並且執行它,確保你可以正確地釋出這個 Movies 應用。
註意:在進入編碼工作之前,還有最後一件事 —— 在這個教程中,你需要編寫大量的 JavaScript 程式碼,Xcode 並非是最好的工具!我使用 Sublime Text,一個價格合理且應用廣泛的編輯器。不過,atom,brackets 或者其他輕量的編輯器都能勝任這份工作。
React Native 你好
在開始“搜房App”之前,先來個簡單的 Hello World App 熱熱身。在這一節裡,你將會使用到一些元件。
下載起始專案,解壓縮到react-native/Examples目錄中。解壓完成後,在Xcode中開啟 PropertyFinder 專案,不要直接執行這個專案,還需要加上一些JS!
在編輯器中開啟 PropertyFinderApp.js,將下麵這行程式碼加到檔案的開頭位置:
‘use strict’;
這行程式碼是用於開啟 Strict Mode,Strict mode的錯誤處理可以有所提高,JavaScript的一些語言缺陷也可以避免。簡而言之就是,JavaScript在這種樣式下工作地更好!
註意:想要研究一下 Strict Mode 的朋友,我會推薦你閱讀 Jon Resig 的文章:“ECMAScript 5 Strict Mode, JSON, and More”
然後,加上這一行:
var React = require(‘react-native’);
這句程式碼是將 react-native 模組載入進來,並將它賦值給變數 React 的。React Native 使用同 Node.js 相同的模組載入方式:require,這個概念可以等同於 Swift 中的“連結庫”或者“匯入庫”。
註意:想要瞭解更多關於 JavaScript 模組的知識,我推薦閱讀 Addy Osmani 寫的這篇文章。
在 require 陳述句的下麵,加上這一段:
var styles = React.StyleSheet.create({
text: {
color: ‘black’,
backgroundColor: ‘white’,
fontSize: 30,
margin: 80
}
});
以上程式碼定義了一段應用在 “Hello World” 文字上的樣式。如果你曾接觸過Web開發,那你很可能已經發現了:React Native 使用的是 CSS 來定義應用介面的樣式。
現在我們來關註應用本身吧!依然是在相同的檔案下,將以下程式碼新增到樣式程式碼的下麵:
class PropertyFinderApp extends React.Component {
render() {
return React.createElement(React.Text, {style: styles.text}, “Hello World!”);
}
}
是的,奏是 JavaScript class!
類 (class) 是在ES6中被引入的,縱然JavaScript一直在進步,但Web開發者受困於相容瀏覽器的狀況中,不能怎麼使用JS的新特性。React Native執行在JavaScriptCore中是,也就是說,你可以使用JS的新特性啦,完全不用擔心相容什麼的呢。
註意:如果你是一名 Web 開發者,我百分百鼓勵你要使用現代的JavaScript,然後使用像 Babel 這樣的工具生成相容性的 JavaScript,用於支援相容性不好的老瀏覽器。
PropertyFinderApp 繼承了 React.Component(React UI的基礎模組)。元件包含著不可變的屬性,可變的狀態變數以及暴露給渲染用的方法。這會你做的應用比較簡單,只用一個渲染方法就可以啦。
React Native 元件並不是 UIKit 類,它們只能說是在某種程度上等同。框架只是將 React 元件樹轉化成為原生的UI。
最後一步啦,將這一行加在檔案末尾:
React.AppRegistry.registerComponent(‘PropertyFinderApp’, function() { return PropertyFinderApp });
AppRegistry 定義了App的入口,並提供了根元件。
儲存 PropertyFinderApp.js,回到Xcode中。確保 PropertyFinder 規劃(scheme)已經勾選了,並設定了相應的 iPhone 模擬器,然後生成並執行你的專案。幾秒之後,你應該就可以看到 “Hello World” 應用正在運行了:
這個JavaScript應用執行在模擬器上,使用的是原生UI,沒有任何內嵌的瀏覽器哦!
還不相信這是真的?:] 那開啟你的 Xcode,選擇 Debug\View Debugging\Capture View Hierarchy,你看 native view hierarchy 中都沒有 UIWebView,就只有一個原生的view!:]
你一定很好奇其中的原理吧,那就在 Xcode 中開啟 AppDelegate.m,接著找到 application:didFinishLaunchingWithOptions:這個方法構建了 RCTRootView 用於載入 JavaScript 應用以及渲染最後的檢視的。
當應用開始執行的時候,RCTRootView將會從以下的URL中載入應用:
http://localhost:8081/Examples/PropertyFinder/PropertyFinderApp.includeRequire.runModule.bundle
重新呼叫了你在執行這個App時開啟的終端視窗,它開啟了一個 packager 和 server 來處理上面的請求。
在 Safari 中開啟那個 URL;你將會看到這個 App 的 JavaScript 程式碼。你也可以在 React Native 框架中找到你的 “Hello World” 程式碼。
當你的App開始運行了以後,這段程式碼將會被載入進來,然後 JavaScriptCore 框架將會執行它。在 Hello World 的例子裡,它將會載入 PropertyFinderApp 元件,然後構建出原生的 UIKit 檢視。關於這部分的內容,後文裡會再詳細解釋的。
你好 JSX 的世界
你當前的應用程式會使用 React.createElement 來構建應用 UI ,React會將其轉換到原生環境中。在當前情況下,你的JavaScript程式碼是完全可讀的,但一個更複雜的 UI 與巢狀的元素將迅速使程式碼變成一大坨。
確保應用程式仍在執行,然後回到你的文字編輯器中,編輯 PropertyFinderApp.js 。修改元件 render 方法的傳回陳述句如下:
return
Hello World (Again) ;
這是 JSX ,或 JavaScript 語法擴充套件,它直接在你的 JavaScript 程式碼中混合了類似 HTML 的語法;如果你是一個 web 開發人員,應該對此不陌生。在本篇文章中你將一直使用 JSX 。
把你的改動儲存到 PropertyFinderApp.js 中,並傳回到模擬器。按下 Cmd + R ,你將看到你的應用程式掃清,並顯示更新的訊息 “Hello World(again)”。
重新執行一個 React Native 應用程式像掃清 web 瀏覽器一樣簡單!:]
因為你會使用相同的一系列 JavaScript 檔案,您可以讓應用程式一直執行,只在更改和儲存 PropertyFinderApp.js 後掃清即可
註意:如果你感到好奇,可以看看你的“包”在瀏覽器中,JSX被轉換成什麼。
這個 “Hello World” 已經夠大家玩耍了,是時候構建實際的應用程式了!
新增導航
我們的房產查詢應用使用標準的棧式導航,基於 UIKit 的 navigation controller。現在正是新增的時候。
在 index.ios.js 檔案中,把 PropertyFinderApp 重新命名為HelloWorld:
class HelloWorld extends React.Component {
“Hello World” 這幾個字你還需要讓它顯示一會兒,但它不再是應用的根元件了。
接下來,在 HelloWorld 這個元件下麵新增如下這個類:
class PropertyFinderApp extends React.Component {
render() {
return (
style={styles.container}
initialRoute={{
title: ‘Property Finder’,
component: HelloWorld,
}}/>
);
}
}
構造一個 navigation controller,應用一個樣式,並把初始路由設為 Hello World 元件。在 Web 開發中,路由就是一種定義應用導航的一種技術,即定義頁面——或者說是路由——與 URL 的對應關係。
在同一個檔案中,更新樣式定義,包含如下 container 的樣式:
var styles = React.StyleSheet.create({
text: {
color: ‘black’,
backgroundColor: ‘white’,
fontSize: 30,
margin: 80
},
container: {
flex: 1
}
});
在隨後的教程中會告訴你 flex: 1 是什麼意思。
回到模擬器,Cmd+R,看看新 UI 的樣子:
這就是包含了 root view 的 navigation controller,目前 root view 就是 “Hello World”。很棒——應用已經有了基礎的導航結構,到新增真實 UI 的時候了。
建立搜尋頁
在專案中新增一個新檔案,命名為 SearchPage.js,然後將其放在PropertyFinderApp.js 所在目錄下。在檔案中新增下麵程式碼:
‘use strict’;
var React = require(‘react-native’);
var {
StyleSheet,
Text,
TextInput,
View,
TouchableHighlight,
ActivityIndicatorIOS,
Image,
Component
} = React;
你會註意到,位於引入 react-native 所在位置的前面有一個嚴格樣式標識,緊接著的宣告陳述句是新知識。
這是一種解構賦值,準許你獲取物件的多個屬性並且使用一條陳述句將它們賦給多個變數。結果是,後面的程式碼中可以省略掉 React 字首;比如,你可以直接取用 StyleSheet ,而不再需要 React.StyleSheet。解構同樣適用於運算元組,更多細節請戳這裡。
繼續在 SearchPage.js 檔案中新增下麵的樣式:
var styles = StyleSheet.create({
description: {
marginBottom: 20,
fontSize: 18,
textAlign: ‘center’,
color: ‘#656565’
},
container: {
padding: 30,
marginTop: 65,
alignItems: ‘center’
}
});
同樣,以上都是標準的 CSS 屬性。和 Interface Builder 相比,這樣設定樣式缺少了視覺化,但是比起在 viewDidLoad() 中逐個設定檢視屬性的做法更友好!
只需要把元件新增到樣式宣告的前面:
class SearchPage extends Component {
render() {
return (
Search for houses to buy!
Search by place-name, postcode or search near your location.
);
}
}
render 很好地展示出 JSX 以及它表示的結構。透過這個樣式,你可以輕易地描繪出元件 UI 的結構:一個容器,包含兩個 text 標簽。
最後,將下麵的程式碼新增到檔案末尾:
module.exports = SearchPage;
這可以 export SearchPage 類,方便在其他檔案中使用它。
下一步是更新應用的路由,以初始化路由。
開啟 PropertyFinderApp.js,在檔案頂部緊接著上一個 require 陳述句的位置新增下麵程式碼:
var SearchPage = require(‘./SearchPage’);
在 PropertyFinderApp 類的 render 函式內部,透過更新 initialRoute 來取用最新新增的頁面,如下:
component: SearchPage
此時,如果你願意則可以移除 HelloWorld 類以及與它相關聯的樣式。你不在需要那段程式碼了。
切換到模擬器,按下 Cmd+R 檢視新的 UI:
使用 Flexbox 定義外觀
現在,你已經看到了用基本的 CSS 屬性來控制外間距(margin),內間距(padding)還有顏色(color)。不過,可能你還不太瞭解要如何使用伸縮盒(flexbox),flexbox 是最近新加入 CSS 規範,用它就能很便利地佈局介面。
React Native 用 css-layout(這是一個用 JavaScript 實現flexbox標準然後編譯成 C(iOS平臺)或者Java(Android平臺)的庫)。
Facebook把這個專案單獨出來實在太正確了,這樣可以編譯成多種語言,促進更多新穎的應用的發展,比如flexbox layout to SVG。
在你的App中,容器(container)預設地是縱向佈局,也就是說在它的子元素將會豎直地排列,像這樣:
這被稱為主軸 (main axis),它的方向可以是豎直的也可以是水平的。
每一個子元素在豎直方向上的位置是由它的margin,height和padding共同決定的。容器的 alignItems 屬性也要設定成 center,這個屬性可以控制子元素在十字軸上的位置。在這裡,它實現了居中對齊的文字。
好啦,現在我們把輸入框和按鈕加上去吧。開啟 SearchPage.js,將下麵的程式碼插入第二個 Text 元素的後面:
style={styles.searchInput}
placeholder=’Search via name or postcode’/>
underlayColor=’#99d9f4′>
Go
underlayColor=’#99d9f4′>
現在你已經加上了兩個最高等級的檢視(top-level view),一個檢視包含了文字輸入框和一個按鈕,還有一個檢視內只有一個按鈕。在後文中你會看到,它們的樣式是什麼樣的。
接著,新增上對應的樣式:
flowRight: {
flexDirection: ‘row’,
alignItems: ‘center’,
alignSelf: ‘stretch’
},
buttonText: {
fontSize: 18,
color: ‘white’,
alignSelf: ‘center’
},
button: {
height: 36,
flex: 1,
flexDirection: ‘row’,
backgroundColor: ‘#48BBEC’,
borderColor: ‘#48BBEC’,
borderWidth: 1,
borderRadius: 8,
marginBottom: 10,
alignSelf: ‘stretch’,
justifyContent: ‘center’
},
searchInput: {
height: 36,
padding: 4,
marginRight: 5,
flex: 4,
fontSize: 18,
borderWidth: 1,
borderColor: ‘#48BBEC’,
borderRadius: 8,
color: ‘#48BBEC’
}
要註意格式問題:每一個樣式都是用逗號分隔開的,所以別忘了在container 選擇器後面還要加上一個逗號。
以上的樣式將會應用在你剛剛加上的輸入框和按鈕上。
現在傳回到模擬器,然後按下 Cmd+R 掃清介面:
文字區域和 ’Go’ 按鈕在同一行,不需要顯式地定義兩個元件的寬度,你只需要將它們放在同一個容器中,加上 flexDirection:’row’ 樣式,再定義好它們的 flex 值。文字區域是 flex:4,按鈕則是 flex:1,這說明兩者的寬度比是4:1。
大概你也發現了,你的“按鈕”其實並不是按鈕!:] 使用了 UIKit 後,按鈕更傾向於是可以輕碰(tap)的標簽(label),所以 React Native 團隊決定直接在 JavaScript 中構建按鈕了。所以你在 App 中使用的按鈕是 TouchableHighlight,這是一個 React Native 元件,當輕碰 TouchableHighlight 時,它會變得透明從而顯示出襯底的顏色(也就是按鈕下層的元件顏色)。
搜尋介面的最後一步就是加上一張圖片.你可以從這裡下載我們用的圖片素材並解壓。
在Xcode中開啟Images.xcassets檔案,點選加號新增一個新的圖片集。然後將圖片素材拖進正確的“區間”:
你需要重啟應用才能讓圖片生效。
將以下程式碼新增到 TouchableHighlight 元件後面,它將用於“獲取位置”按鈕:
現在再樣式表的最後加上圖片對應的樣式,別忘了給原樣式中最後一個加上逗號哦:
image: {
width: 217,
height: 138
}
require(‘image!house’) 陳述句用於確定在你應用的asset目錄下的圖片資源,在 Xcode 中,如果你的開啟了 Images.xcassets,你會看到一個“房屋”的圖示,正是上面程式碼中取用到的。
傳回到模擬器,Cmd+R掃清UI:
註意:如果你這會沒有看到“房屋”圖片,取而代之的是一張“找不到資源”的圖片,嘗試重啟packager(也就是在終端裡輸入 npm start 命令)。
現在你的應用看起來挺不錯的啦,不過它還少了點功能。接下來你的任務就是給它加上點狀態,讓它執行一些操作。
原文出處:Colin Eberhardt
譯文出處:前端外刊評論
連結:http://zhuanlan.zhihu.com/FrontendMagazine/19996445