通过Expo更轻松地响应本机开发

Expo是一系列工具,可让您更轻松地编写React Native应用程序。 在本教程中,我将向您展示如何使用Expo快速创建React Native应用。

使用Expo,开发人员可以创建React Native应用程序,而不会因安装和配置软件依赖项(例如Android Studio,Xcode或开发和运行React Native应用程序所需的所有其他工具)而带来的所有麻烦。

在本教程中,我将向您展示如何使用Expo创建一个简单的记忆游戏。 在此过程中,您还将学到以下内容:

  • 如何使用Expo提供的工具。 这包括CLI,SDK和Expo客户端应用程序。
  • 如何使用Expo创建React Native应用

什么是世博会?

Expo是一个用于快速开发React Native应用程序的框架。 就像PHP开发人员使用Laravel或Symphony,Ruby开发人员使用Ruby on Rails。 Expo在React Native API之上提供了一层,使它们更易于使用和管理。 它还提供了一些工具,使引导和测试React Native应用程序变得更加容易。 最后,它提供仅在安装第三方React Native组件时才可用的UI组件和服务。 所有这些都可以通过Expo SDK获得。

博览会的局限性

在继续进行之前,了解Expo的一些局限性很重要:

  1. 博览会应用程序 支持后台代码执行。 例如,这意味着您无法运行关闭应用程序时侦听位置更改的代码。
  2. Expos应用程序仅限于Expo SDK支持的本机API。 这意味着,如果您的应用具有非常特定的用例,例如与Bluetooth外设通信,则实现此类功能的唯一选择是使用纯React Native,或使用称为ExpoKit的库编写本机代码。
  3. 世博会将您锁定 他们的工具 集中 。 这意味着您不能简单地安装和使用大多数可用于React Native开发的出色工具,例如命令行工具,脚手架和UI框架。 但是,好处是Expo SDK与普通的React Native应用程序兼容,因此从Expo退出应用程序时不会有任何问题。
  4. Expo应用程序的独立二进制文件只能在线构建。 Expo提供了一个名为Exp的命令行工具。 这使开发人员可以在Expo服务器上启动构建过程。 完成后,将提供一个URL以下载.apk.ipa文件。

即使有这些限制,也要记住,Expo是一个功能齐全的框架,并为常用的Android或iOS API提供了大量支持。 这意味着它可以满足您应用程序通常需要的大多数功能。 因此,通常无需查看Expo即可实现本机功能。

应用概述

我们将要创建的应用是一款记忆游戏。 您可能熟悉这种类型的游戏-用户必须通过一次翻两张牌来找到匹配对。 这是默认屏幕的外观:

Memory game default app screen

这是所有对都打开后的样子:

Game completed

解决游戏问题后,用户可以点击“ 重置”按钮以将项目重置为其初始状态。 这使他们可以重新开始游戏。

安装博览会

与必须安装和配置Android Studio或Xcode以及其他依赖项的普通React Native不同,使用Expo只需几步即可开始开发应用程序:

  1. 下载 并安装Node.js。 Expo依靠Node.js平台来提供命令行工具和依赖项管理。
  2. iOS Android 设备 上安装Expo Client 用于在开发应用程序时预览应用程序。
  3. 安装命令行工具。 这使您可以生成新的Expo项目,启动构建过程等。 执行以下命令进行安装:
npm install exp --global

生成新的Expo App

安装完所有依赖项后,您现在可以生成一个新的Expo应用程序:

exp init MemoryGame

完成后,它将创建一个名为MemoryGame的新文件夹。 在其中导航并开始运行开发服务器:

cd MemoryGame
exp start

或者,您也可以使用Expo XDE。 这使您可以通过GUI创建和运行Expo应用。 您可以从Expo GitHub存储库下载安装程序。 当前,它仅支持Windows和Mac。 因此,如果您使用的是Ubuntu或Linux,最好暂时坚持使用命令行。

开发服务器运行后,您现在应该可以看到如下内容:

Running the dev server

这就是指向项目实时预览的QR码。 打开手机上的Expo客户端应用程序,然后使用QR扫描仪扫描代码。 此时,您现在应该可以查看默认屏幕。 每次在任何项目文件上按Control-S时,预览应自动重新加载以反映所做的更改。

您可以在其GitHub repo上找到该项目的完整源代码。 或者,如果您想尝试一下该应用程序,则可以查看演示 。 只需选择QR Code,然后使用Expo Client应用程序在手机上对其进行扫描。

