个性化阅读
专注于IT技术分析

深入研究React Native(初学者教程)

本文概述

当React Native宣布时, 最初的反应是非常积极的。传统上, 当我们考虑移动空间中的Web技术时, 会想到诸如Apache Cordova之类的东西, 这使我们可以将网站或Web应用程序打包为移动平台的应用程序。在这个初学者的教程中, 我们将了解React Native的体系结构, React Native背后的原理以及它与同一领域中其他解决方案的区别。到本文结尾, 我们将把React” Hello World”应用程序转换为React Native应用程序。

首先让我们说React Native是一项相对较新的技术。自2015年3月起, 该应用已于2015年3月正式发布, 并于当年年初在其内部进行了内部测试。 “罗马不是一天建成的”这一说法通常也适用于技术。诸如grunt之类的工具和诸如Node.js之类的平台历经数年才逐渐成熟。在网络世界中, 事情发展很快, 并且每天都有大量的框架, 软件包和工具出炉, 开发人员往往对此持怀疑态度, 不想仅仅因为意识到他们最终陷入了供应商锁定的境地。我们将探讨使React Native如此特别的原因, 为什么它是一项值得涉足的技术, 并涵盖一些并非所有独角兽和彩虹的实例。

引擎盖下

在谈论移动网络技术时, 可用的解决方案通常属于以下类别之一。

在移动Web浏览器中捆绑Web应用程序

该Web应用程序位于移动浏览器(通常称为WebView)中。无需任何重大重构, 网站或Web应用程序即可在移动设备上运行。为了获得完整的用户体验, 我们可能需要考虑移动浏览器事件, 例如点击或收听设备方向的更改以及较小的屏幕, 但是我们的移动版本可以毫不费力地工作。 Cordova / PhoneGap是该类别中最受欢迎的选项。不幸的是, 此选项有很大的缺点:在某些情况下, 使用Cordova开发的应用程序比本地应用程序要慢得多, 尤其是对于图形繁多的应用程序。在其他情况下, 移动操作系统实际上并未提供WebView中移动浏览器中可用的所有功能。用户体验也可能与本机应用程序不同。这可能是由于应用程序或平台本身引起的。此问题的范围可能从滚动条感觉不一样到点按元素时出现明显的延迟。

编译为本机技术

完全不同的解决方案是最后创建本机代码库。这是通过将原始源代码转换为另一种编程语言来实现的。我们将本机性能换成具有一些不确定性的抽象层。在采用闭源解决方案的情况下, 我们甚至无法确定到底发生了什么以及我们正在处理哪种黑匣子。在其他情况下, 我们不确定下一次移动操作系统更新将破坏我们的代码多少, 以及何时提供修补程序或更新。此类别的一个流行示例是Haxe。

使用JavaScript层

在这里, 我们使用移动环境的JavaScript引擎并在那里执行我们的JavaScript。本机控件映射到JavaScript对象和函数, 因此当我们调用名为fancyButtonRightHere()的函数时, 屏幕上会出现一个按钮。 NativeScript或Appcelerator Titanium是此类的知名示例。

React Native可以归为第三类。对于iOS和Android版本, React Native在后台使用JavaScriptCore, 这是iOS上的默认JavaScript引擎。 JavaScriptCore也是Apple Safari浏览器中的JavaScript引擎。 OS X和iOS开发人员实际上可以直接与之交互。

一个巨大的区别是React Native在单独的线程中运行JavaScript代码, 因此用户界面不会阻塞, 动画应如丝般流畅。

React是关键功能

值得注意的是, React Native中的” React”并不是偶然放置的。对于React Native, 我们需要了解React到底提供了什么。尽管这些代码示例是为在浏览器中运行而定制的, 但以下概念在React和React Native中的工作原理相同。

单一渲染入口点

当我们看一个简单的React组件时, 我们可能会注意到的第一件事是该组件具有render函数。实际上, 如果在组件内部没有定义渲染函数, React会引发错误。

var MyComponent = function() {
  this.render = function() {
    // Render something here
  };
};

