IGNITE的官方地址:https://github.com/infinitered/ignite
IGNITE是一个React Native的脚手架生成器(了解ROR的可以理解为rails命令),通过一个命令就可以生成一个结构完整的、可工作的空白react native项目,后续的开发就是向这个项目添砖加瓦,这比从头构建一个RN项目节省很多时间。而且IGNITE默认集成的很多库也都是不二之选,包含了前人的经验。本文解析一下ignite生成项目的结构。
基本安装和命令
使用ignite先要安装:
- 安装命令:npm install -g react-native-ignite
- 生成你的第一个项目,这需要一段时间:ignite new MyApplication
- 生成好的项目是可运行的。进入MyApplication目录运行以下命令就能看到成果了,如果这一步有问题,很可能是ios或者android原生编译环境没有设置好。
- react-native run-ios
- react-native run-android
- 如果没跑起来,可以用npm start命令
开始几步很简单吧,除了react-native命令,其他都是npm命令,安装的也大部分是js代码,如果有问题,可以上网查查npm的基本用法。
除了生成脚手架项目,用ignite命令还能生成其他组件:
- 生成初始项目:ignite new MyApplication
- 添加组件:ignite generate component SmoothButton
- 添加容器:ignite generate container SubMenu
- 添加页面:ignite generate screen Settings
- 添加listview:ignite generate listview HotDates
- 添加新的数据:ignite generate redux Login
- 添加网络请求:ignite generate saga Login
- 添加地图控件:ignite generate mapview MapScreen
- 导入图片资源:ignite import imagesfile
这些命令都很有用,可以先知道有哪些,具体用到时再看。
目录结构
项目目录结构和各部分的作用如下:
- App:ReactJS代码,主要的业务逻辑都在这里,后面详细说明。
- Tests:使用了AVA测试框架:https://github.com/avajs/ava,后面说明。
- android:android原生代码。只有一个主application和一个主activity。这里的代码只是用来把react启动起来,没有任何业务逻辑,初学可以忽略。
- ios:iOS原生代码。跟android目录一样,有效代码只有几行,没有任何业务逻辑,初学可以忽略。
- fastlane:https://github.com/fastlane/fastlane 支持ios/mac/android的自动发布工具
我们主要关注的是App目录,其他部分等需要时再做了解。
采用的库
项目集成了很多库,下面列出所有库的主要功能,主要也是为了备查,知道都是做什么用的,用到时再深入了解。
生产环境依赖库:
- apisauce:js的JSON API客户端。https://github.com/skellock/apisauce
- format-json:json格式化。https://github.com/johan/format-json
- lodash:下一代underline,扩展js库
- querystringify:URL辅助工具。https://github.com/unshiftio/querystringify
- ramda:函数式编程工具集。http://ramdajs.com/
- react
- react-native
- react-native-animatable
- react-native-config
- react-native-device-info
- react-native-drawer
- react-native-i18n
- react-native-maps
- react-native-router-flux:RN路由解释器。用页面(activity)名称实现页面间跳转。https://github.com/aksonov/react-native-router-flux
- react-native-vector-icons:3000个字体图标。https://github.com/oblador/react-native-vector-icons
- react-redux
- redux
- redux-logger
- redux-persist:自动将redux持久化到AsyncStoragex中的工具。https://github.com/rt2zz/redux-persist
- redux-saga:redux异步处理工具
- reduxsauce:redux简化库,包含createActions, createReducer, createTypes等。
- seamless-immutable:不可变数据结构。https://github.com/rtfeldman/seamless-immutable
生产环境依赖库简单总结。可以分为几类:
- 辅助工具:format-json、lodash、querystirngify这些属于辅助工具,是一些常用工具集。
- react类:react开头的库,都是处理view的。
- redux类:redux开头的库,都是处理数据的。特别是seamless-immutable、reduxsauce和redux-saga这3个库串联起来,可以方便的解决redux数据的云同步问题。可以参考这篇文章。
开发环境依赖库:
- ava:js自动化测试工具。https://github.com/avajs/ava
- babel-eslint
- babel-preset-es2015
- enzyme:测试辅助工具集。https://github.com/airbnb/enzyme
- flow-bin:可以用命令执行flow的工具。
- ghooks
- mockery:js的mock工具。
- nyc
- react-addons-test-utils
- react-dom
- react-native-mock
- reactotron-apisauce:reactotron是一个在控制台上控制、监控RN应用的调试器。
- reactotron-react-native
- reactotron-redux
- reactotron-redux-saga
- snazzy
- standard-flow
除了下面的库,还有两个地方可以找到不错的开发好的RN库。一个是https://github.com/react-native-community/React-Native-Elements ,常用的界面元素都有。另一个是https://js.coach/react-native ,可以用来搜开发好的控件。例如微信支付模块等。这个网站就是用react开发的。
React Native
介绍完周边的依赖,现在开始进入正题。
这个项目中真正的代码都在App目录下。App中的目录结构很清晰,叫什么名字放的就是什么东西。containers里都是容器,images里都是图片,所以这里主要跟踪运行逻辑做介绍。
项目根目录的两个文件index.android.js和index.ios.js是不同平台的入口文件,文件内容完全一样,都是调用App/Containers/App.js开始执行。
App/Containers/App.js
RN程序的总入口。它的地位有点类似android中的application。这里用<Provider store>来关联redux store。App/Containers/RootContainer.js
根容器。所有的页面都在这里显示。这里用到了StatusBar和NavigationRouter。StatusBar是自带的状态栏控件,指手机屏幕最上边显示电池和时间那个条。可以控制它的背景色、透明度、是否显示等。NavigationRouter是一个自定义的组件。-
App/Navigation/NavigationRouter.js
Router中描述了所有scene,每个scene是一个页面(对应android中的activity或者fragment)。最外层的drawer和drawerChildrenWrapper是一个抽屉结构的菜单部分和页面部分。drawer的菜单部分在NavigationDrawer中再介绍,这里只说页面部分。可以看到页面部分加载了很多style来定制外观。内部包含了若干个scene,其中第一个scene使用了initial关键字,表示这个页面是第一个显示的页面。这里使用了react-native-router-flux的路由特性。每个页面的key就是它的路由名字,component就是显示时使用的组件。这些页面还还可以定制标题、导航条、导航条左功能和导航条右功能。
从这里可以看出,一个scene是一个页面,但是页面内容本质上是一个Component。为了理解方便,有时这个组件叫container,有时这个组件叫screen。可以用Scene的参数对它做小的定制。
- App/Navigation/NavigationDrawer.js
drawer为什么是个抽屉组件呢?因为这里用的就是<Drawer>,来自react-native-drawer库。这里可以指定很多动作,例如打开、关闭时的动作。还有显示哪些菜单,是用的一个新的组件。
content={<DrawerContent />}
- App/Containers/PresentationScreen.js
应用打开时的首页。这里都是view的代码,使用了View/Image/ScrollView/Text等自带控件,容易看懂。只有一个有趣的,是
<RoundedButton onPress={NavigationActions.componentExamples}>
Component Examples Screen
</RoundedButton>
它是一个自定义的圆角按钮,定义不复杂。如果用android原生的9patch做这个按钮,可是很麻烦。
- App/Containers/Styles/AllComponentsScreenStyle.js
展示了如何显示只针对特定平台的内容。
if (Platform.OS === 'android') {
return (
<Text style={styles.sectionText}>
Android only: Animations are slow?
</Text>
)
}
App/Containers/Styles/UsageExamplesScreenStyle.js
这个页面比较复杂,演示了登录等内容。先看render()中,都是普通代码,主要交互代码都在renderUsageExamples ()中。在这个函数中:根据登录状态显示登录、退出按钮。这个状态是放在this.props中的,结合其他代码,能看出来,只有用户交互的变量放在state中,来自网络的变化都用的props。
登录按钮对应的onPress动作是NavigationActions.login。愣了一下没看到,后来看到这个NavigationActions就是react-native-router-flux的Actions才明白,这就是一次路由跳转。跳转去名为login的Scene,即loginScreen。
点击下边的小图标可以获得温度。获取温度的代码在Services/Api.js中,在Sagas/index.js中重新绑定了。
后面有几个页面跳转。
-
App/Containers/Styles/LoginScreenStyle.js
- 填写用户名和密码,保存到state中。
- 在组件mount前和mount后,会监听虚拟键盘的状态,要调整显示高度和宽度。用Metrics.screenHeight和Metrics.screenWidth获取屏幕大小。这两个应该是像素数。
- 如果取消,就Actions.pop退出当前页。
- 如果确定,就调用LoginRedux和LoginSagas中的函数做登录。登录动作应该是sagas的put做的,更改state,没有实际的网络请求。
这里redux、seamless-immutable、reduxsauce和redux-saga之间的关系还没看明白。
App/Containers/APITestingScreen.js
请求json数据并显示。App/Containers/ThemeScreen.js
多view显示的一个示例。
总结
貌似到这view的部分也就差不多了。model相关的几个关键库的合作关系下次再写。