React Native 入门介绍

2018-11-14  本文已影响3896人  安静守护你

前言

上篇文章讲了自己在搭建React Native环境时遇到的问题,这篇就接着来说一下RN项目的基础,包括布局、各种事件等项目日常开发需要用到的,同时也是做一个记录,方便以后自己查询。

正文

环境搭建好了,接下来就是最基本的Hello World咯,不过不着急,可以先使用Xcode打开HelloWorld项目来看一下项目概况。

项目结构

以上就是项目的基本结构,其中包括了我们最常见的AppDelegateImages.xcassetsInfo.plistLaunchScreen.xibmain.m、这几个文件外,还出现了一个Libraries文件夹,其中有很多以.xcodeproj结尾的文件,这些应该是引入的库文件,可以暂时先放放,先我们最为熟悉的main.mAppDelegate文件。

main.m

main.m文件中,还是跟我们原生的iOS新建项目中的main.m一样,作为程序的起始点,将程序的入口引向了AppDelegate文件。

再来看看AppDelegate文件,.h文件中没有什么变化,但是在.m中却有些改变,以下是.m文件中的代码

#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;

  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"HelloWorld"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

@end

在原生的项目中,我们一般都是设置Navwindow.rootViewController,但是这里却将Nav替换成了RCTRootView,虽然不知道是什么,但是根据上面引入的头文件可知,是从React中引入的,我们可以点击进去看一下RCTRootView类的真面目。

根据进去RCTRootView类中看到的可知其是一个继承自UIView的类,其中包含了两个初始化方法和若干属性,RCTRootView类也充当了该项目的程序主页面咯。

废话到此为止,接下来就详细介绍一下入门基础。

Hello World

学习编程的第一步就是输出Hello World
使用自己喜欢的编辑器,打开项目根目录中的App.js文件,很简单的几十行代码,看看和运行起来的界面有何联系,更改其中的某一些代码使模拟器中显示出来Hello World这两个英文单词吧,相信大家都会的。这里不再赘述。

下面给出原始的App.js文件中的代码做出部分讲解(此讲解是以个人第一次见到的第一印象来解释,并不准确,可作为参考,也可培养自己的码感),具体解释直接放在代码旁边

import React, {Component} from 'react'; // 根据iOS中的#import也可以知道是导入头文件,大概意思就是说从react库中导入React和Component,至于为什么Component用大括号括起来,我也不晓得,或许是类别不一样吧
import {Platform, StyleSheet, Text, View} from 'react-native'; 

// const 常量声明的关键字,所以可以大概知道后面声明的这是一个常量,常量中又有ios和android两个
const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
  android:
    'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu',
});

type Props = {};
// 下面这堆东西可以理解为一个名字为App的主类
export default class App extends Component<Props> {
  render() {
    return (
      // 根据界面显示,可以知道界面上显示的内容就是下面这个View中的内容
      // <View></View>这种写法就跟HTML类似,只不过将<div>换成了view
      // 至于其中跟的style就更明显了,样式声明,可是后面跟的{style.container}是个什么鬼,不过往下面的代码中瞄一眼就立马发现了猫腻,下面声明了一个styles的常量,其中包含了这里所写的styles.container,所以可以知道这里就是将样式声明单独列在一起,避免代码臃肿不便阅读。
      <View style={styles.container}>
        <Text style={styles.welcome}>Welcome to React Native!</Text>
        <Text style={styles.instructions}>To get started, edit App.js</Text>
        // 下面这句代码中的尖括号中写的是{instructions},这里的样式表的值就是用大括号括起来的,这里也是用大括号,所以可以暂时性理解为大括号代表一个变量,大括号中的是变量名字,这里对应的就是上面声明的那个常量。
        // 再根据界面显示的内容,就可以发现上面声明的那个常量后面是一个平台鉴别的函数,鉴别当前使用的设备是iOS还是android以此来显示不同的内容
        <Text style={styles.instructions}>{instructions}</Text>
      </View>
    );
  }
}

// 根据StyleSheet.create可以知道这是一个函数,并且是创建了一个样式表的函数
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,
  },
});

// 其实关于上面样式表声明的这种写法还是可以理解的,因为在原生iOS开发的时候,在初始化控件的时候我也采用过以下的写法,供大家参考,其原理我也不大清楚,按照我自己的理解,等号的右边就是一个有返回值的无名函数
// 以下是iOS原生示例,非App.js中代码
// 以下是iOS原生示例,非App.js中代码
// 以下是iOS原生示例,非App.js中代码
UIView *vv = ({
    UIView *v = [[UIView alloc] init];
    v.frame = CGRectMake(10, 10, 100, 44);
    v.backgroundColor = [UIColor redColor];
    // 这里可以声明很多控件组成的一个复杂控件,只需要在这里的最后返回这个复杂控件即可
    return v;
  });

样式

在RN当中,所有的界面样式都是使用JavaScript来实现的,所有的核心组件都接受名为style的属性,只是其中的样式名称命名做了改变,其余的均为改变。

