React Native超简单完整示例-tabs、页面导航、热更新、用户行为分析
初学react native,如果没有人指引,会发现好多东西无从下手,但当有人指引后,会发现其实很简单。这也是本人写这篇博客的主要原因,希望能帮到初学者。
本文不会介绍如何搭建开发环境,如果你还没有搭建,可参考这里的官方文档: 。
本文也不会介绍各种组件,太多了,可参考这里的官方文档: 。
本文将会从创建项目开始,到基本的常见布局(tabs)、多页面之间的导航,加入热更新、用户行为分析功能。总之,我希望告诉你在实际工作中一个react native app是如何开发出来的。
每一个知识点不会展开来详细讲解,这篇博客只是起到一个指引的作用,能让你少走一点弯路,少一些自己摸索的时间。
第一步:创建项目
当然,前提是你已经搭建好了开发环境。
在命令行中,进入你想要放项目文件的地方,我放在d盘的mydocs文件夹下(d:\mydocs\),执行以下命令:
react-native init 项目名
我的项目名是“test0”,所以完整的命令应该是这样的:
react-native init test0
由于需要到外网下载文件,而我朝对网络是有管制的,因此这个命令可能需要执行较长时间。只要网络没有中断,没有报错,就耐心等待吧。
待命令执行完毕后,在 d:\mydocs\ 目录下就多了一个 test0 文件夹。里面有很多已经默认创建好的文件和文件夹。
先来对默认创建的项目文件做个简单的认识。
用你喜欢的任意编辑器(我喜欢用vs code)找开这个文件夹。你将看到一个类似这样的目录结构:
其中,
/package.json 是包管理的配置文件,要安装什么包,可在这里配置,项目的基本信息,比如项目名、版本号、项目说明、等等,也可在此配置。但大多数情况下,可以不用管它。
/index.js 项目的启动文件。
/app.js 首页文件,在/index.js中会加载这个文件。
/node_modules 项目中用到的所有包都存放在这个文件夹中。自己的项目文件不要放在这里。
/android 这里放的是与android原生编译相关的一些文件,作为一名react native开发者,一般情况下也不用去管它。
/ios 这里放的是与ios原生编译相关的一些文件,作为一名react native开发者,一般情况下也不用去管它。
这里需说明一下,如果你的项目的开发中,需要大量去动 /android 和 /ios 下的代码,甚至在里面加入很多业务逻辑,那就说明你的项目开发是存在问题的,一般情况下,只有在某些与编译、配置、发布相关的才会动到这里的代码。
第二步:创建我们的第一个页面-hello react native
其实,现在我们已经可以运行项目了。 react-native init test0 这个命令已经默认为我们创建了一个首页,但我想替换为我自己的内容。
修改 /app.js ,有以下的代码替换掉原来的所有代码:
1 import react from 'react'; 2 import { safeareaview, view, text } from 'react-native'; 3 4 class app extends react.component { 5 render() { 6 return ( 7 <safeareaview> 8 <view> 9 <text>hello react native</text> 10 </view> 11 </safeareaview> 12 ); 13 } 14 }; 15 16 export default app;
默认生成的代码是hooks语法,但我不喜欢,我更喜欢用class。我觉得class的结构更清晰一些。
第三步:在模拟器中查看运行效果
我们的第一个页面已经创建好了,现在需要查看一下运行效果。可用真机调试,但大多数情况下,用模拟器会更方便一些。
有各种模拟器可供选择,我喜欢用android studio自带的模拟器。安装方法同样见这个文档: 。
打开android studio,点击 configure -> avd manager
在打开的窗口中,就能看到所有你已经创建过的模拟器了。如果你还没有创建过模拟器,就点击 create virtual device 创建一个。下面是我已经创建好的模拟器。
点击后面的绿色三角形,就能启动模拟器了,启动后是这个样子的:
再次回到我们的项目。在命令行中,进入项目文件夹( d:\mydocs\test0\ ),执行以下命令:
react-native run-android
因为我用的是windows系统,就不演示ios的运行效果了。ios的运行,需在mac电脑上执行以下命令:
react-native run-ios
同样,可能需要等待比较长的时间,因为同样需要到外网下载编译工具。以后再执行此命令时就会快很多了。(这就是很多码农们恨透了gfw的主要原因之一 ^_^)
以下是目前我们的代码所运行的效果:
第四步:多个页面之间的导航
现在我们的示例只有一个页面,如果有多个页面,又应该怎样从一个页面跳转到另一个页面呢?
在react native中,有很多包可以实现此功能。比如:react navigation、react native navigation、等等。
网上有各种文章比较过各种包之间的好坏,但对于一般的应用,其实差别不大。虽然在项目中我一般都使用react native navigation,但相对来说,react navigation使用起来更加简单、比较容易上手。因此在这个示例中,我选择使用react navigation。
先创建第二个页面。
在项目的根目录下创建一个文件: /one.js 。并加入以下代码:
1 import react from 'react'; 2 import { view, text } from 'react-native'; 3 4 class one extends react.component { 5 render() { 6 return ( 8 <view> 9 <text>另一个页面</text> 10 </view> 12 ); 13 } 14 }; 15 16 export default one;
然后在 /app.js 中加个按钮,希望点击它后能跳转到 /one.js 。将 /app.js 的代码修改为下面这样子:
1 import react from 'react'; 2 import { safeareaview, view, text, button } from 'react-native'; 3 4 class app extends react.component { 5 onpress() { 6 // todo: 跳到另一个页面 7 } 8 9 render() { 10 return ( 11 <safeareaview> 12 <view> 13 <text>hello react native</text> 14 </view> 15 <view> 16 <button title="点击我去另一个页面" onpress={this.onpress}></button> 17 </view> 18 </safeareaview> 19 ); 20 } 21 }; 22 23 export default app;
接下来需要在 onpress() 方法中加入跳转的代码。在这之前,需要先安装react navigation包。
在项目的根目录下执行以下命令(你可能需要先关闭模拟器中打开的app,或者直接关闭模拟器,并结束项目的运行):
npm install @react-navigation/native
npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
npm install @react-navigation/stack
然后将 /app.js 中的代码修改为下面这样子:
1 import react from 'react'; 2 import { view, text, button } from 'react-native'; 3 import { navigationcontainer } from '@react-navigation/native'; 4 import { createstacknavigator } from '@react-navigation/stack'; 5 import one from './one'; 6 7 const stack = createstacknavigator(); 8 9 class home extends react.component { 10 onpress = () => { 11 this.props.navigation.navigate('one'); 12 } 13 14 render() { 15 return ( 16 <view> 17 <view> 18 <text>hello react native</text> 19 </view> 20 <view> 21 <button title="点击我去另一个页面" onpress={this.onpress}></button> 22 </view> 23 </view> 24 ); 25 } 26 }; 27 28 class app extends react.component { 29 render() { 30 return ( 31 <navigationcontainer> 32 <stack.navigator initialroutename="home"> 33 <stack.screen name="home" component={home} /> 34 <stack.screen name="one" component={one} /> 35 </stack.navigator> 36 </navigationcontainer> 37 ); 38 } 39 } 40 41 export default app;
这次的改动比较大。新加了一个 class home ,将之前 class app 中的代码移到了 class home 中,现在的 class app 是一个维护导航的容器。
留意 class home 中 onpress() 内的代码,在这里,用 navigation.navigate(name) 跳转到另一个页面。通过 class app 中的处理, class home 的 props 中有了一个 navigation 对象。
再次执行 react-native run-android 在模拟器中查看效果:
点击页面中的button,就能跳转到 /one.js 了。
在 /one.js 中,可以直接点击左上角的“返回”图标回到上一页。但为了演示如何使用代码返回到上一页,我将 /one.js 的代码修改为下面这样子:
1 import react from 'react'; 2 import { view, text, button } from 'react-native'; 3 4 class one extends react.component { 5 onpress = () => { 6 this.props.navigation.goback(); 7 } 8 9 render() { 10 return ( 11 <view> 12 <text>另一个页面</text> 13 <view> 14 <button title="返回" onpress={this.onpress}></button> 15 </view> 16 </view> 17 ); 18 } 19 }; 20 21 export default one;
在这里使用了 navigation.goback() 返回到上一页。
现在,点击 /one.js 中的“返回”button,就能回到上一页了。
navigation 还有个 push() 方法,也是较常用的。你可以试试效果。具体使用方法参考官方文档: 。
第五步:加入选项卡tabs
在app中,比较常见的一种布局是在底部有一排选项卡tabs。
其实,tabs是由多个页面组成的,因此,在tabs之间切换,也是在多个页面之间导航。因此这里同样需要用到react navigation。
在项目的根目录下执行以下命令(同样,你可能需要先停止项目的运行):
npm install @react-navigation/bottom-tabs
为了更好的演示效果,加入第三个页面 /two.js ,代码如下:
1 import react from 'react'; 2 import { view, text } from 'react-native'; 3 4 class two extends react.component { 5 render() { 6 return ( 7 <view> 8 <text>第三个页面</text> 9 </view> 10 ); 11 } 12 }; 13 14 export default two;
在 /app.js 中引入它:
1 import two from './two';
将之前的
1 import { createstacknavigator } from '@react-navigation/stack';
替换为:
1 import { createbottomtabnavigator } from '@react-navigation/bottom-tabs';
并将之前的
1 const stack = createstacknavigator();
替换为:
1 const tab = createbottomtabnavigator();
然后将 class app 修改为:
1 class app extends react.component { 2 render() { 3 return ( 4 <navigationcontainer> 5 <tab.navigator> 6 <tab.screen name="home" component={home} options={{title: 'javascript'}} /> 7 <tab.screen name="one" component={one} options={{title: 'python'}} /> 8 <tab.screen name="two" component={two} options={{title: 'php'}} /> 9 </tab.navigator> 10 </navigationcontainer> 11 ); 12 } 13 }
再次在项目根目录下执行 react-native run-android 查看运行效果:
可以看到在底部出现了一排选项卡tabs。点击它就能在不同页面之间切换了。
当然,还可以给tabs加上icon图标。这里就不演示了,懒得去找图标了。更多内容可参考官方文档: 。
第六步:加入热更新功能
好吧,假设到这里我们app的功能已经开发完了。
但就可以这样结束了吗?
当然不行。
我们老大说过:用react native做app,却不做热更新,那你用react native干嘛?
这当然是句玩笑话,但实际情况确实如此,只要是用react native开发app,一般都会加入热更新功能,这是用react native开发app的最大优势之一。如果将最大的优势都丢弃了,确实说不过去。
在这里我使用codepush中国提供的热更新服务。使用的方法比较简单,官方的示例文档已经写得很清楚了,我就不加说明了,直接上代码。
官方示例文档在这里: 。
按照官方文档安装好 cpcn-react-native 后,在 /app.js 文件中引入它:
1 import cpcn from "cpcn-react-native";
为了偷懒,我将官网示例中的代码直接复制到 class home 中:
1 class home extends react.component { 2 constructor(props) { 3 super(props); 4 this.state = { 5 upgradestate: 0, 6 upgradeallbytes: 0, 7 upgradereceived: 0 8 }; 9 } 10 11 onpress = () => { 12 this.props.navigation.navigate('one'); 13 } 14 15 componentdidmount() { 16 cpcn.check({ 17 // 检查是否有新版本后调用此方法 18 checkcallback: (remotepackage, agreecontinuefun) => { 19 if(remotepackage){ 20 // 如果 remotepackage 有值,表示有新版本可更新。 21 // 将 this.state.upgradestate 的值设为1,以显示提示消息 22 this.setstate({ 23 upgradestate: 1 24 }); 25 } 26 }, 27 // 下载新版本时调用此方法 28 downloadprogresscallback: (downloadprogress) => { 29 // 更新显示的下载进度中的数值 30 this.setstate({ 31 upgradereceived: downloadprogress.receivedbytes, 32 upgradeallbytes: downloadprogress.totalbytes 33 }); 34 }, 35 // 安装新版本后调用此方法 36 installedcallback: (restartfun) => { 37 // 新版本安装成功了,将 this.state.upgradestate 的值设为0,以关闭对话框 38 this.setstate({ 39 upgradestate: 0 40 }, () => { 41 // 调用此方法重启app,重启后将会使用新版本 42 restartfun(true); 43 }); 44 } 45 }); 46 } 47 48 upgradecontinue = () => { 49 // 用户确定更新后,调用此方法以开始更新 50 cpcn.agreecontinue(true); 51 // 将 this.state.upgradestate 的值设为2,以显示下载进度 52 this.setstate({ 53 upgradestate: 2 54 }); 55 } 56 57 render() { 58 return ( 59 <> 60 <view> 61 <view> 62 <text>hello react native</text> 63 </view> 64 <view> 65 <button title="点击我去另一个页面" onpress={this.onpress}></button> 66 </view> 67 </view> 68 <modal 69 visible={this.state.upgradestate > 0} 70 transparent={true}> 71 <view style={{padding:18, backgroundcolor:"rgba(10,10,10,0.6)", height:"100%", display:"flex", flexdirection:"row", alignitems:"center"}}> 72 <view style={{backgroundcolor:"#fff", width:"100%", padding:18}}> 73 { 74 this.state.upgradestate == 1 75 && 76 <view> 77 <view style={{paddingbottom:20}}> 78 <text>发现新版本</text> 79 </view> 80 <view> 81 <button title="马上更新" onpress={this.upgradecontinue}/> 82 </view> 83 </view> 84 } 85 { 86 this.state.upgradestate == 2 87 && 88 <view> 89 <text style={{textalign:"center"}}>{this.state.upgradereceived} / {this.state.upgradeallbytes}</text> 90 </view> 91 } 92 </view> 93 </view> 94 </modal> 95 </> 96 ); 97 } 98 };
这就算搞定了。这个app已经有了热更新功能。
第七步:加入用户行为分析功能
这还不算完。在真实开发工作中,一个app上线之后,还需要做的事情还很多,比如需要将错误日志传到服务器,以方便监控是否存在bug。再比如,很重要的一点,需要分析用户的行为,以方便对产品进行改进。这是公司在运营app时的很重要的参考数据。
之前我们都是自己写用户行为分析。但是搞得代码很乱,每次修改和加新的监控都很麻烦,并且我们自己的分析功能写得也不是很好。
前段时间偶然发现,codepush中已经有了用户行为分析的功能,于是便向老大提议用这个,结果还受到了老大的表扬。^_^
官方文档在这里: 。
接下来我在这个示例项目中加入用户行为分析。我需要知道我的用户对哪些内容感兴趣,是javascript,还是python?又或者是php?
只需为每个页面做个埋点,看用户访问哪个比较多就知道结果了。
由于之前在做热更新时已经引入了 cpcn-react-native ,所以不需重复引入。但需要注意的是, cpcn-react-nataive 必须在引入所有组件之前引入,例如我现在的引入顺序是这样子的:
1 import react from 'react'; 2 import cpcn from "cpcn-react-native"; 3 import { view, text, button, modal } from 'react-native'; 4 import { navigationcontainer } from '@react-navigation/native'; 5 import { createbottomtabnavigator } from '@react-navigation/bottom-tabs'; 6 import one from './one'; 7 import two from './two';
然后在 import 语句之后加入以下代码:
1 cpcn.usefootprint();
再然后修改 /app.js 中的 class home ,在它的 constructor 中加入埋点:
1 class home extends react.component { 2 constructor(props) { 3 super(props); 4 this.footprint('javascript'); 5 // 其它代码。。。。 6 } 7 // 其它代码。。。。 8 }
给 /one.js /two.js 也加入埋点:
1 class one extends react.component { 2 constructor(props) { 3 super(props); 4 this.footprint('python'); 5 } 6 // 其它代码。。。。 7 }
1 class two extends react.component { 2 constructor(props) { 3 super(props); 4 this.footprint('php'); 5 } 6 // 其它代码。。。。 7 }
在 /one.js 中有个button,我也希望能监控用户有没有点击它。因此给该button也加个埋点:
1 <button footprint="点了返回按钮" title="返回" onpress={this.onpress}></button>
然后就可以去codepush的控制台中查看分析报表了。下面是我的测试结果:
第八步:现在总应该结束了吧?
结束了吗?
不,还没有。^_^
比如在上面已经提到过的,在真实的开发工作中,我们还需要记录用户的崩溃日志。再比如,我们需要知道我们的用户主要分布在哪些地方。再比如,我们需要将某些数据做缓存。再比如,我们需要在服务器发生错误时给用户一个友好的提示。等等等等。。。。。
总之,在实际的开发工作中,开发一个app,比在培训班做个项目要做的事情多得多。
第九步:结束了
虽然如上面所说,还需要做的事情还很多,但在这篇博客里我就不写了。以后有时间再写吧。
之前曾对自己说,要多写点博客,即可记录一些知识点,也可帮到别人。但却一直没有做到。下班之后就真的不想再动了。^_^
这是第一次写这么长的博客,希望能帮到看这篇博客的你。