作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Alex是一名拥有多年Android和iOS经验的高级移动开发人员. 他为几家银行开发了移动银行套件和应用程序.
几年前,我的一个同事告诉我React Native. 我很怀疑. 我认为它只是另一个跨平台框架,永远不会在现实生活中工作-我几乎不知道我错了.
几年过去了,React Native技能变得非常抢手. 既然我已经有一段时间没有学到新的东西了,我想为什么不试试呢? 今天,我是React Native的忠实拥护者.
Cons:
Pros:
我可以继续说下去,但让我们停在这里,进入这篇博客文章的主题. 在这篇文章中,我将创建四个基于React Native的Android应用:
正如我上面提到的,我们不可能使用Android Studio进行React Native开发. 我们需要一个替代品. React Native可以在任何现代文本编辑器中开发(Atom, VS Code, Sublime Text, Brackets, etc.),但由于我们是带着Android Studio体验而来,我最喜欢的是由同一家公司开发的WebStorm. 虽然WebStorm是付费应用程序(每年129美元),你可以安装它的早期访问版本. WebStorm的EAP版本是免费的,而且非常稳定. 如果你喜欢一个完全免费的编辑器,那就去VS Code吧. 微软甚至为此开发了令人惊叹的React Native插件,而且效果非常好.
前提条件:Android SDK, Node和React Native安装在您的计算机上.
有两种方法可以创建新的React Native项目.
react-native init AwesomeToptalProject
create-react-native-app AwesomeToptalProject
If you use create-react-native-app
,创建的项目将与世博会一起启动. 我就不细讲了, but basically, 这意味着你不需要安装Xcode就可以在iOS上运行应用. 通过博览会让客户端始终保持最新状态也更容易.Io的功能和其他一些特性. 但是你不能添加本地代码. Thus, 如果你正在开发一个特定的功能, 你可能需要从expo中弹出一个应用程序,转而使用一个常规的React Native项目.
我将使用第一种方法.
让我们运行这个项目. 首先,打开模拟器或连接设备. 如果您使用WebStorm GUI创建项目,那么您所需要做的就是选择一个配置. 在WebStorm的右上角, 单击Run按钮左侧的下拉菜单, choose Android, 并单击“运行”或“调试”. 如果您使用Terminal创建项目, 你可以添加一个新的React Native配置或者使用Terminal运行它:
cd AwesomeToptalProject
react-native运行android
如果一切正常,您将看到以下屏幕:
项目中值得注意的项目有:
让我们在项目的根目录下创建一个名为src的文件夹,并移动App.js there. 你需要更新index.. js导入以匹配新的应用程序.js location.
import App from './src/App';
删除App内所有内容.Js并粘贴以下代码:
从“React”中导入React;
从'react-native'中导入{Text};
导出默认类App扩展React.Component {
render() {
return (
Hello TopTal
);
}
}
我们粘贴的代码非常简单. We created a class App
(child of React.Component
) which overrides render()
方法和返回 Text
component. React.Component
使用JSX构建UI的基类是什么. The export default
修饰符创建类 public
.
现在我们准备开始设计我们的布局.
Flexbox
is similar to LinearLayout
, but Flexbox
goes way beyond LinearLayout
’s abilities.
JSX的这段代码:
渲染这个布局:
While this XML:
Renders this:
JSX代码看起来很熟悉?! 让我们为使用JSX和Android XML中类似的布局创建一个“字典”(或备忘单).
请注意,功能不一定相等. 我试图帮助React Native新手掌握React Native中布局系统的概念. Please refer official guide 详细信息.
考虑这个JSX属性:
flex: 1
它等价于:
android: layout_width = " match_parent "
android: layout_height = " match_parent "
JSX的这段代码:
And this XML:
两者都生成如下输出:
类似地,这个JSX:
And this XML:
Generate this:
为了在容器内获得正确的位置,我们通常会使用的组合 flexDirection
, alignItems
, and justifyContent
properties.
This JSX:
And this XML:
将产生如下布局:
This JSX:
And this XML
将产生如下布局:
This JSX:
And this XML:
将产生如下布局:
This JSX:
and this XML:
将产生如下布局:
我们要吸取的教训是:如果我们 flexDirection:行”,
alignItems 作用于Y轴和
justifyContent works on X axis. 所有内容都是镜像的
flexDirection:列' - justifyContent
影响Y轴 alignItems
affect Y axis.
justifyContent:“flex-start” | 重力= "开始|左” |
alignItems:“flex-start” | 重力= "开始|左” |
justifyContent:“flex-end” | 重力= " |吧” |
alignItems:“flex-end” | 重力= " |吧” |
Try it yourself. Set justifyContent
value to space-around
, space-between
, and space-evenly
.
要更新应用程序状态,您将使用React的 state
variable. Whenever state
is updated, render()
is invoked.
将下面的代码复制到你的应用中:
从“React”中导入React;
import {Button, Text, View} from 'react-native';
导出默认类App扩展React.Component {
/*
初始化状态对象
带有变量number
设置为0和变量名
值为空字符串
*/
状态={数量:0};
render() {
return (
);
}
//声明递减函数
decrement() {
//要更新状态,我们需要调用this.setState
//为变量'number'赋新值
this.设置状态({号码:.state.number - 1});
}
increment() {
this.设置状态({号码:.state.number + 1});
}
}
如果单击“递减”和“递增”按钮, 您将看到文本自动为您更新. 没有必要显式地使用 textView.setText(“值”+数字)
.
状态功能很方便,原因有很多:
现在我们已经掌握了基本原理, 让我们创建一些更复杂的东西:为/r/pics创建一个搜索应用程序. Reddit提供了一个简单的JSON API端点, 所以我们不需要通过支线任务去获得验证.
React Native提供了一个内置的Fetch API. 因为我们大多数人可能已经习惯了 Retrofit 而且它很容易使用,我们会用到 axios. You can install axios 通过终端命令
using yarn
(我的首选方法):
yarn add axios
or using npm
:
npm install axios
Imports:
从“React”中导入React;
import {
文本输入,视图,文本,图像,
活动指示器、平台、样式表
} from 'react-native';
从'axios'导入axios;
TextInput = EditText;
ActivityIndicator =进度条
平台-平台检测模块
StyleSheet—用于创建样式表并将其从JSX移出的模块
Create the class:
导出默认类App扩展React.Component {
}
初始化状态. We’ll need:
state = {text: ", loading: false, error: null, imgUrl: null};
Add the JSX code. 我们有一个垂直布局 TextInput
and Image
components.
render() {
return (
//Predefined style. See below
{/*
returnKeyType ~ imeOptions
onSubmitEditing ~.OnEditorActionListener
*/}
this.setState({text})}
onSubmitEditing={() => this.searchPicture()}/>
{/*
渲染错误图像组件
if this.state.imgUrl is
not equal to null
*/}
{
this.state.imgUrl &&
}
);
}
New Stuff:
onChangeText={(text) => this.setState({text})}
onSubmitEditing={() => this.searchPicture()}
{
this.state.imgUrl &&
}
第一种方法的工作类似于 EditText
with the TextWatcher
component. 老实说,React Native要好得多.
第二个方法在按下键盘上的返回键时调用(et.OnEditorActionListener
)触发后 searchPicture()
.
渲染图像时 imgUrl
不为空或未定义,因为&&'操作符不检查第二个参数,如果第一个参数已经为假.
你可能想知道为什么 this.state.imgUrl
is false. 嗯,当在JavaScript中使用逻辑运算符时,除了“(一个空字符串)”, 0
, false
, null
, or undefined
are true. 没有必要进行具体的检查.
searchPicture() {
//Default state
this.setState({loading: true, error: null, imgUrl: null});
axios.get('http://www.reddit.com/r/pics/search.json', {
参数:{//获取参数映射
limitt_sr: 'on', //只搜索/r/pics
限制:1,//限制一个搜索项
Sort: 'new', //按创建日期排序
q: this.state.文本//搜索查询
}
}).then(response => { //promise is resolved and 'then' block is triggered
//使用新值设置状态
this.setState({
imgUrl: response.data.data.children[0]
.data.preview.images[0].source.url,
错误:null, loading: false
})
}).catch(error => {//Some error occurred
//set error
this.设置状态({错误:错误.message, loading: false, imgUrl: null})
})
}
Here we go. 应用程序现在应该按预期工作了. 输入搜索字符串并按回车键.
因为我们的应用程序也已经准备好渲染了 ActivityIndicator
和错误,我们需要在后面添加一些代码 Image
component:
{
//Separate method
this.renderProgress()
}
{/*
呈现错误文本组件
if this.state.error is
not equal to null
*/}
{
this.state.error &&
{this.state.error}
}
可以将渲染组件移到main之外 render()
method, too:
renderProgress() {
//If this.state.loading is true
//返回包含进度条的视图
//视图采用数组样式
if (this.state.加载=== true) {
return (
);
}
}
剩下的都是样式. 把这些放在外面 App
class.
const styles = StyleSheet.create({
containerStyle: {
flexDirection:“列”,
flex: 1,
// React Native是跨平台的
//我们同时处理两个平台.
//添加上边距来修复状态栏重叠
marginTop:平台.OS === 'ios' ? 20 : 0,
},
textInputStyle: {
marginLeft: 16,
marginRight: 16,
height: Platform.OS === 'ios' ? 30 : undefined
}
});
我们现在可以添加一些更多的调整,比如在应用程序启动时自动打开软键盘.
请注意,有一种更简单的方法 TextInput
自动对焦(自动对焦={真}道具
),但出于本例的考虑,我们将不使用它.
添加对 TextInput
with prop:
ref={ref => this.searchInput = ref}
And override componentDidMount ()
像这样的生命周期方法:
componentDidMount () {
this.searchInput.focus();
}
重新加载应用程序,键盘自动为我们打开.
我们已经创建了一个组件,但是让我们回顾一下组件的生命周期.
下面是React的生命周期流程:
constructor()
-构造函数总是在应用程序启动时调用static _getDerivedStateFromProps_(道具,状态)
-在渲染之前和更新之后调用. 返回用于更新状态的对象. 返回null以不更新任何内容.render()
每个React组件类都需要渲染. 它被用来渲染视图.componentDidMount ()
-在组件被渲染并挂载到视图树后调用.shouldComponentUpdate (nextProps nextState)
-状态或道具改变后调用. 每次状态更新后,返回值默认为true. Invokes render()
if returns true.getSnapshotBeforeUpdate (prevProps prevState)
-在提交渲染输出之前调用.componentDidUpdate(prevProps, prevState, snapshot)
-在渲染新更新后调用. 它不是在第一个之后调用的 render()
.componentWillUnmount ()
-在组件卸载和销毁之前调用.在处理项目时,我们经常需要创建可重用的组件. 创建组件有两种方式:
React.Component
. 如果我们需要生命周期方法,应该使用此方法.因为我们已经创建了组件类,让我们为这个实例创建一个函数.
假设我们需要一个类比
. 创建一个“common”文件夹 ./src
directory.
Create CardView.js
.
从“React”导入React;
从react-native中导入{View};
export default CardView = (props) => {
return (
//样式将与默认的containerStyle合并
//and props.style. props.样式属性将覆盖
//参数相同时的值.
{/*
props.子视图包含子视图
如果组件是容器,则添加这一行
*/}
{props.children}
);
};
const styles = {
containerStyle: {
borderRadius: 4,
margin: 5,
padding: 5,
elevation: 5,
shadowColor:“黑色”,
shadowRadius: 5,
shadowOpacity: 0.5,
shadowOffset:{宽度:0,高度:3},
写成backgroundColor:“白色”
}
};
LoginForm
using our new CardView
layout:
从“React”导入React;
从react-native中导入{textput, Platform, Button, StyleSheet};
导入CardView../共同/组件/ CardView”;
导出默认类LoginForm扩展React._Component _{
render() {
return (
//覆盖默认样式
console.日志(onLoginPress)}
buttonStyle ={风格.buttonStyle}/>
);
}
}
const styles = StyleSheet.create({
buttonStyle: {
elevation: 5,
height: 40
},
textInputStyle: {
padding: 10,
//附加参数
//iOS输入更漂亮
...Platform.select({
ios: {
borderRadius: 2,
marginTop: 5,
写成backgroundColor:“继续”
}
})
}
});
Import the LoginForm
class in the App
类,然后用 View
如果你在样式中调整参数,你可以得到看起来更好的东西.
导航到不同的场景是大多数应用程序的重要组成部分. 我们将创建一个Reddit /r/pics浏览器应用程序.
在React Native中创建导航非常简单.
Prerequisites
react-navigation
with yarn
or npm
axios
with yarn
or npm
让我们从创建两个不同的组件开始.
注意:您应该已经熟悉下面的大部分代码. 我要贴全班.
PictureList.js:
从“React”中导入React;
import {
ActivityIndicator FlatList,
图像,文本,TouchableHighlight,视图
} from "react-native";
从“axios”导入axios;
导入CardView../common/CardView";
导出默认类PictureList扩展React.Component {
状态= {loading: true, error: null, posts: null};
componentDidMount () {
axios.get('http://www.reddit.com/r/pics.json')
.then(response => {
this.setState({
posts: response.data.data.children,
loading: false
})
}).catch(error => {
this.setState({
error: error.message,
loading: false
})
})
}
render() {
return (
// FlatList ~ ListView
// data - List的数据源
// renderItem -函数返回视图项
// keyExtractor -项目的唯一id
{this.state.posts &&
(item.data.id + '')}/>}
{this.state.loading &&
}
);
}
navigateToPicture(title, url) {
this.props.navigation.导航(PicturePreview, {
'title': title,
'url': url
})
}
renderItem(item) {
//从item中解构值
//阅读更多'ES6解构'
Const {data} = item.item;
Const {title} = data;
const {url} = data.preview.images[0].source;
return (
//Clickable view
this.navigateToPicture(title, url)}>
{/重用我们的CardView/}
{title}
)
}
}
PicturePreview.js
:
从“React”中导入React;
从“react-native”中导入{Image};
导出默认类PicturePreview扩展React.Component {
/ /变性导航
//设置标题为header
static _navigationOptions = ({navigation}) => ({
title: navigation.state.params.title
});
render() {
const {url} = this.props.navigation.state.params;
return ( )
}
}
The navigationOptions
会被React-Navigation自动调用吗.
Now let’s move to App.js
注意:在React-Navigation中有许多导航类型. 今天,我们将重点关注 StackNavigation
. 详细信息请参考官方网站.
从“React”中导入React;
从"react-navigation"中导入{createStackNavigator};
导入图片列表./组件/ PictureList”;
导入图片预览./组件/ PicturePreview”;
导出默认类App扩展React.Component {
render() {
return (
);
}
}
//自定义标题_
const NavigationOptions = {
headerTintColor:“# fff ',
headerStyle: {
写成backgroundColor:“# f4511e ',
}
};
//创建路由器.
const Router = createStackNavigator({
//Name the screen
'PictureList': {
//链接组件
屏幕:PictureList,
//附加导航选项
navigationOptions: {
title: '/r/pics Browser',
...NavigationOptions
}
},
“PicturePreview”:{
屏幕:PicturePreview,
navigationOptions: navigationOptions
}
}, {
//Root
initialRouterName:“PictureList”
}
);
正如你所看到的,我们所需要做的就是创建一个导航路由器,并把 app render it. 如果一切顺利,我们将有一个功能性的Reddit /r/pics浏览器应用程序.
Android:
iOS:
自从我开始编程,我就有了纯粹的移动开发经验. 但是现在我可以用React编写任何东西:移动端、桌面端和web端.
如果您决定开始开发下一个惊人的应用程序,请使用 React Native, 你会发现它有自己的怪癖和一些bug, 但是React Native功能非常强大,非常适合大多数项目.
集成开发环境,或代码编辑器.
许多流行的应用程序都使用了React Native,包括Facebook, Tesla, Skype, Instagram, Uber, etc.
JavaScript
Flexbox是在React Native中创建布局的方式.
In short, neither. React Native的代码编程, 也就是JavaScript, 并没有真正编译成Java或Swift/Objective C. 所以它仍然需要JavaScript引擎来运行. 但是,UI使用本机组件,因此对于用户界面来说它是本机的.
Yes. React Native是一个用于构建具有原生UI的应用程序的框架.
Alex是一名拥有多年Android和iOS经验的高级移动开发人员. 他为几家银行开发了移动银行套件和应用程序.
世界级的文章,每周发一次.
世界级的文章,每周发一次.
Join the Toptal® community.