编写应用程式

现在我们准备对应用程序进行编码。 让我们从一些UI组件开始,然后再返回并实现主要组件。

标头组件

标头用于显示应用程序的标题。 创建一个组件文件夹。 在其中创建一个Header.js文件,并添加以下内容:

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

export default class Header extends React.Component {
  
  render() {
    return (
      <View style={styles.header}>
        <Text style={styles.header_text}>MemoryGame</Text>
      </View>
    );
  }

}

const styles = StyleSheet.create({
  header: {
    flex: 1,
    flexDirection: 'column',
    alignSelf: 'stretch',
    paddingTop: 20,
    paddingBottom: 5,
    backgroundColor: '#f3f3f3'
  },
  header_text: {
    fontWeight: 'bold',
    fontSize: 17,
    textAlign: 'center'
  }
});

这只是一个基本的React Native组件,具有一些样式可以匹配我们应用的UI。

分数成分

接下来是显示分数的组件components / Score.js ):

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

export default class Score extends React.Component {
  
  render() {
    return (
      <View style={styles.score_container}>
        <Text style={styles.score}>{this.props.score}</Text>
      </View>
    );
  }

}

const styles = StyleSheet.create({
  score_container: {
    flex: 1,
    alignItems: 'center',
    padding: 10
  },
  score: {
    fontSize: 40,
    fontWeight: 'bold'
  }
});

同样,只是一个具有文本视图和一些基本样式的简单显示组件。

卡组件

卡组件( components / Card.js )将显示卡。 这些卡使用来自Expo Vector图标集的图标 。 这是使用Expo时开箱即用的功能之一:它包含来自FontAwesomeEntypoIonicons等图标集的图标。

在下面的代码中,您可以看到我们仅使用FontAwesome。 它具有我们想要显示卡的默认状态的图标:问号。 稍后您将在主要应用程序组件中看到,我们还将使用Entypo和Ionicons中的图标。 对这些图标源的引用将传递到此组件,因此无需在此处指定它们:

import React from 'react';
import { StyleSheet, Text, View, TouchableHighlight } from 'react-native';
import { FontAwesome } from '@expo/vector-icons'; // use FontAwesome from the expo vector icons

render()方法内部,如果打开卡,我们仅使用作为道具传递的源和图标。 默认情况下,它将仅显示FontAwesome中的问号图标。 但是,如果卡片是打开的,它将使用作为道具传递的图标来源,图标和颜色。

每个卡都可以被点击。 点击后,将运行clickCard()函数,该函数也将通过props传递。 稍后您将看到函数的功能,但是现在,仅知道它会更新状态以显示卡上的图标:

export default class Card extends React.Component {

  render() {
    
    let CardSource = FontAwesome; // set FontAwesome as the default icon source
    let icon_name = 'question-circle';
    let icon_color = '#393939';
    
    if(this.props.is_open){
      CardSource = this.props.src;
      icon_name = this.props.name;
      icon_color = this.props.color;
    }
    
    return (
      <View style={styles.card}>
        <TouchableHighlight onPress={this.props.clickCard} activeOpacity={0.75} underlayColor={"#f1f1f1"}>
          <CardSource 
            name={icon_name} 
            size={50} 
            color={icon_color} 
          />
        </TouchableHighlight>   
      </View>
    );
  }
}

不要忘记添加样式:

const styles = StyleSheet.create({
  card: {
    flex: 1,
    alignItems: 'center'
  },
  card_text: {
    fontSize: 50,
    fontWeight: 'bold'
  }
});

帮手

我们还将使用一个名为shuffle()的辅助函数。 这使我们可以按随机顺序对纸牌阵列进行排序,以便每次重置游戏时它们的顺序都会不同:

Array.prototype.shuffle = function() {
  var i = this.length, j, temp;
  if(i == 0) return this;
  while(--i){
   j = Math.floor(Math.random() * (i + 1));
   temp = this[i];
   this[i] = this[j];
   this[j] = temp;
  }
  return this;
}

主要成分

主要组件( App.js )包含主要的应用程序逻辑,并将所有内容组合在一起。 首先包括我们将要使用的React和Expo软件包。 这次,我们使用来自Expo矢量图标的所有图标源:

import React from 'react';
import { StyleSheet, View, Button } from 'react-native';
import { Ionicons, FontAwesome, Entypo } from '@expo/vector-icons';

接下来,包括我们之前创建的组件和帮助器:

import Header from './components/Header';
import Score from './components/Score';
import Card from './components/Card';

import helpers from './helpers';

在构造函数内部,我们首先创建代表唯一卡的数组。 src是图标的来源, name是图标的名称(如果要使用其他图标,可以在GitHub上找到名称),并且color自然是图标的颜色:

export default class App extends React.Component {

  constructor(props) {
    super(props);
    // bind the functions to the class
    this.renderCards = this.renderCards.bind(this);
    this.resetCards = this.resetCards.bind(this);
    
    // icon sources
    let sources = {
      'fontawesome': FontAwesome,
      'entypo': Entypo,
      'ionicons': Ionicons
    };

    // the unique icons to be used
    let cards = [
      {
        src: 'fontawesome',
        name: 'heart',
        color: 'red'
      },
      {
        src: 'entypo',
        name: 'feather',
        color: '#7d4b12'
      },
      {
        src: 'entypo',
        name: 'flashlight',
        color: '#f7911f'
      },
      {
        src: 'entypo',
        name: 'flower',
        color: '#37b24d'
      },
      {
        src: 'entypo',
        name: 'moon',
        color: '#ffd43b'
      },
      {
        src: 'entypo',
        name: 'youtube',
        color: '#FF0000'
      },
      {
        src: 'entypo',
        name: 'shop',
        color: '#5f5f5f'
      },
      {
        src: 'fontawesome',
        name: 'github',
        color: '#24292e'
      },
      {
        src: 'fontawesome',
        name: 'skype',
        color: '#1686D9'
      },
      {
        src: 'fontawesome',
        name: 'send',
        color: '#1c7cd6'
      },
      {
        src: 'ionicons',
        name: 'ios-magnet',
        color: '#d61c1c'
      },
      {
        src: 'ionicons',
        name: 'logo-facebook',
        color: '#3C5B9B'
      }
    ];

    // next: add code creating the clone and setting the cards in the state
  }

}

请注意,我们不是直接为每个对象将src指定为FontAwesomeEntypoIonicons ,而是使用sources对象中使用的属性名称。 这是因为我们将需要创建卡阵列的副本,以便每个卡都具有一对。 使用诸如slice()类的数组方法创建副本将创建该数组的副本,但是问题在于,一旦在副本或原始对象中修改了各个对象,两个数组也会被修改。

这将我们带到下面的解决方案,该解决方案是通过将cards数组转换为字符串,然后对其进行解析以将其转换回数组来创建一个全新的对象。 这就是我们使用字符串的原因,因为函数无法转换为字符串。 然后,我们将两者结合起来以提供一个数组,其中包含我们需要的所有卡:

let clone = JSON.parse(JSON.stringify(cards)); // create a completely new array from the array of cards

this.cards = cards.concat(clone); // combine the original and the clone

接下来,遍历该数组并为每个数组生成唯一的ID,设置图标源,然后默认将其设置为关闭状态:

// add the ID, source and set default state for each card
this.cards.map((obj) => {
  let id = Math.random().toString(36).substring(7);
  obj.id = id;
  obj.src = sources[obj.src];
  obj.is_open = false;
});

随机对卡进行排序并设置默认状态:

this.cards = this.cards.shuffle(); // sort the cards randomly

// set the default state
this.state = {
  current_selection: [], // this array will contain an array of card objects which are currently selected by the user. This will only contain two objects at a time.
  selected_pairs: [], // the names of the icons. This array is used for excluding them from further selection
  score: 0, // default user score
  cards: this.cards // the shuffled cards
}

render()方法呈现标题,卡片,得分和用于重置当前游戏的按钮。 它使用renderRows()函数呈现单个卡片行。 屏幕将有六行,每行包含四张卡:

render() {
  return (
    <View style={styles.container}>
      <Header />
      <View style={styles.body}>
        { 
          this.renderRows.call(this) 
        }
      </View>
      <Score score={this.state.score} />
      <Button
        onPress={this.resetCards}
        title="Reset"
        color="#008CFA" 
      />
    </View>
  );
}

这是renderRows()函数的代码。 这使用了getRowContents()函数,该函数负责创建一个数组,每个数组包含四个项目。 这允许我们渲染每一行,然后使用另一个函数为map()函数的每次迭代渲染卡片:

renderRows() {
 
  let contents = this.getRowContents(this.state.cards);
  return contents.map((cards, index) => {
    return (
      <View key={index} style={styles.row}>
        { this.renderCards(cards) }
      </View>
    );
  });
 
}

