准备

学习React Native之前,需要了解一下其他知识,帮助你更快的理解RN
React:React中文文档
ES6:ES6入门教程

环境搭建

本人搭建的是mac+Android环境,具体过程参考:React Native中文网--搭建环境
搭建结束后,运行项目

cd AwesomeProject
react-native run-android

当在手机或模拟器上出现如下页面,则说明配置成功。
在这里插入图片描述
我在搭建结束后,出现红屏,报错如下:

Unable to load script.Make sure you're either running a metro server
(run 'react-native start' ) or that
your bundle 'index.android.bundle' is packaged correctly for release.

解决方法我记录在这里:点这里。如果遇到同样的问题可以看下。

语法简介

Hello World

先看下大概长什么样

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

export default class HelloWorldApp extends Component {
  state={ text : 'this is state.name' }
  render() {
   let pic = {
      uri: 'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'
    };
    const stateName = this.state.name;
    return (
        <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
          <Text>Hello, world!</Text>
          <Image source={pic} style={{width: 193, height: 110}} />
          <Text>{stateName}</Text>
        </View>
    );
  }
}

基本的用法Props,State等 与React是一样的,只不过基础组件不同。React里使用常规的html标签,div span等,使用了一些RN自己的原生组件Image,Text等。

样式

RN的样式与React写法页基本相同,样式名遵循了web 的css命名,只是按照JS语法要求,改成了驼峰命名。
style属性可以是简单的对象,也可以传入一个数组——在数组中位置居后的样式对象比居前的优先级更高,这样你可以间接实现样式的继承。
当样式复杂时,建议使用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 style={{color:'yellow',fontSize:'20'}}>just yellow</Text>
        <Text style={styles.red}>just red</Text>
        <Text style={styles.bigBlue}>just bigBlue</Text>
        <Text style={[styles.bigBlue, styles.red]}>bigBlue, then red</Text>
        <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',
  },
});

StyleSheet.create可以弥补编写复杂样式时,不能使用css的不便。

宽高与布局

RN中宽高可以直接通过style指定,与web不同的是,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>
    );
  }
}
弹性高宽

在组件样式中使用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>
    );
  }
}

在这里插入图片描述

flex box 布局

RN中flex box 的使用与web上css的flex布局用法基本一直,不同的是flex-direction默认值是column,而且RN中的flex只能是数字,而不是flex-grow, flex-shrink 和 flex-basis的简写。
一般来说,使用flexDirection、alignItems和 justifyContent三个样式属性就已经能满足大多数布局需求。
例如:

export default class JustifyContentBasics extends Component {
  render() {
    return (
      // 尝试把`justifyContent`改为`center`看看
      // 尝试把`flexDirection`改为`row`看看
      <View style={{
        flex: 1,
        flexDirection: 'column',
        justifyContent: 'space-between',
      }}>
        <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>
    );
  }
};

在这里插入图片描述
注意:RN中flexbox不需要一定指定flex:1,只要外层元素高度存在(可以设置height),设置flexDirection、alignItems或 justifyContent,内部元素都会根据样式规则,遵循Flexbox布局。

事件响应

RN事件响应的写法,与ReactJS类似。不同的是,移动端手势的响应要比web端复杂的多,RN通过响应者的声明周期,来响应不同触摸事件。

响应者的生命周期

我们通过两个方法去“询问”一个 View 是否愿意成为响应者:

  • View.props.onStartShouldSetResponder: (evt) => true, - 在用户开始触摸的时候(手指刚刚接触屏幕的瞬间),是否愿意成为响应者?
  • View.props.onMoveShouldSetResponder: (evt) => true, - 如果 View 不是响应者,那么在每一个触摸点开始移动(没有停下也没有离开屏幕)时再询问一次:是否愿意响应触摸交互呢?

如果View 返回 true,并开始尝试成为响应者,那么会触发下列事件之一:

  • View.props.onResponderGrant: (evt) => {} - View 现在要开始响应触摸事件了。这也是需要做高亮的时候,使用户知道他到底点到了哪里。
  • View.props.onResponderReject: (evt) => {} - 响应者现在“另有其人”而且暂时不会“放权”,请另作安排。

如果 View 已经开始响应触摸事件了,那么下列这些处理函数会被一一调用:

  • View.props.onResponderMove: (evt) => {} - 用户正在屏幕上移动手指时(没有停下也没有离开屏幕)。
  • View.props.onResponderRelease: (evt) => {} - 触摸操作结束时触发,比如"touchUp"(手指抬起离开屏幕)。
  • View.props.onResponderTerminationRequest: (evt) => true - 有其他组件请求接替响应者,当前的 View 是否“放权”?返回 true 的话则释放响应者权力。
  • View.props.onResponderTerminate: (evt) => {} - 响应者权力已经交出。这可能是由于其他 View 通过onResponderTerminationRequest请求的,也可能是由操作系统强制夺权(比如 iOS 上的控制中心或是通知中心)。
