作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
亚历山大Pataridze的头像

亚历山大Pataridze

Alex是一名拥有多年Android和iOS经验的高级移动开发人员. 他为几家银行开发了移动银行套件和应用程序.

Previously At

Bank of Georgia
Share

几年前,我的一个同事告诉我React Native. 我很怀疑. 我认为它只是另一个跨平台框架,永远不会在现实生活中工作-我几乎不知道我错了.

几年过去了,React Native技能变得非常抢手. 既然我已经有一段时间没有学到新的东西了,我想为什么不试试呢? 如今,我是一名React Native Android应用开发倡导者.

Cons:

  • 你不能再使用Android Studio了
  • React Native不能用于每个应用程序或功能
  • React Native是一个新颖的框架,更新可能会对你当前的代码库产生负面影响
  • JavaScript不是严格的类型语言
  • React Native需要一个JavaScript引擎来运行,这可能会降低它的性能

Pros:

  • Easy to learn
  • Android和iOS应用程序之间的共享代码库, 只需要微调以匹配平台体验
  • 实时和热重新加载,意味着没有更多的无限构建时间
  • 两个平台的本地组件
  • 不断提高
  • 积极成长的社区
  • 大量的图书馆
  • Expo让开发者无需拥有Mac便可面向iOS平台开发游戏
  • 减少人力资源——尽管你可能仍然需要一些Android/iOS原生开发, 这将是罕见的.

我可以继续说下去,但让我们停在这里,进入这篇博客文章的主题. 在这篇文章中,我将创建四个React Native Android应用:

  1. 带有增加和减少计数器按钮的基本计数器
  2. 一个应用程序搜索r/图片子reddit
  3. 通用登录页面
  4. 浏览r/pics子reddit的应用程序

IDE

正如我上面提到的,我们不可能使用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项目.

  1. 传统方式. 使用WebStorm GUI或终端命令: react-native init AwesomeToptalProject
  2. 更简单的方法“创建React原生应用”. create-react-native-app AwesomeToptalProject

If you use create-react-native-app,创建的项目将与世博会一起启动. 我就不细讲了, but basically, 这意味着你不需要安装Xcode就可以在iOS上运行应用. 通过博览会让客户端始终保持最新状态也更容易.Io的功能和其他一些特性. 但是你不能添加本地代码. Thus, 如果你正在开发一个特定的功能, 你可能需要从expo中弹出一个应用程序,转而使用一个常规的React Native项目.

在这个演示中, 在解释如何为Android构建React Native应用程序时,我将使用第一种方法.

让我们运行这个项目. 首先,打开模拟器或连接设备. 如果您使用WebStorm GUI创建项目,那么您所需要做的就是选择一个配置. 在WebStorm的右上角, 单击Run按钮左侧的下拉菜单, choose Android, 并单击“运行”或“调试”. 如果您使用Terminal创建项目, 你可以添加一个新的React Native配置或者使用Terminal运行它:

cd AwesomeToptalProject
react-native运行android

如果一切正常,您将看到以下屏幕:

Generated layout

结构和基本设置

项目中值得注意的项目有:

  • android -预先配置了React Native支持的android Studio项目.
  • ios - Xcode项目预先配置了React Native支持.
  • node_modules——一个包含React Native框架和其他Javascript库的文件夹.
  • index.js -应用程序的入口点.
  • App.加载初始组件.

让我们在项目的根目录下创建一个名为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() method and returns Text component. React.Component 使用JSX构建UI的基类是什么. The export default 修饰符创建类 public.

现在我们准备开始设计我们的布局.

使用Flexbox布局

Flexbox is similar to LinearLayout, but Flexbox goes way beyond LinearLayout’s abilities.

JSX的这段代码:






渲染这个布局:

Generated layout

While this XML:






Renders this:

Generated layout

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:






两者都生成如下输出:

Generated layout

类似地,这个JSX:






And this XML:






Generate this:

Generated layout

为了在容器内获得正确的位置,我们通常会使用的组合 flexDirection, alignItems, and justifyContent properties.

This JSX:






And this XML:






将产生如下布局:

Generated layout

This JSX:


   
   
   

And this XML


   
   
   

将产生如下布局:

Generated layout

This JSX:


   
   
   

And this XML:


   
   
   

将产生如下布局:

Generated layout