Style属性是一个普通的JavaScript对象,在这里使用的时候还可以传入一个数组,在数组中位置居后的样式对象比居前的优先级更高,这样可以间接实现样式的继承。

实际开发中组件的样式会越来越复杂,我们可以使用StyleSheet.create来几种定义组件的样式,比如下面所示:

import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View } from 'react-native';

export default class LotsOfStyles extends Component {
  render() {
    return (
      <View>
        // 这个Text定义的样式中只改变了显示文字的颜色为红色,其他均为默认
        <Text style={styles.red}>just red</Text>
        // 第二个Text的样式定义改变文字颜色为蓝色,粗体,字号30
        <Text style={styles.bigblue}>just bigblue</Text>
        // 第三个Text的样式定义改变文字颜色为蓝色,粗体,字号30,但是这里是一个数组,styles.red居后,优先级高,所以最终字体的显示效果为红色,粗体,字号30
        <Text style={[styles.bigblue, styles.red]}>bigblue, then red</Text>
        // 第四个Text的样式定义改变文字颜色为红色,但是styles.bigblue优先级更高,故最终显示效果为蓝色,粗体,字号30(后面的字体颜色优先级高于styles.red中定义的红色,故显示蓝色)
        <Text style={[styles.red, styles.bigblue]}>red, then bigblue</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  bigblue: {
    color: 'blue',
    fontWeight: 'bold',
    fontSize: 30,
  },
  red: {
    color: 'red',
  },
});

显示效果图如下:


效果图

高度与宽度

最简单的给组件设定尺寸的方式就是在样式中指定固定数值的widthheight。RN中的尺寸都是无单位的,表示的是与设备像素密度无关的逻辑像素点。

如下:

import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';

export default class FixedDimensionsBasics extends Component {
  render() {
    return (
      <View>
        <View style={{width: 50, height: 50, backgroundColor: 'powderblue'}} />
        <View style={{width: 100, height: 100, backgroundColor: 'skyblue'}} />
        <View style={{width: 150, height: 150, backgroundColor: 'steelblue'}} />
      </View>
    );
  }
}

效果图


效果图

除了指定具体数值的宽高设置,在组件样式中还可以使用flex来在可利用的空间中动态的扩张或收缩。一般而言我们会使用flex:1来指定某个组件扩张以撑满所有剩余的控件。如果有多个并列的子组件使用了flex:1,则这些子组件会平分父容器中剩余的控件。如果这些并列的子组件的flex值不一样,则谁的值更大,谁占据剩余空间的比例就更大(即占据剩余控件的比等于并列组件间flex值的比)

如下示例:

import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';

export default class FlexDimensionsBasics extends Component {
  render() {
    return (
      // 试试去掉父View中的`flex: 1`。
      // 则父View不再具有尺寸,因此子组件也无法再撑开。
      // 然后再用`height: 300`来代替父View的`flex: 1`试试看?
      <View style={{flex: 1}}>
        <View style={{flex: 1, backgroundColor: 'powderblue'}} />
        <View style={{flex: 2, backgroundColor: 'skyblue'}} />
        <View style={{flex: 3, backgroundColor: 'steelblue'}} />
      </View>
    );
  }
}

效果图:


效果图

使用Flexbox布局

我们在RN中使用 flexbox 规则来指定某个组件的子元素的布局。Flexbox 可以在不同屏幕尺寸上提供一致的布局结构。

一般来说,使用flexDirectionalignItemsjustifyContent三个样式属性就已经能满足大多数布局需求。

Flex Direction

在组件的style中指定flexDirection可以决定布局的主轴。
row: 子元素沿着水平轴(row)方向排列
column: 子元素沿着竖直轴(column)方向排列(默认值)

示例如下

import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';

export default class FlexDirectionBasics extends Component {
  render() {
    return (
      // 尝试把`flexDirection`改为`column`看看
      <View style={{flex: 1, flexDirection: 'row'}}>
        <View style={{width: 50, height: 50, backgroundColor: 'powderblue'}} />
        <View style={{width: 50, height: 50, backgroundColor: 'skyblue'}} />
        <View style={{width: 50, height: 50, backgroundColor: 'steelblue'}} />
      </View>
    );
  }
};
效果图
Justify Content

在组件的 style 中指定justifyContent可以决定其子元素沿着主轴的排列方式。效果图均以flexDirection : 'column'为准

以下代码,每次更换justifyContent的值,查看效果

export default class JustifyContentBasics extends Component {
  render() {
    return (
        <View style = {{flex : 1, flexDirection : 'column', justifyContent : 'flex-start'}}>
          <View style = {{width : 50, height : 50, backgroundColor : 'powderblue'}} ><Text>1</Text></View>
          <View style = {{width : 50, height : 50, backgroundColor : 'skyblue'}} ><Text>2</Text></View>
          <View style = {{width : 50, height : 50, backgroundColor : 'steelblue'}} ><Text>3</Text></View>
        </View>
    );
  }
};

flex-start

justifyContent : 'flex-start'

center

justifyContent : 'center'

flex-end

justifyContent : 'flex-end'

space-around

justifyContent : 'space-around'

space-between

justifyContent : 'space-between'

space-evenly

justifyContent : 'space-evenly'
Align Items

在组件的 style 中指定alignItems可以决定其子元素沿着次轴(与主轴垂直的轴,比如若主轴方向为row,则次轴方向为column)的排列方式。

以下代码,以column纵轴为主轴,横轴为次轴,每次更换alignItems的值,查看效果

import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';

export default class AlignItemsBasics extends Component {
  render() {
    return (
      <View style = {{flex : 1, flexDirection : 'column', justifyContent : 'center', alignItems : 'flex-start'}}>
          <View style = {{width : 50, height : 50, backgroundColor : 'powderblue'}} ><Text>1</Text></View>
          <View style = {{width : 50, height : 50, backgroundColor : 'skyblue'}} ><Text>2</Text></View>
          <View style = {{width : 50, height : 50, backgroundColor : 'steelblue'}} ><Text>3</Text></View>
       </View>
    );
  }
};

flex-start

alignItems : 'flex-start'

center

alignItems : 'center'

flex-end

alignItems : 'flex-end'

stretch

注意:要使stretch选项生效的话,子元素在次轴方向上不能有固定的尺寸。

要使stretch生效的话,子元素在次轴上方向上不能有固定的尺寸,即如果主轴为column,子元素不能有固定值的宽;代码如下:

import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';

export default class AlignItemsBasics extends Component {
  render() {
    return (
      <View style = {{flex : 1, flexDirection : 'column', justifyContent : 'center', alignItems : 'flex-start'}}>
          <View style = {{height : 50, backgroundColor : 'powderblue'}} ><Text>1</Text></View>
          <View style = {{height : 50, backgroundColor : 'skyblue'}} ><Text>2</Text></View>
          <View style = {{height : 50, backgroundColor : 'steelblue'}} ><Text>3</Text></View>
       </View>
    );
  }
};
alignItems : 'stretch'

属性(Props)

在OC开发中,当我们自定义一个组件的时候,肯定要对外暴露部分的属性或者方法,以此来增加组件的兼容性,能够让使用者对组件的颜色、内容等可以定制化开发。

在React Native中的大多数组件在创建时就可以使用各种参数来定制。用于定制的这些参数就成为props(属性)。

比如基础组件Image,在创建图片的时候传入一个名为source的prop来指定要显示的图片的地址,传入一个名为style的prop来控制其尺寸大小。

import React, { Component } from 'react';
import { Image } from 'react-native';

export default 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}} />
    );
  }
}