特殊的是, 我们在这里不会弄乱DOM元素, 但是会返回一个基于XML的结构, 该结构表示将在DOM中呈现的内容。这种基于XML的构造称为JSX。

var MyComponent = function() {
  this.render = function() {
    return <div className="my-component">Hello there</div>;
  };
};

特殊的JSX转换器接收所有看起来像XML的代码并将其转换为函数。转换后的组件如下所示:

var MyComponent = function() {
  this.render = function() {
     return React.createElement("div", {
       className: "my-component"
     }, "Hello there");
  };
};

最大的优点是, 通过快速查看组件, 我们总是知道它应该做什么。例如, <FriendList />组件可能会呈现许多<Friend />组件。除了在render函数内部, 我们无法在其他任何地方渲染组件, 因此永远不会担心我们不知道渲染组件的确切来源。

单向数据流

为了构建组件的内容, React提供了一些属性或道具。与XML属性类似, 我们将道具直接传递给组件, 然后可以在构造的组件内部使用道具。

var Hello = function(props) {
  this.render = function() {
    return <div className="my-component">Hello {props.name}</div>;
  };
};

var Greeter = function() {
  this.render = function() {
    return <Hello name="there" />
  }
};

这导致我们的组件处于树状结构中, 并且仅允许在构造子元素时传递数据。

重新呈现更改

除了道具外, 组件还可以具有内部状态。该行为最突出的示例是单击计数器, 该计数器在按下按钮时会更新其值。点击次数本身将保存在该状态中。

prop和状态更改中的每一个都会触发组件的完全重新渲染。

虚拟dom

现在, 当道具或状态发生变化时重新渲染所有内容时, React自身的表现如何如此出色?神奇的成分是”虚拟DOM”。每当需要重新呈现某些内容时, 都会生成更新的DOM的虚拟表示。虚拟DOM由在组件树之后建模的元素的轻量表示组成, 使得生成它们的过程比生成实际DOM元素的效率更高。在将更改应用到实际DOM之前, 要进行检查以确定更改发生在组件树中的确切位置, 创建差异, 并且仅应用那些特定的更改。

这个React Native教程入门

为了为React Native开发, 初学者需要具备某些先决条件。由于iOS是第一个受支持的平台, 并且在本教程中我们将介绍该平台, 因此我们需要macOS和Xcode(至少是6.3版)。还需要Node.js。帮助的是通过带有brew install watchman的Brew软件包管理器安装Watchman。尽管不一定需要这样做, 但在处理我们的React Native项目中的许多文件时会有所帮助。

React Native:最安全的移动应用开发框架。

鸣叫

要安装React Native, 我们只需要使用npm install -g react-native-cli安装React Native命令行应用程序。调用react-native命令可以帮助我们创建一个新的React Native应用程序。运行react-native init HelloWorld会创建一个名为HelloWorld的文件夹, 可以在其中找到样板代码。

终端动画展示了如何设置React Native的" Hello World"应用程序。

转换React应用

以React为主要功能以及来自React库的核心原理, 让我们看一下将最小的React” Hello World”应用程序转换为React Native应用程序所需要的内容。

我们在此代码示例中使用某些ES2015功能, 特别是类。坚持使用React.createClass或使用类似于流行的模块模式的函数形式是完全可行的。

var React = require('react');

class HelloThere extends React.Component {
  clickMe() {
    alert('Hi!');
  }
  render() {
    return (
      <div className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</div>
    );
  }
}

React.render(<HelloThere name="Component" />, document.getElementById('content'));

步骤1:拥抱CommonJS模块

在第一步中, 我们需要进行更改, 要求React模块改为使用react-native。

var React = require('react-native');

class HelloThere extends React.Component {
  clickMe() {
    alert('Hi!');
  }
  render() {
    return (
      <div className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</div>
    );
  }
}

React.render(<HelloThere name="Component" />, document.getElementById('content'));

开发React Web应用程序时通常是工具管道的一部分, 这是React Native不可或缺的一部分。

步骤2:没有DOM

毫不奇怪, 移动设备上没有DOM。以前使用<div />的地方, 我们需要使用<View />;使用<span />的地方, 我们这里需要的组件是<Text />。

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

