从零开始的Android新项目10 - React Native & Redux

本篇来讲讲 React Native 和 Redux,和其他一上来就啪啪啪丢上来一堆翻译的东西不同,本文会从简单的例子入手,让大家能快速地明白 React Native 是什么,Redux 和常见的 MVC、MVP 等有什么区别,怎么去组织一个 Redux 架构的 React Native 项目。

为避免大家还没入门就放弃,预计下一篇才会从我们项目中的实践出发,讲讲更复杂的应用场景。

什么是React Native

从零开始的Android新项目10 - React Native & Redux

React Native 使你能够基于 JavaScript 和 React 在原生平台上构建应用,提倡的是 “learn once, write anywhere”,复用代码,提高开发效率。

项目由 Facebook 开源驱动,在过去的近一年中更新很活跃。文档建议直接看官网的React Native,中文站有点坑。

支持系统:Android 4.1 (API 16) 以及 >= iOS 7.0。

关于 React,可以参见之前为掘金翻译计划翻译的 React.js 新手村教程,简单来说 React 将应用分为一个个动态可复用的组件 —— View的渲染(JSX)、数据如何绑定到显示、状态的变更(State)、属性(Props)都包含在组件内部。

从零开始的Android新项目10 - React Native & Redux

整个应用由一个个组件搭积木而成(组件式开发),而每个组件则由状态驱动而变更。

React Native 正像它的名字,将 React 带到了原生世界,和 H5 不同的是,我们不再使用 CSS 和 HTML,而只有 js 为伴。我们也不再有那些 divinput 这些标签,而是由ViewTextInput 等等取代,更符合原生开发者们的习惯。布局上,幸而有强大的Flexbox 支持,如果开发者们之前有使用或者看到过 Google 在 GitHub 发布的 Android 版 FlexboxLayout,相信对它会很熟悉。原生开发中的页面栈,也由Navigator 进行了实现(在 Android 上还有 BackAndroid 的返回键支持)。

与 WebView 不同,React Native 运行的界面,最终会被解释映射为原生的 View,可以直接使用布局边界或者 Hierarchy Viewer 看出层级(js 文件会打包为一个bundle,位于assets下面,RN引擎会加载并进行解释映射)。

好处

  • 体验 web 开发的便捷,不再需要编译,重新加载一下 js 就行了

  • 可以直接使用 Chrome 或者 Nuclide 调试

  • Android / iOS 两端可以共享很大一部分代码(RN 还在进行 Windows, MacOS, Node-webkit 等平台的支持)

  • 热更新,JS bundle 下发一下新的就行了(当然也有一定局限性,如果是 hybrid,则 native 的 RN module 部分不能更新)

坏处

  • 前端开发不会原生做不了 React Native(除非你能真只用自带的那些东西),而且理解那些 RN 提供的组件也会很头晕(需要同时了解 Android 和 iOS)。

  • 原生开发需要一定成本的学习实践才能掌握 React Native。毕竟 ES6 不像过去的 JS 那么傻瓜式了。

  • React Native 目前仍然处于快速迭代开发的阶段,你永远也不知道下个版本自己升级需要修改多少原来的代码。

  • React Native 的资料较少,尤其是国内的,更尤其是 hybrid 开发的(GitHub 上的开源项目大多是纯 RN 的)。

什么是Redux

从零开始的Android新项目10 - React Native & Redux

Redux 本身和 React 并没有特别紧密的联系,而是 Facebook 提出的 Flux 架构的一种优秀实现,可以搭配其他任何框架一起使用。在 React 上使用,需要搭配 react-redux(如此一来 Redux 可以不局限于 React,而让社区发展出更多的 redux-* 中间件)。

Redux 在 React 的基础上(state 和 props),增加了 storeactionreducer 的概念,规范了全局一个 state,从而只需要根据这个 state 就能回朔出整个应用的状态。组件通过 dispatch 将 action 传到 store,reducer 根据原来的 state 以及 action,返回新的 state,组件根据新的 state 渲染界面。

Redux 是一个可预测的状态容器,即只需要有状态树,就能还原出“事发现场”。

从例子看项目

为了避免说一大堆概念,大家一头雾水,似懂非懂,这里拿一个例子来讲讲 React Native 和 Redux 结合后的效果,尽量避免代码的出现,而以图和文字代替。

