React-Native牛刀小試仿京東砍啊砍砍到你手軟
React-Native基礎教程
*Demo
幾個月前facebook推出了React Native框架,允許開發着使用javascript代碼來實現iOS原生的應用,隨后十月份安卓版的也相繼問世,從此我們可以優雅的Learn once, write anywhere…
早在幾年前開發者就開始使用javascript+html和PhoneGap來編寫各式各樣的app了,開發者可以優雅的完成一套js的shell,然后分別在不同的平台下進行打包,最終生成不同平台的app,知識app的最終的展現形式都是html類型的。一度曾經出現webapp 是否要取代native ,這么多年過去,結果大家也不言而知了。
但是react native的確是一個很了不起的東西,開發者們都不禁為之歡呼,react native所展現出來的應用實質上是native應用,開發者完成同一套js代碼,分別在iOS和安卓平台下分別打包最終分別能映射生成分屬不同的安卓原生應用與iOS原生應用,這個優勢可能是目前為止被廣大開發最為喜歡的地方,一直以來web app最為大家所詬病的可能就是html的頁面永遠無法與原生頁面的體驗相比擬。
-
通過react native框架,你可以用JavaScript來編寫和運行應用程序邏輯,而UI卻可以是真正的本地代碼編寫的,因此,你完全不需要一個HTML5編寫的UI。
-
React框架采用了一種新穎的、激進的和高度函數式的方式來構建UI。簡單說,應用程序的UI可以簡單地用一個函數來表示應用程序當前的狀態
React Native的重點是把React編程模型引進到移動App的開發中去。它的目的並不是跨平台,一次編寫到處運行。它真正的目標是“一次學習多處編寫”。這是一個重大的區別。本教程只涉及iOS,但一旦你學會了它的思想,你就可以快速將同樣的知識應用到Android App的編寫上。
React Native的編寫模式更加友好於從事於js的前端開發者,它本身采用了React js的模式,尤其是從事React js的開發人員,只需要熟悉下基本的文檔就能瞬間變成一個iOS+安卓雙向通吃的移動專家,React內部引入可一些新的概念,如 DOM和reconciliation,React直接將函數式編程的理念用到了UI層面。
不過相對來說,OC的開發人員只要熟悉一下基本demo看上幾個例子應該就不會有太多問題了,如果之前有過web端開發經驗的話相信上手會更快一些。
下面介紹一個簡單的demo操作,這個教程一起帶你去體驗一下京東促銷砍啊砍頁面的OC->React 移植過程,通過本教程你就可以了解React Native的一些基本開發流程了。
效果:

如果你之前從未寫過任何 JavaScript ,別擔心;這篇教程帶着你一點一點編寫代碼。React 使用 CSS 屬性來定義樣式,這些樣式通常都很易於閱讀和理解,但是如果你想進一步了解,可以參考:。
要想學習更多內容,請往下看
開始
React native 關於環境搭建問題此處就不多說了,詳情請見React native基礎教程,此處就從我們已經准備好一切前序工作開始,萬事具備只欠東風,下面開始:
首先React Native 啟動畫面開始,創建helloworld工程,啟動畫面如下:
與此同時Xcode還會打開一個終端窗口,並顯示如下信息:
這是React Navtive Packager,它在node容器中運行。你待會就會發現它的用處。
千萬不要關閉這個窗口,讓它一直運行在后面。如果你意外關閉它,可以在Xcode中先停止程序,再重新運行程序。
注意:
- 在開始接觸具體的代碼之前(在本教程中,主要是js代碼),我們將推薦 Sublime
- Text這個文本編輯工具,因為Xcode並不適合用於編寫js代碼的。當然,你也可以使用 atom, brackets
- 等其他輕量級的工具來替代。
React Native完成的js完成的代碼其實是跑在本地的node下面的,從appdelegate里面可以看到React Native工程會從一個本機地址“http://localhost:8081/index.ios.bundle?platform=ios&dev=true”讀取一個對應的文件,這個文件中就是系統已經自動幫你打包壓縮整合過以后的一個js 代碼庫,接下來React Native引擎會將這個庫中的js代碼完全的解析、翻譯成對應的iOS原生內容,最終以iOS原生UI的形式渲染到桌面上,這個就是React Native整個工作流程。
你好, React Native
在開始編寫這個demo之前我們先創建一個簡單的Hello World項目,用你喜歡的文本編輯器(例如Sublime Text)打開index.ios.js ,刪除所有內容。然后加入以下語句:
'use strict'; var React = require('react-native'); var { AppRegistry, Text, View, } = React; var HelloWorld = React.createClass({ render: function() { return ( <View> <View><Text>你好, React Native</Text></View> </View> ); } }); AppRegistry.registerComponent('HelloWorld', () => HelloWorld);
好了,“Hello World” 的演示就到此為止;接下來我們要編寫一個真正的React App了!
創建一個導航
這個demo使用了標准的UIKit中的導航控制器來提供”棧式導航體驗“。接下來我們就來實現這個功能。
在 index.ios.js, 添加以下代碼:
var Home = require('./cut/Home'); var HelloWorld = React.createClass(//{ render: function() //{ return ( <NavigatorIOS initialRoute=//{//{title:'首頁', component:Home, //}//}//> ); //} //});
NavigatorIOS就是React Native中對應的導航視圖,我們再次暫時可以理解就是iOS中的UINavigationController,我們在此處創建了一個基於導航的視圖控制器,rootViewController對應的頁面就是Home。
創建rootView Home,添加Home.js 文件,添加代碼如下:
var cutList = require('./CutList'); var Home = React.createClass({ render:function (){ return ( <TouchableHighlight onPress={()=> this.goToNext()}> <View> <Text}>go to cut</Text> </View> </TouchableHighlight> ); }, goToNext:function(){ this.props.navigator.push({ component: cutList, }); }, });
Home 我們只放了一個按鈕,按鈕文字“go to cut”,另外添加了一個點擊觸摸事件,事件相應題是goToNext:function(); 在函數處理事件內部,我們只做了頁面的push跳轉,目標頁面是cutList頁面,運行效果如下:
構建砍啊砍List頁面,從網絡獲取數據,繪制table綁定事件
構造頂部bunner動畫圖
輪播圖這個地方采用了React Native的一個第三方庫swiper(偷懶了),
var Swiper = require('react-native-swiper'); 初始化數據 var sliderImgs = [ 'http://m.360buyimg.com/mobile/s725x175_jfs/t2332/80/701506039/111191/37a1273/5624850bN2469d61f.jpg', 'http://m.360buyimg.com/mobile/s725x175_jfs/t2401/354/694665708/117887/3a283185/56248ee2N58518e76.jpg', 'http://m.360buyimg.com/mobile/s725x175_jfs/t2506/269/651438394/152836/cf430d42/561f6b3aN80cb83f4.jpg', 'http://m.360buyimg.com/mobilecms/s750x410_jfs/t2326/263/687562306/170970/c3f92c7/5620cbddNaa6a2cda.jpg!q70.jpg', 'http://m.360buyimg.com/mobilecms/s750x410_jfs/t1891/237/637408747/193879/1acee0f7/5620be19N801621e4.jpg!q70.jpg' ]; //初始化UI render:function () \\{ return ( <View> <View> <Swiper style=\\{styles.wrapper\\} showsButtons=\\{false\\} autoplay=\\{true\\} height=\\{150\\} showsPagination={true}> <Image style={[styles.slide,]} source=></Image> <Image style={[styles.slide,]} source=></Image> <Image style={[styles.slide,]} source=></Image> <Image style={[styles.slide,]} source=></Image> <Image style={[styles.slide,]} source=></Image> </Swiper> </View> <View style={styles.listViewSuper}> <ListView style={styles.tableStyle} dataSource = {this.state.dataSource} renderRow={this._renderRow.bind(this)} pageSize={5} automaticallyAdjustContentInsets={false}//> </View> </View> ); },
再次看到render:function()這個函數,應該沒那么陌生了吧,暫時可以理解render相當於ViewController中的ViewDidLoad:,我們一般在render里面做一些初始化UI視圖的工作,此處我們初始化了swiper和ListView
swiper
- showsButtons (bool) 是否顯示左右切換按鈕(顯示兩個按鈕左切 友切)
- autoplay (bool) 是否開啟自動播放
- height (bool) 高度(不解釋)
- showsPagination (bool) 是否顯示pageControl
ListView
- dataSource 綁定數據源
- renderRow cell綁定函數事件(等價於tableView:cellForRowAtIndexPath:)
- automaticallyAdjustContentInsets UI布局相關的
初始化數據源
getInitialState:function(){ var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); return { dataSource: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }), loaded:false, currentPage:0, }; }
getInitialState:function()類似於OC中的init函數,我們一般的習慣喜歡在init函數初始化一個變量等數據,在React Native依舊是這樣。
發起網絡請求,獲取數據,緩存在全局變量List中
//定義request url var urlPath = 'http://ccguo.gitcafe.io/cut.json'; var CACHE = []; //componentDidMount:function 系統方法 componentDidMount:function(){ this.fetchData(); }, //自定義函數處理網絡獲取數據,將數據放入全局變量CACHE cache:function(items){ for (var i in items) { CACHE.push(items[i]); } this.setState({ dataSource: this.state.dataSource.cloneWithRows(CACHE), }); }, //發起 網絡請求,獲得json fetchData:function(){ console.log('hello world'); fetch(urlPath) .then((response) => response.json()) .then((responseText) => { console.log(responseText.cutList); this.cache(responseText.cutList); }) .catch((error) => { console.log(error); }); }
這個過程模擬了在iOS原生應用里面,初始化網絡request,發起網絡請求,得到數據,解析數據,然后將數據存入list這一些列操作,其實在js中,js腳本處理json的能力還是很強的,我們再也不需要像OC中哪些objectForKey:的操作了,我們不需要任何MJExtension、JSONModel、 Mantle等一些潛在的工具了,省去了很多的麻煩,我們直接拿到一個json對象,直接對對象進行操作。
另外React的網絡請求此處我們只是使用了fetch API
臉譜官方的api(臉譜對於網絡請求提供了多種API,如:fetch WebSocket XMLHttpRequest等,具體可參照API)
從代碼上看js的鏈式編程剛看上去有點不太習慣,不過整體使用起來還是比OC中快捷多了,foreach遍歷、消息隊列進出棧,總之腳步里面省去了以往還不得不在意的好多麻煩,其實這塊相對swift而言,新的版本中漸漸的已經得到了部分提升,不過還是要感謝臉譜團隊,沒有他們,可能還見識不到React的強大。
構建cell
_renderRow:function(data,sectionID,rowID){ return ( <TouchableHighlight onPress={() => this._pressRow(data,rowID)}> <View style=> <View style=> <View style=> <Image style= source=></Image> </View> <View style={styles.row,{flex:3,borderColor:'blue',borderWidth:0.5}}> <Text style=> {rowID+'-'+data.wname} </Text> <Text style=>京東價318.00</Text> <View style=> <Text style=>已有256人砍價</Text> <Text style=>馬上砍</Text> </View> </View> </View> <View style={styles.separator} /> </View> </TouchableHighlight> ); }, _pressRow: function(data,rowID) { this.props.navigator.push({ component: detail, passProps: {data: data} }); }
在上述初始化ListView UI的時間,我們指定了renderRow 對應的action事件,此處我們可以直接在_renderRow:function中構建自己的cell模版,至於React Native中UI的標簽基本用法,大家可以去頭部基礎教程里面找,有點類似於html標簽,總之我們在_renderRow:function純碎是構造cell的代碼,這個類似於tableViewCell subClass, cell點擊事件我們使用一個TouchableHighlight來代替
<TouchableHighlight onPress={() => this._pressRow(data,rowID)}> .... </TouchableHighlight>
TouchableHighlight事件處理action同樣是一個函數(不解釋),在_pressRow事件中我們處理自己的cell點擊跳轉,順便插一句下一步的操作,_pressRow(data,rowID)是帶有形參的
另外 ListView renderRow 事件的重載函數,形參類型這個具體參照臉譜官方的api
_renderRow:function(data,sectionID,rowID)。
整體運行效果如下:
處理cell跳轉事件
var detail = require('./Detail'); _pressRow: function(data,rowID) { this.props.navigator.push({ component: detail, passProps: {data: data} }); }
React在處理事件跳轉的時間,仍舊采用進棧出棧的形式,這一點和Apple的理念還是類似的。
- component: 參數對應將要跳轉的目標頁面,
- passProps: 傳參字典,內部為key-val形式,相當於一個容器,到了目標頁面后可以根據key從容器中取出傳遞的值。
獲取頁面跳轉時間容器中的值
<Text style={styles.view}>{this.props.data.wname}</Text>
到了目標頁面后,我們直接從props容器直接根據key就能將傳遞的參數去處,此處我們傳遞參數的本身是一個json,我們只是講wname顯示到detail頁面。
效果如下:
接下來做什么
恭喜你,你的第一個React Native App終於完成了!你可以在GitHub上找到每一個”可運行的“步驟的項目源文件,如果你搞不定的時候它們會非常有用的 :]
如果你來自Web領域,你可能覺得在代碼中用JS和React框架建立基於本地化UI的App的界面並實現導航不過是小菜一碟。但如果你主要開發的是本地App,我希望你能從中感受到React Native的優點:快速的App迭代,現代JavaScript語法的支持和清晰的CSS樣式規則。
在你的下一個App中,你是會使用這個框架,還是會繼續頑固不化地使用Swift和O-C呢?
無論你怎么選擇,我都希望你能從本文的介紹中學習到一些有趣的新東西,並把其中一些原理應用到你的下一個項目中。
如果你有任何問題及建議,請參與到下面的討論中來!