This JSX:


   
   
   

and this XML:


   
   
   

将产生如下布局:

Generated layout

我们要吸取的教训是:如果我们 flexDirection:行”, alignItems 作用于Y轴和 justifyContent works on X axis. 所有内容都是镜像的 flexDirection:列' - justifyContent affects Y axis and alignItems affect Y axis.

justifyContent:“flex-start”重力= "开始|左”
alignItems:“flex-start”重力= "开始|左”
justifyContent:“flex-end”gravity="end|right"
alignItems:“flex-end”gravity="end|right"

Try it yourself. Set justifyContent value to space-around, space-between, and space-evenly.

State Management

要更新应用程序状态,您将使用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 (
           
               
最终递减程序

如果单击“递减”和“递增”按钮, 您将看到文本自动为您更新. 没有必要显式地使用 textView.setText(“值”+数字).

状态功能很方便,原因有很多:

  • 易于获取值—您总是知道在哪里以及如何获取特定变量的值.
  • 数据不绑定到特定的小部件.
  • 拥有多个依赖于公共值更改的小部件.

创建一个搜索/r/pics的应用程序

现在我们已经掌握了基本原理, 让我们创建一些更复杂的东西:为/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:

  • loading -用于显示进度条.
  • error -显示在发出REST API请求时是否产生了一些错误.
  • imgUrl -预览搜索的图像.
  • 文本搜索查询.
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) after it triggers 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. 应用程序现在应该按预期工作了. 输入搜索字符串并按回车键.

最终的reddit搜索应用程序

因为我们的应用程序也已经准备好渲染了 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 自动对焦(自动对焦={真}道具),但我们的React Native Android示例不会使用它.

添加对 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 () -在组件卸载和销毁之前调用.

组件生命周期图

Reusable Components

在处理项目时,我们经常需要创建可重用的组件. 创建组件有两种方式:

  1. 创建一个扩展的类 React.Component. 如果我们需要生命周期方法,应该使用此方法.
  2. 通过编写一个返回View的函数来简化语法.

因为我们已经创建了组件类,让我们为这个实例创建一个函数.

假设我们需要一个类比 . 创建一个“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 (
    //覆盖默认样式
           
               
               
               

Import the LoginForm class in the App 类,然后用 View


   

如果你在样式中调整参数,你可以得到看起来更好的东西.

最终生成的登录表单应用程序

导航到不同的场景是大多数应用程序的重要组成部分. 我们将创建一个Reddit /r/pics浏览器应用程序.

在React Native中创建导航非常简单.

Prerequisites

  • Install react-navigation with yarn or npm
  • Install 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 ',
   }
};
//Create the router.
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:

Android的最后一个浏览应用

iOS:

iOS的最后一个浏览应用

针对Android开发者的React Native:非常规但有效

自从我开始编程,我就有了纯粹的移动开发经验. 但是现在我可以用React编写任何东西:移动端、桌面端和web端.

如果您决定开始开发下一个惊人的应用程序,请使用 React Native, 你会发现它有自己的怪癖和一些bug, 但是React Native功能非常强大,非常适合大多数项目.

了解基本知识

  • 你所说的IDE是什么意思?

    集成开发环境,或代码编辑器.

  • 哪些应用使用React Native?

    许多流行的应用程序都使用了React Native,包括Facebook, Tesla, Skype, Instagram, Uber, etc.

  • React Native使用什么编程语言?

    JavaScript

  • 如何在React Native中布局UI?

    Flexbox是在React Native中创建布局的方式.

  • React Native是原生的还是混合的?

    In short, neither. React Native的代码编程, 也就是JavaScript, 并没有真正编译成Java或Swift/Objective C. 所以它仍然需要JavaScript引擎来运行. 但是,UI使用本机组件,因此对于用户界面来说它是本机的.

  • React Native是一个框架吗?

    Yes. React Native是一个用于构建具有原生UI的应用程序的框架.

就这一主题咨询作者或专家.
Schedule a call
亚历山大Pataridze的头像
亚历山大Pataridze

Located in Tbilisi, Georgia

Member since June 5, 2018

About the author

Alex是一名拥有多年Android和iOS经验的高级移动开发人员. 他为几家银行开发了移动银行套件和应用程序.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Previously At

Bank of Georgia

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal Developers

Join the Toptal® community.