React Native,是一个混合移动应用开发框架,本文根据 C. Enrique Ortiz 的文章 Dissecting React Native 同时结合本身开发中的一些理解编写html
React Native 是目前流行的跨平台移动应用开发框架之一。本文概述了 React Native,以及做者在开发 React Native 应用的最佳实践经验杂谈。node
在开发移动应用时,咱们能够选择使用原生,纯基于 Web 或使用混合方法(即便用原生和基于 Web 的技术的组合)。可使用 Apache Cordova(或 Adobe PhoneGap)等开源平台编写 HTML,CSS 和 JavaScript 的跨平台混合应用。这些应用在原生 Web 视图(浏览器)中运行,因此看着就像 Web 应用,不过可使用 JavaScript 和一些 Apache Cordova 插件调用原生功能和 API。因此可使用相似于 Web 应用而非原生应用的用户界面上架到 Apple Store 或 Google Play 的应用。或者,可使用 C#,XAML 和 CSS 使用 Xamarin 来编写原生应用。对于用 HTML5,CSS 和 JavaScript 编写的非原生移动 Web 应用,还能够配套使用 jQuery Mobile 等框架。react
React Native 采用不一样的方法进行混合移动应用开发。React,React Native 采用 JavaScript 代替生成原生 UI 组件的方式来构建基于 Web 的交互界面,所以会有更丰富的 UI 体验效果,同时也可以很好地调用底层框架的UI使用。 React Native 已经成为一种流行的移动开发技术,它提供了一个使用 JavaScript 构建原生跨平台移动应用的强大框架,在须要时,咱们也可使用 Objective-C,Swift或 Java 来编写原生代码来桥接。android
使用 React Native 有利有弊。从积极的方面来讲,React Native 已经成为受支持的开源社区的热门,可使用一组技术(如 JSX,React Native 组件和 JavaScript)为 iOS 和 Android 构建移动应用。这反过来有助于在发布新版本时使 iOS 和 Android 应用保持同步。可是,React Native 仍在不断发展,当要在当前不须要 React Native 提供的功能(例如如何进行UI导航或使用地图)时决定使用哪些库时,可能会形成混淆。此外,根据应用的复杂程度,可能须要编写区别于平台的代码来解决移动平台的差别。对于复杂的应用,可能要编写自定义组件或深刻了解 iOS 和 Android(例如,出于性能缘由或将 React Native 添加到现有原生应用时所需的但不支持的UI组件)。ios
React Native 的核心语言是 JavaScript,特别是 ECMAScript 6(ES6)。所以,须要熟悉ES6的一些最新功能。此外,在开始开发第一个 React Native 应用以前,须要安装必备的工具,好比 IDE,JavaScript 库等。另外,还须要了解核心的 React Native API。git
ES6 引入了许多改进,其中一些值得一提,由于它们在最新的 React Native 框架中已经被用到:es6
没有指定参数的箭头函数:github
() => { ... }
复制代码
带一个参数,注意省略了的括号:数据库
x => { ... }
复制代码
指定几个参数:npm
(x, y) => {*
...
}
复制代码
参数定义和箭头函数的箭头必须在同一行中。
function myAsyncFunc() { return new Promise( function (resolve, reject) { : resolve(result); : if (error) { reject(error); } }); } 复制代码
比较之前的异步函数:
myAsyncFunc().then(result =\> { ··· }).catch(error =\> { ··· });
复制代码
这只是新功能的一个示例,但还有许多其余功能。关于ES6的一个很好的在线参考是 Axel Rauschmayer 的 Exploring ES6。
接下来,咱们来看看如何设置开发平台和环境。
目前来看,开发 iOS 原生应用只能用 macOS 系统。因为大多数移动应用都会涉及到 Android 和 iOS两端,所以应该考虑使用 Mac 来开发 React Native 移动应用。
在建立项目以前,你须要安装一堆东西,主要包括:
Android Studio和Xcode工具:安装最新版本以构建和发布应用。对于 Android 开发,确保为要运行的Android API 版本配置正确的模拟器。
JavaScript IDE或文本编辑器:不须要使用 Xcode 或 Android Studio 来编写 JavaScript 代码,真正须要的只是一个文本编辑器。好比使用 Atom(由 GitHub 开发),Sublime Text 或喜欢的任何优秀文本编辑器,笔者目前用的是 VSC。
JavaScript 或 React Native 包:安装一些工具来管理 React Native 软件包以及三方库,包括:
sudo npm install -g create-react-native-app
下载经常使用的第三方库。 React Nativ 提供了不少基础组件,与原生 API 使用相比会更加简单。常规的移动应用使用可能包括有相机,管理状态,并具备导航,地图,图标和复选框。这都均可以从 React Native 社区获取对用组件:
此外,能够到React Native Elements获取须要的其余UI组件元素。在开发一个完整功能的 app 时,须要用到上面提到的 UI 组件。用这个方法来安装之前的 packages :
npm install _package-name_ --save
React Native Debugger:这是一个很是棒的调试器,同时它仍是一个独立的应用,里面能用到的工具包括 React Inspector 和 Redux DevTools。配置好环境后能够链接到应用(注意:一次只能调适一个应用),能够很方便的实时查看 React Native 应用的状态,调适必备工具,如图所示:
如今,来创建一个简单应用并运行一下:
1.建立工程。用如下语句来建立,指定建立的工程路径,默认建立在当前路径下:
create-react-native-app [APPNAME] cd [APPNAME] 复制代码
注意:这一步建立了一个没有进行任何配置的 React Native app 模板。在 GitHub 项目中阅读有关 create-react-native-app 的更多信息。
安装完成后,可使用 npm 执行如下命令来运行应用:
2.启动 app。经过运行 npm run android 和 npm run ios 在模拟器上运行应用。这将在开发模式下启动应用。同时还会启动 Metro bundler,能够在修改代码后实时更新应用(在调试移动应用时这很棒)。
若是运行成功,会有这样的提示:
咱们简单快速浏览一下 React Native API ,从 React 转变过来的 React Native ,它继承了 JSX,state,props 和组件生命周期的概念。而后,它经过提供对原生UI组件和功能的支持来扩展 React。这是经过导入 React 和 React Native 功能来实现的,如清单1所示:
清单1.从 React 和 React Native 导入
import React, { Component } from **'react'** import { View, ScrollView, StyleSheet } from **'react-native**' 复制代码
导入后,咱们就能够访问 React Native 对应的组件,这些组件支持咱们所需的许多经常使用UI组件:
还有一点须要注意,React Native 还提供了不少原来不支持的功能。好比使用地图,甚至是图标和复选框。虽然底层操做系统支持,但 React Native 仍是须要安装第三方库。也就是说,React Native(包括开发工具)所支持的任何复杂或不受支持的内容都要求咱们编写其余代码或使用第三方库来承载。
常规的的 React Native 应用由许多组件组成,如图所示:
在一个应用容器中,每一个界面会包含一个或多个 View(和样式),界面跳转导航,状态管理和数据模型。还有本地存储和网络请求功能。最后但并不是最不重要的是,有不少第三方库能够扩展应用的功能。
如下介绍部分图中内容:
应用容器中包含多个 screen 和其余组件的集合。应用自己由许多 screen 组成,每一个 screen 包含多个视图,如按钮,文本输入和列表。View 是构建用户界面的最基本组件,映射到 iOS 和 Android 的原生 View 组件。
清单2是一个界面示例,包含一个列表,使用 ScrollView 实现,以及一个封装在 Checkbox UI 组件中的任务列表:
# components/TaskList/TaskList.js import React, { Component } from 'react' import { View, ScrollView, StyleSheet } from 'react-native' import { CheckBox } from 'react-native-elements' import styles from './TaskListStyle; export default class TaskList extends Component { renderTaskItem = (item, idx) => { return ( <View style={styles.row}> <Checkbox checked = {item.checked} title = {item.text} onPress = { () => onPressItem(item, idx) } /> </View> ); } render() { const {list} = this.props return ( <ScrollView style={styles.container}> {list.map( this.renderTaskItem )} </ScrollView> ) } } 复制代码
代码中列表组件须要导入各类依赖项,包括 React Native 中未找到的第三方Checkbox UI组件,能够从 React Native Elements下载该组件。
在清单2中,UI组件和样式是分开的。咱们能够建立更高级UI组件并与样式隔离开。咱们把全部组件放到一个子目录中,每一个组件都有其 component.js 和 style.js 文件,如清单3所示:
./Components
+-TaskList
+-TaskList.js
+-TaskListStyle.js
复制代码
或者,使用更通用的方法:
+-TaskList
+-index.js
+-style.js
复制代码
清单4显示了一个样式文件的示例:
# components/TaskList/TaskListStyle.js import { StyleSheet } from 'react-native'; // Define the styles export default StyleSheet.create({ container: { flex: 1, paddingLeft: 20, }, row: { flex: 1, flexDirection: 'row', alignItems: 'flex-start', }, }); 复制代码
样式须要使用 StyleSheet 组件建立,它相似于 CSS 样式表。能够查看 individual UI component 获取更多样式属性。
React Native 提供了一个 Dimensions 组件来检测屏幕尺寸:
import { Dimensions } from 'react-native'; var {height, width} = Dimensions.get('window'); 复制代码
Dimensions 可根据须要实时调整界面UI;例如,咱们能够根据须要的应用样式,在方向更改时调整UI,包括改变横向与纵向方向的文本输入字段的位置和长度。
另外咱们还可能须要处理另外一种状况的UI适配。好比须要实现 Androi d或 iOS 特有的行为的状况。包括与两端系统相关的差别,例如UI控件的呈现及使用。要解决此设计问题,咱们须要使用不一样的逻辑或样式,或者在更复杂的状况下,实现自定义组件。所以 React Native 提供了不一样的方法来检测系统,以即可以以编程方式决定要执行的操做:使用 Platform 组件甚至使用特定于平台的文件扩展名。
使用Platform 组件检测系统会返回“ios”或“android”。例如:
import {Platform} from 'react-native'; if (Platform.OS === 'ios') { } if (Platform.OS === 'android') { } 复制代码
Platform 还提供 Platform.select 和 Platform.Version,分别用于检测平台和平台版本。有关更多信息,请参阅平台文档。应该仅对较小的更改使用 Platform.O S和 Platform.select。不然,咱们将使用大量硬编码的 if 或 switch 语句,这会使写的代码更难以遵循。
若是要编写更复杂的代码或组件,则应使用特定于平台的文件扩展名。在这种方法中,使用每一个平台文件拆分代码。也就是说,若是咱们的代码不能跨平台公用,好比咱们的任务列表,那么将编写两个文件,一个用于 iOS,一个用于Android,而后让 React Native 选择对应的文件:
./Components
+-TaskList
+-TaskList.io.js
+-TaskList.android.js
+-TaskListStyle.ios.js
+-TaskListStyle.android.js
复制代码
而后,让React Native选择正确的文件:
const TaskList = require('./Components/TaskList/TaskList); 复制代码
本文未介绍 iOS 和 Android 之间究竟有什么不一样,但要注意的事项包括渲染和常规组件或UI控件使用布局。如今,若是事实证实 Android 与 iOS 应用的布局设计在主要方面有所不一样,咱们能够分别使用 index.ios.js 和 index.android.js 文件而不是 index.js 来控制主应用。这就能够彻底控制不一样平台的应用布局和流程。为了最大限度地减小重复代码,须要将其从新打包为可重用的程序。
React Native 中的导航是一个具备挑战性的领域,由于 React Native 提供的导航要么不够强大,要么仅针对 iOS。相反,有大量社区项目试图解决跨平台导航问题,其中一些项目比其余项目更受欢迎。这里只是重点阐述当下流行的 React Native Navigation。
React Native Navigation 提供了一套很是完整的 API 来注册界面组件,包括含有 tab 的应用,以及启动单个界面应用。同时它还提供了其余 API 来管理模态跳转和导航跳转,以及用于处理堆栈,处理按钮,可见性以及自定义导航器自己的低级 API。
安装最新稳定版本的 react-native-navigation(运行 npm install react-native-navigation --save)并按照其网站上的 Android 和 iOS 安装说明进行操做
另外必须经过调用 registerComponent()将惟一名称的 Navigator 注册到全部 screen 组件。注册后,就能运行包含 Tab 的应用或单个 screen 应用,如清单8所示:
import { Navigation } from "react-native-navigation"; import MainScreen from "./src/screens/MainScreen/MainScreen"; : // register the MainScreen component Navigation.registerComponent( 'MainScreen', () => 'MainScreen' ); : : // Start the (single screen)App export default () => Navigation.startSingleScreenApp({ screen: { screen: "MainScreen", title: "Main Screen" } }); 复制代码
若是是基于 tab 的使用,则须要建立更多的 screen ,由于必须指明不一样的 tab 及其关联的 screen ,tab 详细信息和样式,例如:
// Start the (tab-based) App export default () => Navigation.startTabBasedApp({ tabs: [ { screen: "screen unique name", label: "label", title: "title", icon: icons[0], navigatorButtons: { leftButtons: [ { icon: icons[2], title: "title", id: "unique id" } ] } }, { screen: "screen unique name", label: "label", title: "title", icon: icons[1], navigatorButtons: { leftButtons: [ { icon: icons[2], title: "title", id: "unique id" } ] } } ], tabsStyle: { tabBarSelectedButtonColor: "black" }, drawer: { left: { screen: "drawer screen unique name" } }, appStyle: { tabBarSelectedButtonColor: "red" }, }); 复制代码
能够根据须要经过调用相应的功能在单个和基于 tab 的 screen 之间切换。另外,请注意前一个示例中抽屉的使用。能够为单个 screen 应用和基于 tab 的应用定义抽屉侧面菜单。
在 React(和 React Native)中,props 和 state 用于控制组件。 props(properties)是用于在建立组件时自定义组件的参数。例如,Button 组件为许多属性提供支持;在清单10中,title 和 onPress 是 props。
<Button onPress={onPressHandler} title="Learn More" : /> 复制代码
属性由 React 自身设置(由父组件设置),并在组件的整个生命周期内保持固定。 State 是应用内的特殊数据,可根据须要进行更改以驱动组件行为,好比刷新数据。state 应该在构造函数中初始化,而且只能经过调用 setState 来更改。清单11显示了 state 的用法:
class MyComponent extends Component { constructor(props) { super(props); this.state = {isActive: true}; } toggleActiveState() { this.setState(previousState => { return { isActive: !previousState.isActive }; }); } render() { : } } 复制代码
清单11是一个简单 state 管理的示例,但若是应用对于许多 screen 组件和组件间状态管理更复杂,则应该利用 Redux。 Redux 是一种更先进的 state 管理方法,它提供了一个实现 state 管理及相关操做和处理程序的框架。可是使用 Redux 会增长应用的复杂性,所以应该考虑仅在真正须要它时使用它(即,对于较大的复杂应用)。
React Native 使用 AsyncStorage 为持久化存储提供支持,AsyncStorage 是一个易于使用的基于键值的异步(持久)存储系统,对整个应用而言是全局的。它很简单,由于它不须要过多的设置。在 iOS 上,它表现为一系列字典或者是文件。在Android 上,它基于 SQLite 或 RocksDB:
import {AsyncStorage} from 'react-native' : const USERNAMES_KEY = 'Usernames' const user = { name: 'John', last: 'Smith', username: 'jsmith' } // Storing the item AsyncStorage.setItem(USERNAMES_KEY, JSON.stringify(user)) : // Get item promise to retrieve the item AsyncStorage.getItem(USERNAMES_KEY).then((item) => { const userItem = JSON.parse(item) }) : 复制代码
可是 AsyncStorage 有许多限制,例如性能低下,没有索引,也没有加密。让我再说一遍:它没有加密。若是要存储大量数据,性能和加密是应用的重要因素,则应考虑使用 Realm React Native 等替代方案。 Realm React Native 是跨平台的,性能更好,支持加密,甚至还带有版本的 ListView(具备与 React Native ListView 相同的 API 签名),它在 Realm 之上进行了优化,大大改善了对本地存储的访问。 Realm 使用 static schema 的途径来提升性能。(能够看一下笔者的这篇文章来学习 Realm 的使用,传送门->移动数据库 Realm 在 React-Native 的使用详解 ):
class Usernames { static schema = { name: 'User', properties: { name: 'string', last: 'string', username: 'string', } } } let realm = new Realm({schema: [Usernames]}); realm.write(() => { realm.create('Usernames', { name: 'John', last: "Smit", username : "jsmith" }); }); 复制代码
移动应用一般是须要网络链接的应用。移动应用链接到网络以进行身份验证。 React Native 提供不一样的网络 API:
清单14使用 fetch API 从服务器获取用户数据的 json 文件:
{ "users": [ { "name": "John Smith", "username": "jsmith"}, { "name": "Matt People", "username": "mpeople"}, { "name": "Graciela Lopez", "username": "glopez"}, { "name": "Jeff Bezos", "username": "jbezos"}, ] } -- import React from 'react'; import { View, Text, FlatList, ActivityIndicator} from 'react-native'; export default class FetchUsernames extends React.Component { constructor(props){ super(props); } // dispatch the fetch once the component mounts componentDidMount(){ return fetch('https://...') .then((response) => response.json()) .then((responseJson) => { this.setState({ dataSource: responseJson.users, }, function(){ }); }) .catch((error) => { console.error(error); }); } // render the component, populating the FlatList from the // state.dataSource that was in turn populated from the JSON render(){ return( <View> <FlatList data={this.state.dataSource} renderItem={({item}) => <Text>{item.name}, {item.username}</Text>} keyExtractor={(item, index) => index} /> </View> ); } } 复制代码
关于 fetch API 和网络的一些注意事项:
本文仅是对 React Native 提供了一个高层次的观点见解。 React Native 是一个很复杂的话题。若是要深究,则须要更详细地研究本文所涵盖的每一个领域。
React Native 是一个充满活力且不断发展的框架和社区。但 React Native 并不像原生应用开发那样成熟。因此必须为Android,iOS 和 React Native 的开发保持稳定的环境,包括全部相关的开发工具。对 于React Native 中不存在的组件,能够 Google 搜索,就能够找到所需内容,请记住所选的第三方库可能会更改或维护可能会中止或被放弃,这是一个要考虑的主要风险。
React Native 并不适合全部人或每一个项目。这须要考虑建立和维护 React Native 代码,工具,开发环境和技能集的时间,精力和成本,而不是仅仅关注原生代码。去年也有大型开发组织的例子,例如 Airbnb,它们在 React Native 上投入了大量的精力和投入,但最终决定回归纯粹的原生开发。
- 韦誉翔:广州芦苇科技 APP 团队 iOS 开发工程师
- 咱们正在招募小伙伴,有兴趣的小伙伴能够把简历发到 app@talkmoney.cn,备注:来自掘金社区
- 详情能够戳这里--> 广州芦苇信息科技