class App extends Component<Props> {
    onStartShouldSetResponder = () => {
        console.log("onStartShouldSetResponder");
        return false;
    };
    onMoveShouldSetResponder = () => {
        console.log("onMoveShouldSetResponder");
        return false;
    };
    onResponderMove = eve => {
        console.log("onResponderMove", eve);
    };
    onResponderGrant = eve => {
        console.log("onResponderGrant", eve);
    };
    onResponderReject = eve => {
        console.log("onResponderReject", eve);
    };

    pressIt = eve => {
        console.log("onPress");
    };
    render() {
        return (
            <View style={styles.container}>
                <View
                    onResponderMove={this.onResponderMove}
                    onStartShouldSetResponder={this.onStartShouldSetResponder}
                    onResponderReject={this.onResponderReject}
                    onResponderGrant={this.onResponderGrant}
                    onMoveShouldSetResponder={this.onMoveShouldSetResponder}
                    style={{
                        height: 50,
                        width: 50,
                        backgroundColor: "#ccc",
                        cursor: "pointer"
                    }}
                >
                        <Text>点我!</Text>
                </View>
              )
              }

生命周期的执行顺序,可以实际触发试试。

Touchable系列组件
  • 一般来说,你可以使用TouchableHighlight来制作按钮或者链接。注意此组件的背景会在用户手指按下时变暗。
  • 在 Android 上还可以使用TouchableNativeFeedback,它会在用户手指按下时形成类似墨水涟漪的视觉效果。
  • TouchableOpacity会在用户手指按下时降低按钮的透明度,而不会改变背景的颜色。
  • 如果你想在处理点击事件的同时不显示任何视觉反馈,则需要使用TouchableWithoutFeedback。

滚动视图与长列表

滚动视图

ScrollView是一个通用的可滚动的容器,你可以在其中放入多个组件和视图,而且这些组件并不需要是同类型的。ScrollView 不仅可以垂直滚动,还能水平滚动(通过horizontal属性来设置)。

export default class IScrolledDownAndWhatHappenedNextShockedMe extends Component {
  render() {
      return (
        <ScrollView>
          <Text style={{fontSize:96}}>Scroll me plz</Text>
          <Image source={{uri: "https://facebook.github.io/react-native/img/favicon.png", width: 64, height: 64}} />
          <Image source={{uri: "https://facebook.github.io/react-native/img/favicon.png", width: 64, height: 64}} />
          <Image source={{uri: "https://facebook.github.io/react-native/img/favicon.png", width: 64, height: 64}} />
          <Image source={{uri: "https://facebook.github.io/react-native/img/favicon.png", width: 64, height: 64}} />
          <Image source={{uri: "https://facebook.github.io/react-native/img/favicon.png", width: 64, height: 64}} />
          <Text style={{fontSize:96}}>If you like</Text>
          )
          }

ScrollView内部所有组件都会被渲染,即使这些组件在屏幕的外部。如果要显示较长滚动内容,建议使用性能更好的长列表组件:FlatList。

长列表

长列表常用的组件有FlatList和SectionList。

FlatList用法
  • 用于显示一个垂直的滚动列表,其中的元素之间结构近似而仅数据不同
  • 更适于长列表数据,且元素个数可以增删。FlatList并不立即渲染所有元素,而是优先渲染屏幕上可见的元素。
  • FlatList组件必须的两个属性是data和renderItem。data是列表的数据源,而renderItem则从数据源中逐个解析数据,然后返回一个设定好格式的组件来渲染。
export default class FlatListBasics extends Component {
  render() {
    return (
      <View style={styles.container}>
        <FlatList
          data={[
            {key: 'Devin'},
            {key: 'Jackson'},
            {key: 'James'},
            {key: 'Joel'},
            {key: 'John'},
            {key: 'Jillian'},
            {key: 'Jimmy'},
            {key: 'Julie'},
          ]}
          renderItem={({item}) => <Text style={styles.item}>{item.key}</Text>}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
   flex: 1,
   paddingTop: 22
  },
  item: {
    padding: 10,
    fontSize: 18,
    height: 44,
  },
})
SectionList的用法

SectionList与FlatList相似,只不过多了一个分组功能。

export default class SectionListBasics extends Component {
  render() {
    return (
      <View style={styles.container}>
        <SectionList
          sections={[
            {title: 'D', data: ['Devin']},
            {title: 'J', data: ['Jackson', 'James', 'Jillian', 'Jimmy', 'Joel', 'John', 'Julie']},
          ]}
          renderItem={({item}) => <Text style={styles.item}>{item}</Text>}
          renderSectionHeader={({section}) => <Text style={styles.sectionHeader}>{section.title}</Text>}
          keyExtractor={(item, index) => index}
        />
      </View>
    );
  }
}

网络请求

React Native 提供了和 web 标准一致的Fetch API,用于满足开发者访问网络的需求。
Fetch的第一个参数用来指定请求路径,可选的第二个参数,可以指定http一些请求参数。

fetch('https://mywebsite.com/endpoint/', {
  method: 'POST',
  headers: {
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    firstParam: 'yourValue',
    secondParam: 'yourOtherValue',
  }),
});

Fetch返回一个Promise对象,RN中可以使用promise这样获取数据:

fetch('https://facebook.github.io/react-native/movies.json')
    .then((response) => response.json())
    .then((responseJson) => {
      return responseJson.movies;
    })
    .catch((error) => {
      console.error(error);
    });

也可以使用async/await

