# React Native基础

RN底层引擎是 JavaScript Core,但调用的是原⽣的组件⽽非HTML5组件。这样运⾏时可以做到与 Navive App 相媲美的性能体验,同时因为 JavaScript 代码可以使用后端强⼤的Web方式管理,既可以做到⾼效开发,也可以实现快速部署和问题热修复。

React Native优缺点:

优点

  1. 跨平台开发:运⽤React Native,我们可以使⽤同⼀份业务逻辑核⼼代码来创建原⽣应⽤运⾏在Web端,Android端和iOS端;
  2. 追求极致的⽤户体验:实时热部署;
  3. React Native不强求一份原生代码支持多个平台,所以不是write once,run anywhere;

缺点

  1. react native在iOS上仅⽀持 iOS7 以上,Android仅⽀持 Android4.1 以上;
  2. 开发成本较⾼;
  3. 部分复杂的界⾯和操作,RN⽆法实现(可以考虑原⽣+React Native混合开发);

# 一、环境搭建

环境搭建参考:环境搭建

需要注意的是不要在目录、文件名中使用中文、空格等特殊符号。我第一次搭建环境的时候就踩到这个坑了, 折腾了很久,都是没认真看官网的结果。

# 二、基础知识

# 布局

RN中的布局⽅式采⽤的是FlexBox(弹性布局),FlexBox提供了在不通尺⼨设备上都能保持⼀致的布局方式。

RN中FlexBox和Web Css上FlexBox⼯作⽅式是⼀样的,但有些地⽅还是有出入的

  • flexDirection:(主轴⽅向)

    RN中默认是flexDirection:'column',Web Css中默认是 flex-direction:'row'

  • alignItems:(在交叉轴上的对⻬⽅式)

    RN中默认alignItems: 'stretch',在Web Css中默认 align-items:'flex-start'

  • flex:

    RN中只接受⼀个属性,Web css 可以接受多个属性:flex: 2 2 10%

  • 不⽀持的属性: align-content flex-basis order flex-flow flex-grow flex-shrink

以下属性是RN所⽀持的Flex属性

  • 容器属性
  flexDirection: row | column| row-reverse | column-reverse
  flexWrap: wrap | noWrap
  justifyContent: flex-start | flex-end | center | space-between | space-around
  alignItems: flex-start | flex-end | center | stretch
1
2
3
4
  • 项⽬属性
  alignSelf
  stretch
  center
  flex-start
  flex-end
1
2
3
4
5
  • flex:定义了⼀个元素可伸缩的能⼒,默认是0

# 像素无关

在RN中尺⼨是没有单位的,它代表的是设备独⽴像素

<View style={{width:100,height:100,margin:10,backgroundColor:'gray'}}>
 <Text style={{fontSize:16,margin:20}}>尺⼨</Text>
</View>
1
2
3

上述代码,运⾏在Android上时,View的⻓宽被解释成:100dp 100dp,字体被解释成16sp,运⾏在ios上时尺⼨单位被解释成pt,这些单位确保了布局在任何不通DPI的⼿机屏幕上,显示效果⼀致。

# 样式

在RN中样式需要引入StyleSheet API

写法⼀:

<View style={styles.container}></View>
const styles = StyleSheet.create({
 container:{
 ...
 }
});
1
2
3
4
5
6

组件内写法:

<View style={{backgroundColor:'red'}}></View>
//or
<View style={[styles.container,{backgroundCorlor:'red'}]}></View>
1
2
3

# 三、常用组件

在RN中使⽤原⽣组件,是依赖React的,所以在使⽤过程中需要导⼊react

import React, { Component } from "react";
import { Button, Platform, StyleSheet, Text, View } from "react-native";
1
2

# Button

⼀个简单的跨平台的按钮组件。可以进⾏⼀些简单的定制。

<Button 
 onPress={onPressLearnMore} //⽤户点击此按钮时所调⽤的处理函数
 title="Learn More" //按钮按钮内显示的⽂本
 color="#841584" //⽂本的颜⾊(iOS),或是按钮的背景⾊(Android)
 disabled={false} //按钮是否可以点击
 accessibilityLabel="Learn more about this purple button" //⽤于给残障⼈⼠显示的⽂本(⽐如读屏应⽤可能会读取这⼀内容
/>
1
2
3
4
5
6
7

# ActivityIndicator

显示⼀个圆形的 loading 提示符号。