注意:{pic}外围有一层大括号,大括号的意思是说在大括号内部是一个js变量或者表达式,需要执行后取值。

以上说的是自带的基础组件,当然我们也可以像在OC中一样,自定义自己需要的组件,这时候我们也可以为自己的组件定义prop,当在调用的时候,就可以在父组件中指定其值。如下:

// 此为一个js文件
import React, { Component } from 'react';
import { Text, View } from 'react-native';

// 在RN的一个js文件中,可以有很多形如 “class 组件名 extends Component” 的组件声明
class Greeting extends Component {
  render() {
    return (
      <Text>Hello {this.props.name}!</Text>
    );
  }
}

// 在RN的一个js文件中,必须要只有一个下面的默认组件声明,这个声明可以理解为一个文件的入口函数
export default class LotsOfGreetings extends Component {
  render() {
    return (
      <View style={{alignItems: 'center'}}>
        // 这里是引用上面定义的Greeting组件,其中name是其属性,在其内部使用this.props.name引用父组件设置的值
        <Greeting name='Rexxar' />
        <Greeting name='Jaina' />
        <Greeting name='Valeera' />
      </View>
    );
  }
}

状态(State)

我们使用两种数据来控制一个组件:propsstateprops是在父组件中指定,而且一经指定,在被指定的组件的声明周期中则不再改变。对于需要改变的数据,我们需要使用state

就像我们在iOS原生中请求网络数据一样,在得到服务器返回的数据后,我们就会进行赋值、刷新界面等操作。在React Native中,我们可以直接使用state,一般来说,我们首先需要在constructor中初始化state,然后在需要修改时(如收到服务器返回的数据时)调用setState方法,在每次调用setState时,系统会重新执行render方法重新渲染界面。

注意:

  • 一切界面变化都是状态state变化
  • state的修改必须通过setState()方法
  • setState是一个merge合并操作,只修改指定属性,不影响其他属性
  • setState是异步操作,修改不会马上生效

结束语

关于React Native的入门介绍其实并不是很多,如果是从事网站开发的人员入手会更加的简单快速,这对于App开发人员来说也不是特别难的一件事,当然,关于这些入门的懂了之后,剩下的就是研究一下React Native的基础组件的使用、生命周期函数、一些特别的写法等问题,在接下来的空闲时间,我也会一一列举这些知识,希望能够帮到更多的人。

上一篇下一篇

猜你喜欢

热点阅读