美文网首页
1.React-Native初体验

1.React-Native初体验

作者: 炒鸡范 | 来源:发表于2016-10-27 16:34 被阅读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语法基础,各个控件的高级用法等。
    明天继续!加油!

    相关文章

      网友评论

          本文标题:1.React-Native初体验

          本文链接:https://www.haomeiwen.com/subject/xbijuttx.html