这是getRowContents()函数:

getRowContents(cards) {
  let contents_r = [];
  let contents = [];
  let count = 0;
  cards.forEach((item) => {
    count += 1;
    contents.push(item);
    if(count == 4){
      contents_r.push(contents)
      count = 0;
      contents = [];
    }
  });

  return contents_r;
}

接下来是renderCards()函数。 这接受卡对象数组,并通过Card组件渲染它们。 我们需要做的就是将每个卡片对象的各个属性作为道具传递。 然后,如您在Card组件的代码中所看到的,它用于呈现正确的图标。 clickCard()函数也作为道具传递。 卡ID传递给该功能,以便可以识别和更新唯一卡:

renderCards(cards) {
  return cards.map((card, index) => {
    return (
      <Card 
        key={index} 
        src={card.src} 
        name={card.name} 
        color={card.color} 
        is_open={card.is_open}
        clickCard={this.clickCard.bind(this, card.id)} 
      />
    );
  });
}

clickCard()函数内部,我们获取所选卡的详细信息,并检查是否应对其进行进一步处理:

clickCard(id) {
  let selected_pairs = this.state.selected_pairs;
  let current_selection = this.state.current_selection;
  let score = this.state.score;
  
  // get the index of the currently selected card
  let index = this.state.cards.findIndex((card) => {
    return card.id == id;
  });

  let cards = this.state.cards;
  
  // the card shouldn't already be opened and is not on the array of cards whose pairs are already selected
  if(cards[index].is_open == false && selected_pairs.indexOf(cards[index].name) === -1){

    // next: add code for processing the selected card

  }

}

现在,让我们填写用于处理所选卡的代码。

首先,我们打开卡片并将其添加到当前选择的卡片阵列中:

cards[index].is_open = true;
    
current_selection.push({ 
  index: index,
  name: cards[index].name
});

// next: add code for determining whether the user has selected the correct pair or not

当前选择的卡阵列中有两项时,我们将检查图标名称是否相同。 如果是,则表示用户已选择正确的对。 如果它们不相同,那就是不正确的对。 在这种情况下,我们将关闭选择的第一张卡,然后在关闭第二张卡之前增加一些延迟。 (通过这种方式,用户可以在卡片图标恢复为关闭状态之前看到它。)

if(current_selection.length == 2){
  if(current_selection[0].name == current_selection[1].name){
    score += 1; // increment the score
    selected_pairs.push(cards[index].name); 
  }else{
    cards[current_selection[0].index].is_open = false; // close the first
    
    // delay closing the currently selected card by half a second.
    setTimeout(() => {
      cards[index].is_open = false;
      this.setState({
        cards: cards
      });
    }, 500);
  }

  current_selection = [];
}

// next: add code for updating the state

在click事件处理程序中,我们需要做的最后一件事是更新状态以反映UI中的更改:

this.setState({
  score: score,
  cards: cards,
  current_selection: current_selection
});

一个相关的功能是重置事件处理程序。 轻按重置按钮后,我们只需关闭所有卡并重新排列即可恢复默认状态。

resetCards() {
  // close all cards
  let cards = this.cards.map((obj) => {
    obj.is_open = false;
    return obj;
  });

  cards = cards.shuffle(); // re-shuffle the cards
  
  // update to default state
  this.setState({
    current_selection: [],
    selected_pairs: [],
    cards: cards,
    score: 0
  });
}

最后,我们将添加一些基本样式以使我们的应用看起来不错。

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignSelf: 'stretch',
    backgroundColor: '#fff'
  },
  row: {
    flex: 1,
    flexDirection: 'row'
  },
  body: {
    flex: 18,
    justifyContent: 'space-between',
    padding: 10,
    marginTop: 20
  }
});

测试应用

由于您的Expo开发服务器一直在运行,因此每次更改都应通过实时重新加载推送到您的移动设备上。 试用该应用程序,并确保其可以正常运行。

结论

而已! 在本教程中,您学习了如何使用Expo XDE快速连接React Native应用程序。 Expo是开始开发React Native应用程序的一种非常好的方法,因为它消除了安装很多软件的需求,而这通常会使人们感到沮丧,特别是对于初学者。 它还提供了一些工具,可在开发过程中非常轻松地预览应用程序。 如果您想了解更多信息,请务必查看世博网站上提到的资源

翻译自: https://code.tutsplus.com/tutorials/easier-react-native-development-with-expo--cms-30546

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值