Counter!没错,就是 Counter,不是 TODO,TODO已经被黑的不成样了。

项目源码位于:https://github.com/alinz/example-react-native-redux。包含了Counter 和 Counters 两个子项目。前者是单个的计数器,后者则在前者的基础上增加了可以加减计数器个数的功能,相对更复杂一些,不过引入了一些不错的实践可以参考。

运行效果

先看看最后的效果,方便对应后面的解说。 

第一个 Counter 项目很简单,就是一个文本框加上两个按钮,一个加1,一个减1。

第二个 Counters 项目在前者的基础上(使用了 Counter 组件),可以增加任意个计数器,还添加了带延迟的加1功能,来模拟耗时操作。

Counter

先看看Counter,我们从物理架构和动作流两个角度来进行观察。

目录下,有以下文件:

从零开始的Android新项目10 - React Native & Redux

index.android.js 和 index.ios.js 分别是 android 和 iOS 的 rn 入口,通常内容是相同的。 android 为 Android 的工程目录,下面有我们熟悉的 build.gralde。 ios 为 iOS 的工程目录,包含了 xcode 的项目。 app 就是 rn 的目录,包含了 Android 和 iOS 项目共享的 js 源码。 node_modules 是 node 通过解析 package.json 下载的依赖。

物理结构

从零开始的Android新项目10 - React Native & Redux

CounterApp.js 则是整个应用的实际入口。

动作流

且不谈那些具体的 bind 和 createStore 操作,我们来看看当发生交互的时候,整个动作的分发,拿点击加号为例:

从零开始的Android新项目10 - React Native & Redux

onPress 事件触发了后续的一系列活动,而 Counter Component 的 action function 则由外部通过 props 传入(在这里,是 CounterApp 的 render 函数,如下)。

从零开始的Android新项目10 - React Native & Redux

再看看 store 的创建,在 App.js 入口:

从零开始的Android新项目10 - React Native & Redux

而 Component 也不是直接调用 action 的,而是通过 store 进行调用,store 在初始化的时候会通过 bindActionCreators 绑定所有action。绑定如下:

从零开始的Android新项目10 - React Native & Redux

Counters

接着我们来看看更为复杂的 Counters 项目,顶层目录结构类似,不再赘述。

从零开始的Android新项目10 - React Native & Redux

看完上面的 demo 动图后,相信大家对下面的解说会更容易理解。

物理结构

从零开始的Android新项目10 - React Native & Redux

我们来详细讲一下 modules 下的 app 目录中的文件组织。

actions.js 和刚才一样,定义了一个个的 action,略有不同的是由于这次有异步的操作,所以涉及到了 dispatch 函数,关于 dispatch 可以查看官方文档

constants.js 定义了所有 action 的 type,以及 App 的名字。

reducers.js 一样根据 action(payload 和 type)以及原来的 state 返回新的 state,另外,这里还进行了 initial state 即初始状态的定义(我们也可以把它放到单独的文件中)。

App.js 定义了页面的布局(渲染和 action),导出了 connect 生成的 container。我们简单看一下 render 部分是怎么做的。

从零开始的Android新项目10 - React Native & Redux

怎么样,JSX 是不是挺容易理解的?

动作流

Counter 本身的动作流上面我们已经举例过了,本工程中增减计数类似,唯一的区别是 action 不只有 type,还带了 payload(id)来标示不同的计数器。

所以这里我们拿增加计数器的点击事件来做例子。

从零开始的Android新项目10 - React Native & Redux

看上去是不是跟上面的差不多?剩下的那个 incrementWithDelay 其实也差不多,只不过返回的是一个function,在 setTimeOut 回调中才进行 dispatch(thunk middleware 会帮我们进行处理)。

总结

上面我们通过物理结构和活动图大致了解了 React Native 上的 Redux 架构 app 是如何工作的。具体的细节,建议大家还是去查看 GitHub 上的源代码,通过上面的讲解后,应该不难理解。

技术栈

我们目前实践的React Nataive技术栈:

  • immutable.js

  • react

  • redux

  • react-redux

  • redux-thunk

  • redux-logger

  • redux-mock-store

  • react-native-router-flux

  • react-native-simple-store

  • regenerator

  • undefined

  • jest

更多阅读