接上集~
初始化项目后进入目录,里面的结构大概是这样的(忽略隐藏文件):
- android(directory)
- ios(directory)
- index.android.js
- index.ios.js
- package.json
Android和ios俩文件夹分别是对应系统的配置文件,深入的分析将在下次分享(其实是还没有研究);android.index.js和index.ios.js分别是android和ios应用的入口;package.json是项目的依赖配置文件,新的add-ons都需要写进这个文件中。
整个项目的js代码是采用reactjs的架构,不熟悉的童鞋可以先go through一下reactjs的官网。 简单地说就是把app与数据分离并且组件化,使组件可复用。我用RN写了一个android的demo,github地址是:这里,下面我就以demo为例分析一下其基本结构。
index.android.js
'use strict';
var React = require('react-native');
var AppMain = require('./components/nav');
var {
AppRegistry,
ListView,
StyleSheet,
Text,
View,
} = React;
var App = React.createClass({
render: function() {
return (
<View style=>
<AppMain />
</View>
);
}
});
AppRegistry.registerComponent('ReactTODO', () => App);
RN中,module的引用用的是require,这里第1,2个var分别引入了React和我在components下面自建的module。第三个var里面是这个文件用到的原生APIs和components,所有的原生组件可以去RN的官网查询,例如Image。需要注意的是目前RN for Android还处于完善阶段,原生的components比IOS的要少很多(带IOS的原生组件只support IOS),还有不少坑要填。AppRegistry是js entry;最后一行AppRegistry.registerComponent就是用来用来注册和运行app的;ListView,Text和View分别是列表,文字和视图的组件;StyleSheet是样式的组件。
React.createClass这个方法创建了叫App的组件,最后被AppRegistry调用运行。下面看另外一个自定义的component:
detail.js
'use strict';
var React = require('react-native');
var apiList = require('../api');
var CommentList = require('./commentList');
var CommentInput = require('./commentInput');
var {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
} = React;
var DetailPage = React.createClass({
getInitialState: function() {
return {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
};
},
componentDidMount: function() {
this.fetchData();
},
fetchData: function() {
fetch(apiList.apiWrapper('comments', String(this.props.todo.id)))
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData),
loaded: true,
});
})
.done();
},
updateComment: function() {
this.fetchData()
},
render: function() {
return (
<View style={styles.container}>
<View style={styles.tag}>
<Text style={styles.tagName}>{this.props.todo.tag}</Text>
</View>
<View style={styles.task}>
<Text>{this.props.todo.task}</Text>
</View>
<View style={styles.cell}>
<View style={styles.name}>
<Text>{this.props.todo.name}</Text>
</View>
<View style={styles.time}>
<Text>{this.props.todo.time}</Text>
</View>
</View>
<View style={styles.header}>
<Text style={styles.headerText}>Comments</Text>
</View>
<CommentList
dataSource={this.state.dataSource} />
<CommentInput
todo={this.props.todo}
updateComment={this.updateComment} />
</View>
);
},
});
var styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
cell: {
flexDirection: 'row',
borderBottomWidth: 0.5,
borderBottomColor: '#f5f5f5',
paddingTop: 15,
paddingBottom: 15,
paddingLeft: 20,
},
task: {
paddingTop: 15,
paddingBottom: 15,
borderBottomWidth: 0.5,
borderBottomColor: '#f5f5f5',
paddingLeft: 20,
paddingRight: 20,
},
tag: {
paddingTop: 15,
paddingBottom: 15,
borderBottomWidth: 0.5,
borderBottomColor: '#f5f5f5',
paddingLeft: 20,
},
tagName: {
color: '#C74433',
},
time: {
paddingRight: 20,
},
name: {
flex: 1,
},
header: {
paddingTop: 10,
paddingBottom: 10,
paddingLeft: 20,
backgroundColor: '#f5f5f5',
},
headerText: {
fontSize: 11,
},
});
module.exports = DetailPage;
这个DetailPage是一个比较完整的组件,最后module.exports使得其他模块可以调用DetailPage。在createClass中,有几个方法需要注意:
- getInitialState:初始化函数,返回需要初始化的变量。
- componentDidMount:Component load好以后执行的函数。这里定义了一个fetchData()用来拿RESTFul API的数据。有关fetch可以参阅官方文档。
- componentWillMount:这个没有定义但也很重要,看名字就知道是component load之前执行的函数。
- render:返回组件结构的函数。
StyleSheet.create创建了样式供render的组件调用。
最后关于数据传输,沿用的是react中props和statee,有关于两者的区别和用法可以参阅官方的BLOG:Props and state。