class HelloThere extends React.Component {
  clickMe() {
    Alert.alert(‘hi!');
  }
  render() {
    return (
      <View className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</View>
    );
  }
}

React.render(<HelloThere name="Component" />, document.getElementById('content'));

虽然直接将文本直接放在<div />元素中非常方便, 但是在本机世界中, 文本不能直接放在<View />中。为此, 我们需要插入一个<Text />组件。

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

class HelloThere extends React.Component {
  clickMe() {
    Alert.alert(‘hi!');
  }
  render() {
    return (
      <View className="box" onClick={this.clickMe.bind(this)}>
        <Text>Hello {this.props.name}. Please click me.</Text>
      </View>
    );
  }
}

React.render(<HelloThere name="Component" />, document.getElementById('content'));

步骤3:内联样式是必经之路

React Native允许我们使用Flexbox建模, 而不是搞乱我们在网络世界中非常熟悉的float和inline-block。有趣的是, React Native不使用CSS。

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

class HelloThere extends React.Component {
  clickMe() {
    Alert.alert(‘hi!');
  }
  render() {
    return (
      <View style={styles.box} onClick={this.clickMe.bind(this)}>
        <Text>Hello {this.props.name}. Please click me.</Text>
      </View>
    );
  }
}

var styles = StyleSheet.create({
  box: {
    borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100
  }
});

React.render(<HelloThere name="Component" />, document.getElementById('content'));

使用内联样式对于初学者来说似乎很困惑。这类似于React开发人员在面对JSX并先前使用诸如Handlebars或Jade之类的模板引擎时必须经历的过渡。

我们的想法是, 我们没有使用CSS的全局样式表。我们直接在组件级别声明样式表, 因此我们拥有查看组件的功能, 其创建的布局以及所应用的样式所需的所有信息。

import React from ‘react';
import {Text} from ‘react-native';

var Headline = function(props) {
  this.render = () => <Text style={headlineStyle.text}>{props.caption}</Text>;
};

var headlineStyles = StyleSheet.create({
  text: {
    fontSize: 32, fontWeight: 'bold'
  }
});

module.exports = Headline;

步骤4:处理事件

等效于单击网页就是在移动设备上点击一个元素。让我们更改代码, 以便在点击元素时弹出”警报”。

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

class HelloThere extends React.Component {
  clickMe() {
    Alert.alert("Hi!")
  }
  render() {
    return (
      <TouchableOpacity onPress={this.clickMe()}>
        <View style={styles.box}>
          <Text>Hello {this.props.name}. Please click me.</Text>
        </View>
      </TouchableOpacity>
    );
  }
}

var styles = StyleSheet.create({
  box: {
    borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100
  }
});

React.render(<HelloThere name="Component" />, document.getElementById('content'));

除了需要在<View />组件上直接提供事件之外, 我们还需要显式地使用触发事件的元素, 在本例中为按下事件时的触摸事件。有不同类型的可触摸组件, 每种组件都提供不同的视觉反馈。

步骤5:跨平台自定义行为

通过访问Platform.OS的值, 可以检测React Native应用程序在哪个平台上运行。假设在上面的示例中, 我们希望根据运行的平台显示不同的警报消息。我们可以这样做:

...
clickMe() {
  var message = ‘';
  if(Platform.OS == ‘ios') {
    message = ‘Welcome to iOS!';
  } else if(Platform.OS == ‘android') {
    message = ‘Welcome to Android!';
  }   
  Alert.alert(message);
}
...

另外, 还可以使用select方法, 该方法提供了类似于开关的语法:

…
clickMe() {
  Alert.alert(Platform.select({
    ios: ‘Welcome to iOS!', android: ‘Welcome to Android!'
  })
  );
}
...

步骤6:自定义字体和react-native链接

为了添加自定义字体, 我们需要跳过一些箍。首先, 请确保字体全名和字体的文件名相同:iOS将使用字体的全名来选择字体, 而Android使用文件名。

因此, 如果字体的全名是myCustomFont, 请确保字体的文件名是myCustomFont.ttf。

之后, 我们需要创建一个资产文件夹并将npm指向它。为此, 我们可以先在应用程序根目录中的Assets / fonts下创建文件夹。任何其他目录都可以, 但是这是用于fonts目录的常规名称。

我们可以通过在React的npm集成部分rnpm下添加Assets属性来告诉npm我们在哪里拥有资产:

"rnpm": {
  "Assets": [
    "./assets/fonts/"
  ]
}

完成所有这些操作后, 我们终于可以运行react-native链接。这样会将字体复制到正确的目录, 并将必要的xml添加到iOS上的info.plist。

完成后, 我们可以通过在任何样式表中按全名引用字体来使用它。让我们在Text元素上使用它:

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

class HelloThere extends React.Component {
  clickMe() {
    Alert.alert("Hi!")
  }
  render() {
    return (
      <TouchableOpacity onPress={this.clickMe()}>
        <View style={styles.box}>
          <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text>
        </View>
      </TouchableOpacity>
    );
  }
}

var styles = StyleSheet.create({
  box: {
    borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100
  }, message: {
    fontFamily: 'myCustomFont'
  }
});

React.render(<HelloThere name="Component" />, document.getElementById('content'));

步骤7:四处移动

React Native使用与Flexbox相同的规则来布置组件。假设我们想将按钮放置在屏幕底部:让我们用容器视图包装TouchableOpacity:

<View style={styles.container}>
    <TouchableOpacity onPress={this.clickMe.bind(this)}>
        <View style={styles.box}>
            <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text>
        </View>
    </TouchableOpacity>
</View>

现在, 我们来定义容器样式以及其他已经定义的样式:

container: {
    flex: 1, justifyContent: 'center', alignItems: 'center'
  }

让我们集中讨论justifyContent和alignItems。这两个属性控制组件分别沿其主轴和副轴对齐的方式。默认情况下, 主轴是垂直轴, 辅助轴是水平轴(你可以通过将flexDirection属性设置为row来更改它)。

justifyContent有六个可能的值, 可以将其设置为:

  • flex-start将所有元素放置在组件边界框的开始处。
  • flex-end将所有元素放在最后。
  • center会将所有元素定位在边界框的中心。
  • 周围的空间将均匀地散布组件, 并将组件在其创建的边界框中居中。
  • 均匀分布的空间也将均匀地散布组件, 但是它将尝试在组件和其他边界之间保留相等的空间。
  • 通过保持相邻组件之间的间距相等, 间距将扩展组件。

alignItems可以设置为四个可能的值:flex-start, flex-end, center和Stretch。前三个行为的行为与对justifyContent所做的行为相同, 而Stretch会将组件设置为占据沿轴的所有可用空间, 以便将轴完全填充。

因此, 由于我们希望TouchableOpacity显示在底部并沿水平轴居中, 因此可以像这样更改样式:

container: {
  flex: 1, justifyContent: 'flex-end', alignItems: 'center'
}

可以在此处和此处找到关于justifyContent和alignItems值的更多信息。

步骤8:注册应用程序

在使用针对浏览器的React开发时, 我们只需要定义一个安装点, 调用React.render, 然后让React发挥作用即可。在React Native中, 这有点不同。

import React from ‘react';
import {View, Text, StyleSheet, TouchableOpacity, Alert, Platform} from ‘react-native';

class HelloThere extends React.Component {
  clickMe() {
    Alert.alert(Platform.select({
      ios: ‘Welcome to iOS!', android: ‘Welcome to Android!'
    }));
  }

  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity onPress={this.clickMe.bind(this)}>
          <View style={styles.box}>
            <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text>
          </View>
        </TouchableOpacity>
      </View>
    );
  }
}

var styles = StyleSheet.create({
  container: {
    flex: 1, justifyContent: 'flex-start', alignItems: 'center'
  }, box: {
    borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100
  }, message: {
    fontFamily: 'myCustomFont'
  }
});

var MainComponent = function() {
  this.render = function() {
    return <HelloThere name="Component" />;
  }
};

AppRegistry.registerComponent('MainComponent', function() {
  return MainComponent;
});

我们必须为对象的Objective-C端注册组件, 这是使用AppRegistry对象完成的。我们提供的名称必须与Xcode项目中的名称匹配。

我们的Hello World React Native应用程序比其Web应用程序具有更多的代码行, 但是, 另一方面, React Native进一步分离了关注点, 尤其是因为样式是由组件定义的。

附带说明一下, 我们不应该在render方法中将clickMe方法重新绑定到此上下文, 尤其是当我们的React(Native)应用程序变得更加复杂时。它会在每次渲染调用时重新绑定该方法, 这可能会变得很多。另一种方法是将方法绑定到构造函数中。

运行应用程序

要运行该应用程序, 我们需要从最后一步开始, 用转换后的应用程序的代码片段替换index.ios.js文件的内容。然后, 我们只需要打开Xcode项目并按大”运行”按钮。首先, 一个终端将与React Native服务器一起打开, 然后将出现模拟器窗口。 React Native服务器创建一个包, 然后本机应用程序将获取该包。这样可以实现类似于Web开发的快速开发周期, 其中的更改几乎可以立即在模拟器中反映出来。

对于Android, 只需将以下内容添加到你的package.json文件中的脚本下:

"android-linux": "react-native bundle --platform android --dev false --entry-file index.ios.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/
main/res && react-native run-android"

然后运行npm运行android-linux。确保事先存在android / app / src / main / assets目录。

终端弹出后, 我们的应用程序将显示在模拟器中。按CMD + D将显示开发菜单。单击该框将显示警报。 iOS版本:

带有警告弹出窗口的苹果手机

Android会呈现如下内容:

带有提醒弹出式窗口的"嗨"的Android手机。

对于分发, 拥有一个指向本地开发服务器的应用程序对我们而言并不可行。因此, 当React Native服务器未与命令react-native bundle运行时, 我们可以创建要使用的bundle。在这种情况下, 我们需要更新AppDelegate的didFinishLaunchingWithOptions方法以使用脱机捆绑包。

Github上也提供了此示例应用程序。

使用React Native

值得一提的另一件事是, 我们不仅将React概念和JavaScript用于我们的移动应用程序, 而且一些Web开发人员惯用的工作流也可以在React Native中使用。来自Web开发时, 我们习惯于开发人员工具, 检查元素和实时重载。

React Native的工作方式是将所有JavaScript文件打包在一起。该捆绑包可以从服务器提供, 也可以与应用程序捆绑在一起。第一个对于在Simulator中进行开发非常有用, 因为我们可以启用实时重载。 React提供的开发人员菜单绝不像Chrome开发人员工具那么强大, 但是它通过使用Chrome(或Safari)开发人员/调试器工具进行实时重新加载和调试, 提供了非常类似于Web的开发人员体验。

Web开发人员熟悉JSFiddle或JSBin, 这是用于进行快速Web测试的在线游乐场。有一个类似的环境, 使我们可以在Web浏览器中试用React Native。

React Native:可靠的现代选择

我最初建议对React Native使用更谨慎的方法。今天, 这是一个成熟而坚实的选择。

React的一大优点是它不代表你的工作流程, 因为它只代表视图层。你是否要定义自己的Grunt管道?还是你更愿意使用Webpack?你是否将Backbone.js用于模型需求?还是要使用普通的JavaScript对象?所有这些问题的答案完全取决于你, 因为React不会对这些选择施加任何限制。就像官方网站上所说的那样:”由于React对你的其余技术堆栈没有任何假设, 因此很容易在现有项目的一个小功能中进行尝试。”

在某种程度上, React Native也是如此。移动开发人员可以将React Native作为其应用程序的一部分进行集成, 利用Web启发式的开发工作流程, 并在需要时选择大规模集成该库。

无论如何, 可以肯定的是:React Native不会消失。 Facebook在应用程序商店中拥有多个由React Native支持的应用程序, 因此拥有巨大的利益。 React Native周围的社区很大, 并且还在继续增长。

相关:构建QR扫描仪:React Native Camera教程

赞(0)
未经允许不得转载:srcmini » 深入研究React Native(初学者教程)

评论 抢沙发

评论前必须登录!