前言
第一回合 我们初步了解到了RN的环境和语法,然后我们继续往下看
继第一个项目demo往下延伸新的代码,今天要做一个城市列表
城市列表
拆分目录
这里,我们做一个城市列表,真实的访问接口获取数据,然后渲染页面,看看做出来效果如何。
首先,我们初始化一个RN项目:
react-native init Citylist
然后使用Xcode打开iOS中的项目,编译运行:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View } from 'react-native';
export default class Citylist extends Component {
render() {
return(
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions} >
To get started, edit index.ios.js
</Text>
<Text style={styles.instructions} >
Press Cmd+R to reload,{'\n'} Cmd+D or shake for dev menu
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('Citylist', () => Citylist);
这里除了index.io.js,其他文件我们不必理睬,我们做的第一件事情是,将样式文件剥离出去,新建static文件夹,加入images和style,将样式文件移入style文件,新建style.js:
import {
StyleSheet
} from 'react-native';
export let styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
然后首页代码再做一些改动,在import后边插入本行代码:
import {styles} from './static/style/style';
PS:这里有一个箭头函数
() => Citylist
//===>
function () {
return Citylist;
}
静态资源剥离后,我们先不处理其它的,我们来做数据请求。
数据请求
RN虽然内置了ajax库,但是一般推荐使用RN自带的Fetch,最简单的使用是:
fetch('https://mywebsite.com/mydata.json')
PS:我们在学习RN的时候,也是在学习神马方式是适合的,或者说熟悉使用合适的组件
请求一个接口是这样写的(使用promise):
fetch('https://apikuai.baidu.com/city/getstartcitys')
.then((response) => response.json())
.then((jsonData) => { console.log(jsonData);
})
.catch((e) => {
console.log(e)
})
这里打开调试环境一看,输出了我们要的数据:
一般来说,我们需要对数据请求应该封装为一个底层库,这里只做一些简单改造,真实项目不会这样做:
export default class Citylist extends Component {
getdata(url, suc, err) {
return fetch(url)
.then((response) => response.json())
.then((data) => {
if(data.errno == 0) {
suc && suc(data.data)
}
})
.catch((e) => {
console.log(e)
});
}
render() {
this.getdata('https://apikuai.baidu.com/city/getstartcitys', function(data) {
s = ''
});
PS:这里的使用不一定正确,先完成功能再改进吧
我们取所有的城市cities,这个数据量很大,有1000多条记录,也可以测试下拖动效率了,这里为类加入构造函数,因为列表是可变的,暂时把列表数据归为state(react也不是太熟,如果有问题后续优化,先完成功能):
constructor(props) {
super(props);
this.state = {
cities: []
};
}
var scope = this;
this.getdata('https://apikuai.baidu.com/city/getstartcitys', function(data) {
scope.state.citys = data.cities;
});
列表渲染
处理了数据问题后,我们开始做列表渲染,这里使用ListView组件,这个组件用以显示一个垂直滚动列表,适合长列表,两个必须的属性是datasource和renderRow:
dataSource:列表数据源
renderRow:逐个解析数据源中的数据,然后返回一个设定好的格式来渲染
简单书写代码:
export default class Citylist extends Component {
constructor(props) {
super(props);
this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
cities: this.ds.cloneWithRows([
{cnname : "文山壮族苗族自治州",
enname : "wszzmzzzz",
extflag : "1",
flag : "0",
name : "wenshanzhuangzumiaozuzizhizhou",
parentid : "28",
regionid : "177",
shortname : "文山",
shownname : "文山",
type : "2"
},{
cnname : "文山壮族苗族自治州",
enname : "wszzmzzzz",
extflag : "1",
flag : "0",
name : "wenshanzhuangzumiaozuzizhizhou",
parentid : "28",
regionid : "177",
shortname : "文山",
shownname : "文山",
type : "2"
},{
cnname : "文山壮族苗族自治州",
enname : "wszzmzzzz",
extflag : "1",
flag : "0",
name : "wenshanzhuangzumiaozuzizhizhou",
parentid : "28",
regionid : "177",
shortname : "文山",
shownname : "文山",
type : "2"} ]
)
};
}
getdata(url, suc, err) {
return fetch(url)
.then((response) => response.json())
.then((data) => {
if(data.errno == 0) {
suc && suc(data.data)
}
})
.catch((e) => { console.log(e) }); }
componentDidMount(){
var scope = this;
this.getdata('https://apikuai.baidu.com/city/getstartcitys', function(data) {
console.log(data)
scope.setState({ cities: scope.ds.cloneWithRows(data.cities) });
//scope.state.citys = data.cities;
//this.getdata('https://apikuai.baidu.com/city/getstartcitys', (data) => {
// this.state.citys = data.cities;
//});
});
}
render() {
return (
<View style={styles.container}>
<LisrView
dataSource={this.state.cities}
renderRow={(rowData) =><Text>{rowData.cnname}</Text>
/>
</View>
); }}
然后就这样了,虽然丑是丑点,但是还能看嘛,这里我们先不去理睬城市的排序,也不做搜索功能,我们先把布局处理下,他的丑陋我已经受不了了
样式处理
现在我们开始处理这段样式:
import {
StyleSheet
} from 'react-native';
export let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
listView: {
marginTop: 30,
flex: 1,
borderBottomColor:'#CCCCCC',
//cell的分割线
borderBottomWidth:1
},
listItem: {
paddingTop: 15,
paddingBottom: 15,
paddingLeft: 10,
flexDirection:'row',
borderBottomColor:'#CCCCCC',
//cell的分割线
borderBottomWidth:1
} });
事件绑定
然后,我们再为每行数据加上点击事件,这里也做简单一点,打印出当前行的值即可:
onPressAction(data){
alert(data.cnname)
}
render() {
return (
<View style={styles.container}>
<ListView style={styles.listView} enableEmptySections={true}
dataSource={this.state.cities}
renderRow={(rowData) =>
<View style={styles.listItem}>
<Text onPress={() => this.onPressAction(rowData)}>{rowData.cnname} </Text>
</View>
}
/>
</View>
); }
PS:我尼玛,这个RN的学习,很大程度就是一个个API或者组件的熟悉,这块不熟悉的话,做起来恼火的很
我这里开始想给Text设置边框,怎么都不能成功,后面就加了一层View就好了,这种小细节需要多摸索,这个是最终的结构:
结语
作为一个demo的话,这个例子基本可以说明一些问题的,虽然我本意是想做成这个样子的:)
通过这个例子,我们简单的学习了下RN的开发模式,做出来的感受是Facebook很强大,做了一个体系性的东西,举个例子来说(个人感受)
之前我们做Hybrid的时候Header是Native提供的,大概做法是这样的:
//Native以及前端框架会对特殊tagname的标识做默认回调,如果未注册callback,或者点击回调callback无返回则执行默认方法
//back前端默认执行History.back,如果不可后退则回到指定URL,Native如果检测到不可后退则返回Naive大首页
//home前端默认返回指定URL,Native默认返回大首页
this.header.set({
left: [ {
//如果出现value字段,则默认不使用icon
tagname: 'back', value: '回退',
//如果设置了lefticon或者righticon,则显示icon
//native会提供常用图标icon映射,如果找不到,便会去当前业务频道专用目录获取图标
lefticon: 'back',
callback: function () { }
}
],
right: [ {
//默认icon为tagname,这里为icon
tagname: 'search',
callback: function () { }
},
//自定义图标
{ tagname: 'me',
//会去hotel频道存储静态header图标资源目录搜寻该图标,没有便使用默认图标
icon: 'hotel/me.png',
callback: function () { }
}
],
title: 'title',
//显示主标题,子标题的场景
title: ['title', 'subtitle'],
//定制化title
title: {
value: 'title',
//标题右边图标
righticon: 'down',
//也可以设置lefticon
//标题类型,默认为空,设置的话需要特殊处理
//type: 'tabs',
//点击标题时的回调,默认为空
callback: function () { } } });
通过这个约定,我们的Native就会生成一系列headerUI:
而RN做了什么呢,他可能是实现了一个这样的标签(或者说是语法):
< Header title="" right="[]" ></ Header>
然后RN会自己去解析这个标签,生成上述的对象,然后生成Native的UI,这个我们其实也能做到,但是我们一个能做到,10个就不一定做得到了,RN牛的地方就牛在他提供了这么大一坨东西:
然后还有他一整套的样式体系,非常之大手笔,而通过RN的完善约定,生成了一套NativeUI,应该说来体验是非常高的,开发效率因为可以做到大部分iOS Android通用,虽然整体开发效率无法与Hybrid比肩,但绝对有其应用场景。
我们也有一些同事说了一些RN的问题,但是框架在发展,容器在优化,这些问题在某个时间点应该能解决的,总的说来,RN还是很有学习的价值,后面我可能会花很多功夫去进行落地!!!