react-native入門
react native的強大牛逼之處就不扯了,直接進入主題,談談自己初次接觸react native的學習經驗,寫一個簡單的入門文檔,便於總結學習知識,分享學習中遇到和解決的坑,由於我是做android開發的,因此該文主要針對android開發環境進行講解,基於win7系統。
重點參考以下網址和書籍
- React Native中文網
http://reactnative.cn/docs/0.24/getting-started.html#content- ECMAScript 6 入門
http://es6.ruanyifeng.com/#docs/destructuring- 深入淺出 React Native:使用 JavaScript 構建原生應用
https://zhuanlan.zhihu.com/p/19996445- JavaScript高級程序設計(第三版)
1. 環境搭建
搭建過程參考“React Native中文網”上的講解,這里詳細說明一下android環境搭架過程。考慮到作為一名android開發人員,androidstudio以及對應的sdk等android開發環境都應該會搭架(或者自己百度android開發環境搭建),默認android環境處於搭建好的狀態。
1.1.三步搞定
- step1 安裝Node.js
訪問node.js官網:https://nodejs.org/en/,下載最新的穩定版本,這里我下載的是v4.4.4LTS,下載下來后雙擊安裝即可。 - step2 安裝react-native
如果node.js已經安裝好,以及對應的環境變量配置沒有問題,直接運行該語句即可:npm install -g react-native-cli - step3 生成react-native開發工程
react-native init AwesomeProject備注:AwesomeProject為你的工程名,自己隨意寫。
1.2.注意事項
- 由於眾所周知的網絡原因,react-native命令行從npm官方源拖代碼時會遇上麻煩。請將npm倉庫源替換為國內鏡像,否則很有可能step3不能成功,或者耗時過長:
npm config set registry https://registry.npm.taobao.org
npm config set disturl https://npm.taobao.org/distreact-native init一個工程的時間本來就比較長,請耐性等待(不要過早認為失敗了),我在win7上init大概需要4分鍾左右。
1.3.結束
是的,如果你本身就使用androidstudio進行android應用開發,這只需要這幾步,就完成了react native的開發環境搭建,當然,考慮到斷點調試,你還需要安裝chome瀏覽器以及對應的插件:
- React Developer Tools
https://chrome.google.com/webstore/search/react?hl=en
2.開發走起
好吧,雖然我們環境已經搭建完成,但是選用一個合適的編輯器還是很重要的,不然敲着代碼都不順心,這里我使用的webstorm,並使用對應的提示插件:https://github.com/virtoolswebplayer/ReactNative-LiveTemplate
2.1.萬事俱備,開始敲代碼
寫一個helloworld實在太簡單,只需要改改之前生成的工程的字符串就搞定了,因此就不寫helloworld了,直接參考《深入淺出 React Native:使用 JavaScript 構建原生應用》這篇文章開始實踐。由於在實踐過程中僅僅只看了這篇文章,沒有看它的源碼,所以覺得自己好坑,讀者遇到問題時,可以嘗試看源碼解決。
- index,android.js
import React, {
AppRegistry,
Component,
Navigator
} from 'react-native';
import SearchPage from './js/SearchPage';
class PropertyFinder extends Component {
render() {
return (
<Navigator
initialRoute={{title: 'SearchPage',component: SearchPage}}
configureScene={(route) => {
return Navigator.SceneConfigs.VerticalDownSwipeJump;
}}
renderScene={(route, navigator) => {
let SearchPage = route.component;
return <SearchPage {...route.params} navigator={navigator} />;
}}/>
);
}
}
AppRegistry.registerComponent('PropertyFinder', () => PropertyFinder);
這里直接使用了es6的最新語法進行編寫,以前沒有怎么寫js代碼,不過感覺這樣的語法結構更容易適應,爽歪歪。
- 第1行
import語句后的React導入的react-native模塊中的默認導出內容(export default xxx)。這里特別結實一下,剛開始沒看懂這個語法。 - 第10行
如其名字render意思,該函數為該組件的渲染函數,渲染整個組件的顯示。 - 第25行
組成入口組件,注意函數的第一個參數名字不能隨便修改,它與android原生代碼中的getMainComponentName保持一致,第二個參數名嘛,只要是一個Component就行了,名字無所謂。
2.2.重點解釋Navigator
Navigator與android系統中的activity棧的作用類似。
- initialRoute
它定義了Navigator的初始化組件,該組件將在應用啟動時加載。 - configureScene
配置界面與界面之間跳轉時的動畫效果 - renderScene
渲染對應的組件
2.3.坑點
第19行的 {…route.params},到目前為止,我還不怎么了解它的原理,僅僅是了解它的作用(ps:剛學習,對jsx理解不深刻),這段代碼定義了一個組件跳轉到另一個組件時,傳遞對象數據的名稱,后續所有的界面跳轉都需要和改名字保持一致,本文使用了params,那么再進行跳轉時,傳遞的參數名必須為params,例如:
this.props.navigator.push({
title: 'Results',
component: SearchResults,
params: {listings: response.listings}
});
3.布局
react-native的布局與html與css的思想保持一致,例如:html5使用html標簽描述文檔結構,使用css對文檔表現進行渲染,而react-native使用自己的組件進行界面結構描述,使用類似於css的語法進行界面表示描述。
/**
* Created by zjh on 2016/5/5.
*/
import React, {
StyleSheet,
Text,
View,
TextInput,
TouchableHighlight,
Image,
ProgressBarAndroid,
Component
} from 'react-native';
import SearchResults from './SearchResults';
class SearchPage extends Component {
// 構造
constructor(props) {
super(props);
// 初始狀態
this.state = {
searchString: 'london',
isLoading: false,
message: ''
};
}
render() {
console.log('SearchPage.render');
var spinner = this.state.isLoading ? (<ProgressBarAndroid styleAttr="Inverse" />) : (<View/>);
return (
<View style={styles.container}>
<Text style={styles.decription}>Search for houses to buy!</Text>
<Text style={styles.decription}>Search by place-name, postcode or search near your location.</Text>
<View style={styles.flowRight}>
<TextInput
style={styles.searchInput}
value={this.state.searchString}
onChangeText={this.onSearchTextChanged.bind(this)}
placeholder='Search via name or postcode'/>
<TouchableHighlight
onPress={this.onSearchPressed.bind(this)}
style={styles.button}>
<Text style={styles.buttonText}>Go</Text>
</TouchableHighlight>
</View>
<TouchableHighlight style={styles.button} onPress={this.onLocationPressed.bind(this)} >
<Text style={styles.buttonText}>Location</Text>
</TouchableHighlight>
<Image source={require('./image/house.png')} />
{spinner}
<Text style={styles.decription}>{this.state.message}</Text>
</View>
);
}
onSearchTextChanged(text) {
console.log('onSearchTextChanged');
this.setState({searchString: text});
console.log(this.state.searchString);
}
_executeQuery(query) {
console.log(query);
this.setState({isLoading: true});
fetch(query)
.then(response => response.json())
.then(json => this._handleResponse(json.response))
.catch(error => this.setState({
isLoading: false,
message: 'Something bad happened: ' + error
}));
}
_handleResponse(response) {
this.setState({isLoading: false, message: ''});
if (response.application_response_code.substr(0, 1) === '1') {
//console.log('Properties found: ' + response.listings.length);
this.props.navigator.push({
title: 'Results',
component: SearchResults,
params: {listings: response.listings}
});
} else {
this.setState({message: 'Location not recognized; please try again.'});
}
}
onSearchPressed() {
console.log('onSearchPressed');
var query = urlForQueryAndPage('place_name', this.state.searchString, 1);
this._executeQuery(query);
}
onLocationPressed() {
navigator.geolocation.getCurrentPosition(
location => {
var search = location.coords.latitude + ',' + location.coords.longitude;
this.setState({searchString: search});
var query = urlForQueryAndPage('centre_point', search, 1);
this._executeQuery(query);
},
error => {
this.setState({
message: 'There was a problem with obtaining your location: ' + error
});
}
);
}
}
function urlForQueryAndPage(key, value, pageNumber) {
var data = {
country: 'uk',
pretty: '1',
encoding: 'json',
listing_type: 'buy',
action: 'search_listings',
page: pageNumber
};
data[key] = value;
var queryString = Object.keys(data)
.map(key => key + '=' + encodeURIComponent(data[key]))
.join('&');
return 'http://api.nestoria.co.uk/api?' + queryString;
}
var styles = StyleSheet.create({
decription: {
marginBottom: 20,
fontSize: 18,
textAlign: 'center',
color: '#656565'
},
container: {
padding: 30,
marginTop: 65,
alignItems: 'center'
},
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'
},
image: {
width: 217,
height: 138
}
});
export default SearchPage;
3.1.常用布局組件與渲染熟悉
react-native提供了大量的組件,例如VIew、Text、Image等等,詳細的使用方法,請參考《React Native中文網》中的內容。
3.2.坑點
雖然這里的界面渲染語法與css保持一致,比較react-native不是運行在瀏覽器上,因此它支持的也僅僅是一個子集而已。
- flex
該屬性,在進行多設備界面適配時,會使用得很多,我遇到一個坑,沒有設置父組件為alignSelf: ‘stretch’,導致子組件雖然設置了alignSelf: ‘stretch’和對應的flex值,但是卻沒有按照比例正常顯示的情況。
4.全文結束
是的,在這里就結束了,掌握的知識不多,入門確實也就是這么簡單。