1.React-Native初体验

2016-10-27  本文已影响109人  炒鸡范

前言

最近因为离职赋闲在家,投投简历找找工作,杭州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模拟器竟然运行起来了

WechatIMG2.jpeg

66666666666666666666666666666666
点一点,然而并没有什么卵用。

接下来教程叫我们打开index.ios.js自行修改
我们先打开AwsomeProject文件夹看下RN项目究竟是什么样的

WechatIMG3.jpeg

看到我们最熟悉的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.jpeg

6666666666666666666666666666666666
可以看到界面已经变成我改的文字了。
还有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刷新下。
我擦类,竟然报错了什么鬼!

WechatIMG6.jpeg

看报错说的大概是“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);

跑起来,过了半天才出现图片..

WechatIMG8.jpeg
然后教程里说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控件能够被用来做输入框

  1. onChangeText当文本发生改变时调用
  2. 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>
    )
  }
}

总结:

  1. initialRoute传入初始路由,标题,位置,其实我觉得就是传入一些初始数据。
  2. renderScene需要制定需要渲染的初始页面。
    3.navgator.push会推出一个新页面,因为navigator.push是写在这里的关系,推出的页面就是之前指定的randerScene.
    4.navigator.pop是退出页面。

今日总结

今天通过入门基础这些章节,粗略的看了下RN是怎么搞的,基本了解了RN的套路,当然估计有很多理解错误的地方....写一个简单的页面应该不在话下,就是数据的问题....
感觉到接下来需要学习的东西仍然很多。比如JS语法基础,各个控件的高级用法等。
明天继续!加油!

上一篇下一篇

猜你喜欢

热点阅读