    let response = await fetch(
      'https://facebook.github.io/react-native/movies.json',
    );
    let responseJson = await response.json();
    return responseJson.movies;

除了Fetch之外也可以使用frisbee和axios等其他网络库。

React Native的优缺点

RN的语法先介绍到这里,详细的用法可以看这个React Native中文网
在具体的使用之前,我们了解下RN的优劣势,来判断是否需要用它。
简单介绍下RN的优劣势,来决定是否需要用它。

优点

  1. 调试方便
    RN支持hotReload以及liveReload,在调试界面的时候非常方便,修改代码之后保存,界面就自动跟着变化。也可以使用chrome,react-devtools调试。
  2. 跨平台开发,可以写一套代码,Android和ios并行开发。
  3. 使用热更新,便于维护快速更迭的模块,跳过漫长的App审核。

缺点

  1. 长列表及动画性能不佳

    长列表快速向上滚动时,列表会出现白屏的情况,RN的动画会出现卡顿问题。这两个问题基本是因为js与native频繁通信造成的,很难从根本上解决。
  2. 版本更新快,项目中更新一次RN,对代码改动较大,很耗时。
  3. 两个平台还没有完全统一,一些控件在两个平台变现会有差异。

Flutter,比RN更好的跨平台方案?

在实际工作中RN一般作为原生开发的补充,来开发一些快速迭代的页面,但很难取代原生开发,成为App开发的最优解。Flutter作为目前炙手可热的跨平台方案,能否取代RN,Weex,还不能确定。首先我们先简单了解下Flutter。
React-Native、Weex 核心是通过 Javascript 开发,执行时需要 Javascript 解释器,UI 是通过原生控件渲染。Flutter 与用于构建移动应用程序的其它大多数框架不同,因为 Flutter 既不使用 WebView,也不使用操作系统的原生控件。 相反,Flutter 使用自己的高性能渲染引擎来绘 制 widget。
它的开发语言是Dart,Dart是面向对象的,类定义的,单继承语言,语法类似c语言,可以转义为js。

Flutter特点:

  • 快速开发
    由于Flutter选用了Dart作为其开发语言,Dart既可以是AOT(Ahead Of Time)编译,也可以是JIT(Just In Time)编译,其JIT编译的特性使Flutter在开发阶段可以达到亚秒级有状态热重载,从而大大提升了开发效率。但是Flutter不支持代码的热更新。
  • 性能优越
    使用自带的高性能渲染引擎(Skia)进行自绘,渲染速度和用户体验堪比原生。
  • UI 一致性
    Flutter 因为是自己做的渲染,因此在iOS和Android的效果基本完全一致。React Native存在将RN控件转换为对应平台原生控件的过程,存在一定的差异(如Button在iOS和Android下面显示效果不一样)。
  • App体积
    Flutter iOS空项目 30M左右,Android空项目 7M左右 (iOS需要额外集成Skia)。 React Native iOS空项目 3M左右,Android20M左右(Android会加入OKHttp导致体积增大)。

具体flutter介绍可以看下:
移动跨平台框架Flutter介绍和学习线路
如何评价 Google 的 Fuchsia、Android、iOS 跨平台应用框架 Flutter?
此外作为 Fuchsia OS 的正统 app framework,Flutter的应用前景,与Fuchsia的推广程度息息相关。可以看下知乎对Fuchsia和Flutter的讨论:
如何看待 Google 的新操作系统 Fuchsia?
Flutter 在 2019 年会有怎样的表现

总结

React Native语法和基本用法与ReactJs类似,之前用过React的,RN上手很快。虽然RN在性能上和跨平台一致性等方面算不上完善,但目前在国内主流大厂,RN仍是最常用的跨平台解决方案之一。对于日常工作中可能会用到技术,花时间学学还是很必要的。即使Flutter真的取代了RN,Flutter的React 风格,也会让熟悉RN开发的同学更好上手。

参考

React Native 中文网
react-native技术的优劣
React Native 官网


巨大星星星
58 声望1 粉丝