<View style={[styles.container, styles.horizontal]}>
 <ActivityIndicator 
 size="large" //指示器的⼤⼩,默认为'small'[enum('small', 'large'), number]。⽬前只能在 Android 上设定具体的数值
 animating={true} //是否要显示指示器动画,默认为 true 表示显示,false 则隐藏。
 hidesWhenStopped={false} //在animating为 false 的时候,是否要隐藏指示器(默认为true)。如果animating和hidesWhenStopped都为 false,则显示⼀个静⽌的指示器。
 color="#0000ff" //滚轮的前景颜⾊(默认为灰⾊)。
 />
</View>
1
2
3
4
5
6
7
8

# Image

⽤于显示多种不同类型图⽚的 React 组件,包括⽹络图⽚、静态资源、临时的本地图⽚、以及本地磁盘上的图⽚(如相册)等。下⾯的例⼦分别演示了如何显示从本地缓存、⽹络甚⾄是以 'data:' 的 base64 uri 形式提供的图⽚。

<Image source={require('/react-native/img/favicon.png')}/>

<Image style={{width: 50, height: 50}}
 //⽹络和 base64 数据的图⽚需要⼿动指定尺⼨
 source={{uri: 'https://facebook.github.io/reactnative/docs/assets/favicon.png'}}
/>
<Image style={{width: 66, height: 58}}
 //⽹络和 base64 数据的图⽚需要⼿动指定尺⼨
 source={{uri:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg=='}}
/>
1
2
3
4
5
6
7
8
9
10
11

默认情况下 Android 是不⽀持 GIF 和 WebP 格式的。你需要在 android/app/build.gradle ⽂件中根据需要⼿动添加以下模块:

dependencies {
 // 如果你需要⽀持Android4.0(API level 14)之前的版本
 compile 'com.facebook.fresco:animated-base-support:1.10.0'
 // 如果你需要⽀持GIF动图
 compile 'com.facebook.fresco:animated-gif:1.10.0'
 // 如果你需要⽀持WebP格式,包括WebP动图
 compile 'com.facebook.fresco:animated-webp:1.10.0'
 compile 'com.facebook.fresco:webpsupport:1.10.0'
 // 如果只需要⽀持WebP格式⽽不需要动图
 compile 'com.facebook.fresco:webpsupport:1.10.0'
}
1
2
3
4
5
6
7
8
9
10
11

# SafeAreaView

SafeAreaView 的⽬的是在⼀个“安全”的可视区域内渲染内容。具体来说就是因为⽬前有 iPhone X 这样的带有“刘海”的全⾯屏设备,所以需要避免内容渲染到不可⻅的“刘海”范围内。本组件⽬前仅⽀持 iOS 设备以及 iOS 11 或更⾼版本。SafeAreaView 会⾃动根据系统的各种导航栏、⼯具栏等预留出空间来渲染内部内容。更重要的是,它还会考虑到设备屏幕的局限,⽐如屏幕四周的圆⻆或是顶部中间不可显示的“刘海”区域。

# Text

⼀个⽤于显示⽂本的React组件,并且它也支持嵌套、样式,以及触摸处理,在Text内部的元素不再使⽤flexbox布局,⽽是采⽤⽂本布局。这意味着 <Text> 内部的元素不再是⼀个个矩形,⽽可能会在行末进行折叠.

<Text
  ellipsizeMode={"tail"} //这个属性通常和下⾯的 numberOfLines 属性配合使⽤,⽂本超出numberOfLines设定的⾏数时,截取⽅式:head- 从⽂本内容头部截取显示省略号。例如:"...efg",middle - 在⽂本内容中间截取显示省略号。例如: "ab...yz",tail - 从⽂本内容尾部截取显示省略号。例如: "abcd...",clip - 不显示省略号,直接从尾部截断。
  numberOfLines={1} //配合ellipsizeMode设置⾏数
  onPress={} //点击事件
  selectable={true}//决定⽤户是否可以⻓按选择⽂本,以便复制和粘贴。
>
</Text>
1
2
3
4
5
6
7

# TextInput

是⼀个允许⽤户在应⽤中通过键盘输⼊⽂本的基本组件。本组件的属性提供了多种特性的配置,譬如⾃动完成、⾃动⼤⼩写、占位⽂字,以及多种不同的键盘类型(如纯数字键盘),TextInput 在安卓上默认有⼀个底边框,同时会有⼀些padding。如果要想使其看起来和iOS上尽量⼀致,则需要设置 padding: 0。

<TextInput
  style={{
    width: 100,
    height: 40,
    borderWidth: 3,
    borderColor: "blue"
  }}
  keyboardType={"default"} //决定弹出何种软键盘类型,譬如numeric(纯数字键盘),default,number-pad,decimal-pad,numeric,email-address,phone-pad
  maxLength={20} //限制⽂本框中最多的字符数。使⽤这个属性⽽不⽤JS逻辑去实现,可以避免闪烁的现象。
  editable={true} //如果为false,⽂本框是不可编辑的。默认值为true。
  defaultValue={"xxxx"} //提供⼀个⽂本框中的初始值
  caretHidden={true} //如果为true,则隐藏光标。默认值为false。
  autoCapitalize={"none"} //控制TextInput是否要⾃动将特定字符切换为⼤写:characters:所有的字符,words: 每个单词的第⼀个字符,sentences: 每句话的第⼀个字符(默认),none: 不切换。
  //当⽂本框内容变化时调⽤此回调函数。改变后的⽂字内容会作为参数传递。从TextInput⾥取值这就是⽬前唯⼀的做法!
  onChangeText={text => {
    this.setState({
      text: text
    });
  }}
 />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# View

类似于html中的div,容器组件,可以使⽤[,]的形式返回多个兄弟组件。

# WebView

WebView 创建⼀个原⽣的 WebView,可以⽤于访问⼀个网页。

class MyWeb extends Component {
 render() {
   return (
   <WebView
     source={{uri: 'https://github.com/facebook/react-native'}}
     style={{marginTop: 20}}
     />
   );
}
1
2
3
4
5
6
7
8
9

注意:新版的RN已经⽤react-naitve-webView替代RN原⽣Core中的WebView 使用方法:

yarn add react-native-webview
react-native link react-native-webview
1
2

使用的时候 import { WebView } from "react-native-webview"

# ListView

经常使⽤ListView的同学都知道,这个组件的性能⽐较差,尤其是当有⼤量的数据需要展示的时候,ListView对内存的占⽤较多,常出现丢帧卡顿现象。ListView底层实现,渲染组件Item是全量渲染,⽽且没有复⽤机制,这就不可避免的当渲染较⼤数据量时,会发现以下情况:

  • 第⼀次打开与切换Tab时会出现卡顿或⽩屏的情况,⽐如ListView中有100个Item,只能等这100条Item都渲染完成,ListView中的内容才会展示
  • 滑动列表时会出现卡顿不跟⼿,listVIew渲染⼤量数据,需要占⽤较多的内存用于计算。 未来有很⼤可能性会被移除。

# VirtualizedList

FlatListSectionList 的底层实现,VirtualizedList通过维护⼀个有限的渲染窗⼝(其中包含可见的元素),并将渲染窗⼝之外的元素全部⽤合适的定⻓空⽩空间代替的⽅式,极⼤的改善了内存使⽤,提⾼了⼤量数据情况下的渲染性能。这个渲染窗⼝能响应滚动⾏为,元素离可视区越远优先级越低,越近优先级越⾼,当⽤户滑动速度过快时,会出现短暂空白的情况。

# FlatList

在RN0.43版本中引⼊了FlatListSectionListVirtualizedList,其中VirtualizedListFlatListSectionList的底层实现。 缺点:(1)为了优化内存占⽤同时保持滑动的流畅,列表内容会在屏幕外异步绘制。这意味着如果⽤户滑动的速度超过渲染的速度,则会先看到空⽩的内容。(2)不支持分组列表。

<FlatList
  data={[{key: 'a'}, {key: 'b'}]}
  renderItem={({item}) => <Text>{item.key}</Text>}
/>
1
2
3
4

# SectionList

高性能的分组列表组件。缺点:同样会有空白内容的情况。

# RefreshControl

这⼀组件可以⽤在ScrollView或FlatList内部,为其添加下拉刷新的功能。当ScrollView处于竖直⽅向的起点位置(scrollY: 0),此时下拉会触发⼀个 onRefresh事件。

# 四、常用API

# Dimensions

⽤于获取设备屏幕的宽⾼。

let {height, width} = Dimensions.get('window');
1

# Platform

平台API判断。

import { Platform, StyleSheet } from "react-native";
const styles = StyleSheet.create({
 height: Platform.OS === "ios" ? 200 : 100
});
// Platform.select(),以Platform.OS为 key,从传⼊的对象中返回对应平台的值:
const Component = Platform.select({
 ios: () => require("ComponentIOS"),
 android: () => require("ComponentAndroid")
})();
// 检测Adr版本
if (Platform.Version === 25) {
 console.log("Running on Nougat!");
}
// 检测iOS版本
const majorVersionIOS = parseInt(Platform.Version, 10);
if (majorVersionIOS <= 9) {
 console.log("Work around a change in behavior");
}
// 当不同平台代码逻辑较为复杂时,可以使⽤平台扩展名
// BigButton.ios.js
// BigButton.android.js
const BigButton = require("./BigButton");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 五、FlatList案例

FlatList 实现上拉加载,下拉刷新,模拟请求数据,处理没有数据时的效果。

import React, { useState } from 'react';
import { Button, StyleSheet, FlatList, Text, View, RefreshControl, ActivityIndicator } from "react-native";

const CITY_NAMES = ["北京", "上海", "广州", "深圳", "成都", "武汉", "南京"];

const App = () => {
  const [isLoading, setIsLoading] = useState(false) // 是否正在加载
  const [dataArray, setDataArray] = useState(CITY_NAMES) // 默认数据

  // 列表项组件
  const renderItemView = (data) => (
    <View style={styles.item}>
      <Text style={styles.text}>{data.item}</Text>
    </View>
  )

  // 下拉刷新组件
  const renderRefreshView = () => (
    <RefreshControl
      title="Loading" //android中设置无效
      colors={["red"]} //android
      tintColor={"red"} //ios
      titleColor={"red"}
      refreshing={isLoading}
      onRefresh={loadData} // 下拉时请求数据
    />
  )

  // 模拟下拉刷新数据
  const loadData = () => {
    setIsLoading(true)
    //模拟网络请求
    setTimeout(() => {
      //把数据反转
      let newArray = [];
      for (let i = dataArray.length - 1; i >= 0; i--) {
        newArray.push(dataArray[i]);
      }
      setIsLoading(false)
      setDataArray(newArray)
    }, 1500);
  }

  // 上拉加载组件
  const renderLoadMoreView = () => (
    <View style={styles.loadMore}>
      <ActivityIndicator
        style={styles.indicator}
        size={"large"}
        color={"red"}
        animating={true}
      />
      <Text>正在加载更多</Text>
    </View>
  )

  // 模拟上拉加载数据
  const loadMoreData = () => {
    //模拟网络请求
    setTimeout(() => {
      let newArray = [];
      for (let i = dataArray.length - 1; i >= 0; i--) {
        newArray = dataArray.concat(CITY_NAMES)
      }
      setDataArray(newArray)
    }, 1500);
  }

  // 渲染空组件
  const renderEmptyView = () => (
    <View style={styles.empty}>
      <Text>当前列表为空</Text>
    </View>
  )

  // 清空列表
  const clearList = () => {
    setDataArray([])
  }

  // key
  const keyExtractor = (item, index) => ('index' + item + index)

  return (
    <View style={styles.container}>
      <FlatList
        data={dataArray}
        keyExtractor={keyExtractor}
        // 列表项组件
        renderItem={renderItemView}
        // 下拉刷新组件
        refreshControl={renderRefreshView()}
        // 上拉加载组件,列表为空时不显示
        ListFooterComponent={dataArray.length && renderLoadMoreView()}
        // 列表为空时的组件
        ListEmptyComponent={renderEmptyView()}
        // 触底时上拉加载更多数据
        onEndReached={loadMoreData}
      />
      <Button
        onPress={clearList}
        title="清空列表"
        color="#841584"
      />
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  item: {
    backgroundColor: "#169",
    height: 200,
    margin: 15,
    justifyContent: "center",
    alignItems: "center"
  },
  text: {
    color: "red",
    fontSize: 20,
  },
  loadMore: {
    alignItems: "center"
  },
  indicator: {
    color: "red",
    margin: 10
  },
  empty: {
    alignItems: "center"
  }
});

export default App
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

# 六、图标react-native-vector-icons

这个图标库包含了AntDesign、Entypo、EvilIcons、Feather、FontAwesome、FontAwesome5、FontAwesome5Brands、Fontisto、Ionicons、MaterialCommunityIcons、MaterialIcons等多种风格的图标,使用的时候需导入对应风格的图标。

1、安装yarn add react-native-vector-icons

2、在android项目app下中的build.gradle增加库:apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

3、项目中使用

import FontAwesome from 'react-native-vector-icons/FontAwesome';
import AntDesign from 'react-native-vector-icons/AntDesign';

<AntDesign name="stepforward" size={40} color="red" />
<FontAwesome name="id-card" size={40} color="red" />
1
2
3
4
5

图标库

Last Updated: 11/1/2020, 4:08:04 PM