1.React-Native初体验
前言
最近因为离职赋闲在家,投投简历找找工作,杭州iOS的这波行情我也是醉了投出去的简历分分钟石沉大海。
失业的日子总让人焦躁不安,自己又不是一个喜欢看那种无聊的面试经的人,想来还是做点正事。
女友的公司正在大规模转向React-Native,反正我闲着也是闲着,既然RN这么火,我不学也要落后啊,落后就要挨打。
下面的我就一边学一边写了,加油!~~~
一、教程
ReactNative中文网 这个网站应该是大多数同学学习RN的途径,当然网上其他教程也挺多,我觉得这个网站不错还有中文翻译文档,我们今天就从这个网站入手开始学习RN。
二、今日计划
打开ReactNative中文网的ReactNative中文网-文档页面,看到左边排列了几十个栏目,今天因为是初体验,我们的目的是大致了解RN是什么,尝试写一些简单的页面。那么今天我们就争取完成文档的“入门基础”部分。(你们说我擦这么多能完成么,哥哥我失业在家好么..不要太空好么TUT)
三、那就开始吧!
1.搭建开发环境
我们是iOS开发人员,当然选择目标平台:iOS,开发平台:mac.不过它默认就是这么选的,我们也不用去改动他。
照着教程走,安装Homebrew,我擦嘞好慢啊,我去烧个早饭...
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
如果碰到权限问题用下面的命令解决(反正我是没遇到)
sudo chown -R `whoami` /usr/local
安装node
brew install node
更改npm镜像
npm config set registry https://registry.npm.taobao.org --global
npm config set disturl https://npm.taobao.org/dist --global
安装React Native
npm install -g react-native-cli
安装Xcode...(iOS开发者没有Xcode..你逗我Xcode下载)
后面是推荐安装工具,反正装了没错万一到时候用到了..
安装Watchman
brew install watchman
安装Flow
brew install flow
安装Sublime Text,因为我本来就装过了,我就用这个了,如果大家喜欢用Nuclide或者WebStorm也行
好了~这下应该要用到的东西都装好了!
接下来就是跑一把了
为了方便我们先切到桌面
cd desktop
初始化一个叫AwesomeProject的RN项目
react-native init AwesomeProject
切换到AwesomeProject目录下
cd AwesomeProject
运行iOS项目
react-native run-ios
我擦命令行发狂了...然后还弹出了一个新的命令行界面什么鬼!!!
过了大概1分钟...iOS模拟器竟然运行起来了
66666666666666666666666666666666
点一点,然而并没有什么卵用。
接下来教程叫我们打开index.ios.js
自行修改
我们先打开AwsomeProject文件夹看下RN项目究竟是什么样的
看到我们最熟悉的ios,先点击ios文件夹看下
WechatIMG4.jpeg原来ios就是指这个RN项目对应的iOS工程文件,外面还有一个android就应该是对应的安卓工程文件。
node_modules,很多很长,应该是RN自己用到的相关文件,我们就不深入看了反正也看不懂。
index.ios.js,恩JS文件,打开来看看。
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
export default class AwesomeProject 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('AwesomeProject', () => AwesomeProject);
单词大家都认识,但是语法啥的不一样还是没法一下子明白的。教程里叫我们随便改上几行,我想应该也不能太随便吧...
那我们就先改个文字,应该是这部分,改成我们自己的
return (
<View style={styles.container}>
<Text style={styles.welcome}>
哈哈哈哈哈哈哈哈哈哈
</Text>
<Text style={styles.instructions}>
我擦嘞
</Text>
<Text style={styles.instructions}>
什么鬼
</Text>
</View>
);
保存下,然后在iOS模拟器上按下command+R.
WechatIMG5.jpeg6666666666666666666666666666666666
可以看到界面已经变成我改的文字了。
还有command+R就能刷新界面这个有点吊的。
恩至此教程的第一部分,搭建开发环境已经看完了。我们来做个简单总结:
1.搭建环境有毛好总结的...
2.react-native init AwesomeProject
来初始化一个项目,然后react-native run-ios
就能让项目跑起来。command+R
能够不用重启项目就刷新界面。
3.没了,差不多,恩
2.编写hello world
上来教程就给了我们一段代码
import React, { Component } from 'react';
import { AppRegistry, Text } from 'react-native';
class HelloWorldApp extends Component {
render() {
return ( <Text>Hello world!</Text> );
}
}
// 注意,这里用引号括起来的'HelloWorldApp'必须和你init创建的项目名一致
AppRegistry.registerComponent('HelloWorldApp', () => HelloWorldApp);
覆盖AwesomeProject的index.ios.js文件。command+R刷新下。
我擦类,竟然报错了什么鬼!
看报错说的大概是“AwesomeProject没有被注册,这是因为在初始化的时候没有调用AppRegistry.registerComponent”
回头看看代码,看到有句注释说的是
// 注意,这里用引号括起来的'HelloWorldApp'必须和你init创建的项目名一致
AppRegistry.registerComponent('HelloWorldApp', () => HelloWorldApp);
恩我猜想大概因为是我们项目叫AwesomeProject,而这里注册的是HelloWorldApp。
恩把代码改为如下
import React, { Component } from 'react';
import { AppRegistry, Text } from 'react-native';
class HelloWorldApp extends Component {
render() {
return ( <Text>Hello world!</Text> );
}
}
// 注意,这里用引号括起来的'HelloWorldApp'必须和你init创建的项目名一致
AppRegistry.registerComponent('AwesomeProject', () => HelloWorldApp);
保存(一定要记得保存,这不是Xcode),command+R.界面果然正常了。
WechatIMG7.jpeg在状态栏下有一个helloworld文字看到没~!(反正我是瞎了,神之DEMO).
回头再看下代码,教程里说了,<Text>Hello world!</Text>
是显示文本的组件,我猜想大概就跟iOS里的UILabel差不多,然后class HelloWorldApp extends Component
是声明了一个叫HelloWorldApp
的组件,最后通过AppRegistry.registerComponent('AwesomeProject', () => HelloWorldApp);
将刚才声明的组件注册到'AwesomeProject'项目中。
这一节结束了,总结下:
1.其他没啥重要的,声明组件后别忘了将组件AppRegistry.registerComponent
一下。
3.Props(属性)
教程里的第一段代码
import React, { Component } from 'react';
import { AppRegistry, Image } from 'react-native';
class Bananas extends Component {
render() {
let pic = {
uri: 'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'
};
return (
<Image source={pic} style={{width: 193, height: 110}} />
);
}
}
AppRegistry.registerComponent('AwesomeProject', () => Bananas);
跑起来,过了半天才出现图片..
然后教程里说http的图片可能无法加载,应该就是iOS拦截了非https的请求,改下plist文件的设置就好了,这个大家应该都知道。(这篇说明修改)
第二段代码
import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';
class Greeting extends Component {
render() {
return (
<Text>Hello {this.props.name}!</Text>
);
}
}
class LotsOfGreetings extends Component {
render() {
return (
<View style={{alignItems: 'center'}}>
<Greeting name='Rexxar' />
<Greeting name='Jaina' />
<Greeting name='Valeera' />
</View>
);
}
}
AppRegistry.registerComponent('AwesomeProject', () => LotsOfGreetings);
这段代码有点意思,跑起来后的结果
WechatIMG9.jpeg我们看下代码来强行理解下
import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';
//这里定义了一个叫Greeting的组件
class Greeting extends Component {
render() {
return (
//这里的文本,获取了this.props.name的内容
<Text>Hello {this.props.name}!</Text>
);
}
}
//这里定义了一个叫LotsOfGreetings的组件
class LotsOfGreetings extends Component {
render() {
return (
<View style={{alignItems: 'center'}}>
//这里使用了上面定义的Greeting 传递了一个叫name的东西,应该就是文中的props的意思。
<Greeting name='Rexxar' />
<Greeting name='Jaina' />
<Greeting name='Valeera' />
</View>
);
}
}
AppRegistry.registerComponent('AwesomeProject', () => LotsOfGreetings);
那么我大概明白了这个代码的套路,接下来我们尝试自己写下。
import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';
class NameLabel extends Component{
render (){
return(
<Text>我的名字是:{this.props.name}</Text>
);
}
}
class AgeLabel extends Component{
render (){
return(
<Text>我的年龄是:{this.props.age}</Text>
);
}
}
class Person extends Component{
render(){
return(
<View style={{alignItems: 'center'}}>
<NameLabel name = '范方梁'/>
<AgeLabel age = '24'/>
</View>
);
}
}
AppRegistry.registerComponent('AwesomeProject', () => Person);
command+R,运行起来,非常完美
WechatIMG10.jpeg总结:
1.我们可以通过props向组件传递数据
4.State(状态)
同样先来强行理解下教程中的代码
import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';
//定义一个Blink组件
class Blink extends Component {
//初始化函数??应该类似OC中的init,传入props就是外面传入的参数
constructor(props) {
//类似OC[super initwith..]
super(props);
//当前的状态? showText为真
this.state = { showText: true };
// 每1000毫秒对showText状态做一次取反操作
setInterval(() => {
//哦我知道了 state应该是类似oc中的字典,保存了key为"showText",值为true.
//然后this.state{ showText: true }是初始化state,
//this.setState{}用来改变state的值?
this.setState({ showText: !this.state.showText });
}, 1000);
}
//可是即使state改变了,难道state每改变一次就会调用一次rander()吗?
render() {
// 根据当前showText的值决定是否显示text内容
let display = this.state.showText ? this.props.text : ' ';
return (
<Text>{display}</Text>
);
}
}
class BlinkApp extends Component {
render() {
return (
<View>
<Blink text='I love to blink' />
<Blink text='Yes blinking is so great' />
<Blink text='Why did they ever take this out of HTML' />
<Blink text='Look at me look at me look at me' />
</View>
);
}
}
AppRegistry.registerComponent('AwesomeProject', () => BlinkApp);
阅读代码的过程中产生了几个问题:
1.state应该能够保存多个状态
2.每次state改变,都会调用rander()吗?
为此我们自己写个代码来测试下
class MyLabel extends Component{
constructor (props){
super(props);
this.state = {num:1,total:10};
setInterval(() => {
this.setState({ num: this.state.num+1});
}, 500);
setInterval(() => {
this.setState({total:this.state.total-1});
}, 1000);
}
render(){
return(
<View>
<Text>{this.state.num}</Text>
<Text>{this.state.total}</Text>
</View>
);
}
}
class ShowLabel extends Component{
render(){
return(
<View>
<MyLabel />
</View>
);
}
}
AppRegistry.registerComponent('AwesomeProject', () => ShowLabel);
总结:实验证明确实state能够保存多个状态,并且每次setState都会导致render()被调用。
5.样式
6.高度宽度
7.使用Flexbox布局
5.6.7为啥写一起呢,因为我大概看了下,就是对样式的更改,我在这里不做更多解释了。这个直接写总结吧,大家看教程其实差不多。
总结:
1.用style的形式来写样式,接触过HTML+CSS的同学应该都了解。
在RN里的样式语法其实和CSS差不多
2.用StyleSheet能更好的管理样式
const styles = StyleSheet.create({
bigblue: {
color: 'blue',
fontWeight: 'bold',
fontSize: 30,
},
red: {
color: 'red',
},
});
3.后设置的样式会覆盖前面的样式
8.处理文本输入
照惯例先来解读一下示例代码
import React, { Component } from 'react';
import { AppRegistry, Text, TextInput, View } from 'react-native';
//定义一个叫PizzaTranslator的组件
class PizzaTranslator extends Component {
constructor(props) {
super(props);
//初始化state中的text为''
this.state = {text: ''};
}
render() {
return (
<View style={{padding: 10}}>
//使用TextInput
<TextInput
style={{height: 40}}
placeholder="Type here to translate!"
//当文本改变时 会调用onChangeText后面括号中的方法 设置state中的text为输入框中的text 这会导致调用rander()重绘界面
onChangeText={(text) => this.setState({text})} />
<Text style={{padding: 10, fontSize: 42}}>
//将state中的text通过' '空格来分割,然后将文本替换为🍕这里涉及到split(),map(),join()我们暂时不太理解用法之后看语法再理解
{this.state.text.split(' ').map((word) => word && '🍕').join(' ')}
</Text>
</View>
);
}
}
// 注册应用(registerComponent)后才能正确渲染
// 注意:只把应用作为一个整体注册一次,而不是每个组件/模块都注册
AppRegistry.registerComponent('AwesomeProject', () => PizzaTranslator);
运行起来
WechatIMG11.jpeg
确实像教程写的一样,输入后的文本被空格分隔,替换为🍕,并且能够在每次输入的同时进行及时刷新。
其中需要注意的是onChangeText,教程中还说了onSubmitEditing属性。我们改下代码看下会有什么结果。
import React, { Component } from 'react';
import { AppRegistry, Text, TextInput, View } from 'react-native';
class PizzaTranslator extends Component {
constructor(props) {
super(props);
this.state = {text: ''};
}
render() {
return (
<View style={{padding: 10}}>
<TextInput
style={{height: 40}}
placeholder="Type here to translate!"
onChangeText={(text) => this.setState({text})}
onSubmitEditing = {() => this.setState({text:'a b c d e'})}
/>
<Text style={{padding: 10, fontSize: 42}}>
{this.state.text.split(' ').map((word) => word && '🍕').join(' ')}
</Text>
</View>
);
}
}
// 注册应用(registerComponent)后才能正确渲染
// 注意:只把应用作为一个整体注册一次,而不是每个组件/模块都注册
AppRegistry.registerComponent('AwesomeProject', () => PizzaTranslator);
按下回车后输入的text会被替换为'a b c d e'.
总结:
1.TextInput控件能够被用来做输入框
- onChangeText当文本发生改变时调用
- onSubmitEditing当按下回车按钮时调用
9.如何使用ScrollView
照惯例看代码
import React, { Component } from 'react';
import{ AppRegistry, ScrollView, Image, Text, View } from 'react-native'
class IScrolledDownAndWhatHappenedNextShockedMe extends Component {
render() {
return(
//使用scrollview组件
<ScrollView>
//这里看出使用的scrollview应该就是UISCrollview的套路,不会被复用的那种
<Text style={{fontSize:96}}>Scroll me plz</Text>
//指定本地的图片地址
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Text style={{fontSize:96}}>If you like</Text>
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Text style={{fontSize:96}}>Scrolling down</Text>
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Text style={{fontSize:96}}>What the best</Text>
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Text style={{fontSize:96}}>Framework around?</Text>
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Image source={require('./img/favicon.jpg')} />
<Text style={{fontSize:80}}>React Native</Text>
</ScrollView>
);
}
}
// 注册应用(registerComponent)后才能正确渲染
// 注意:只把应用作为一个整体注册一次,而不是每个组件/模块都注册
AppRegistry.registerComponent(
'AwesomeProject',
() => IScrolledDownAndWhatHappenedNextShockedMe);
很好理解,轻松写意。
总结:没啥好总结的.
10.如何使用ListView
listView应该就是iOS中的UITableView了,会进行复用,能够提升性能
import React, { Component } from 'react';
import { AppRegistry, ListView, Text, View } from 'react-native';
class ListViewBasics extends Component {
// 初始化模拟数据
constructor(props) {
super(props);
//这里应该是创建了一个数据源,rowHasChanged: (r1, r2) => r1 !== r2是为了告诉listview当两行数据不同时是否需要重绘
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
//这里为ds设置了数据源
dataSource: ds.cloneWithRows([
'John', 'Joel', 'James', 'Jimmy', 'Jackson', 'Jillian', 'Julie', 'Devin'
])
};
}
render() {
return (
<View style={{flex: 1, paddingTop: 22}}>
<ListView
//传入数据源是this.state.datasource
dataSource={this.state.dataSource}
renderRow={(rowData) => <View style={{height:50,backgroundColor:'red'}}><Text>name = {rowData}</Text></View>}
/>
</View>
);
}
}
// 注册应用(registerComponent)后才能正确渲染
// 注意:只把应用作为一个整体注册一次,而不是每个组件/模块都注册
AppRegistry.registerComponent('AwesomeProject', () => ListViewBasics);
WechatIMG12.jpeg
这里代码还是有些不太明白,没关系我们先放着,慢慢会懂的嗯。
毕竟今天初体验,知道个套路就行。
11.网络
网络套路深,这里先跳过这节,我们没必要立刻接触这么深入的东西。
12.使用导航器跳转页面
跳转这个太重要了。没有跳转的app还是app么。
完整代码如下
index.ios.js
import React, { Component } from 'react';
import { AppRegistry, Navigator, Text, View } from 'react-native';
//这里引入MyScene
import MyScene from './MyScene';
class SimpleNavigationApp extends Component {
render() {
return (
//使用Navigator组件
<Navigator
//初始路由? 标题,名称
initialRoute={{ title: 'My Initial Scene', index: 0 }}
//渲染场景?
renderScene={(route, navigator) =>
<MyScene
title={route.title}
// 这个方法被当做一个props传入 也就是点击下一页会调用
onForward={ () => {
const nextIndex = route.index + 1;
navigator.push({
title: 'Scene ' + nextIndex,
index: nextIndex,
});
}}
// 这个方法被当做一个props传入 也就是点击返回
onBack={() => {
if (route.index > 0) {
navigator.pop();
}
}}
/>
}
/>
)
}
}
AppRegistry.registerComponent('AwesomeProject', () => SimpleNavigationApp);
MyScene.js
import React, { Component } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
export default class MyScene extends Component {
static defaultProps = {
title: 'MyScene'
};
render() {
return (
<View>
<Text>Hi! My name is {this.props.title}.</Text>
//当点击时onPress 会调用onForward方法
<TouchableHighlight onPress={this.props.onForward}><Text>点我下一页</Text></TouchableHighlight >
//当点击时onPress 会调用onBack方法
<TouchableHighlight onPress={this.props.onBack}><Text>点我返回</Text></TouchableHighlight >
</View>
)
}
}
总结:
- initialRoute传入初始路由,标题,位置,其实我觉得就是传入一些初始数据。
- renderScene需要制定需要渲染的初始页面。
3.navgator.push会推出一个新页面,因为navigator.push是写在这里的关系,推出的页面就是之前指定的randerScene.
4.navigator.pop是退出页面。
今日总结
今天通过入门基础这些章节,粗略的看了下RN是怎么搞的,基本了解了RN的套路,当然估计有很多理解错误的地方....写一个简单的页面应该不在话下,就是数据的问题....
感觉到接下来需要学习的东西仍然很多。比如JS语法基础,各个控件的高级用法等。
明天继续!加油!