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

原文链接:http://blog.zhaiyifan.cn/2016/08/04/android-new-project-from-0-p10/

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

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

什么是React Native

React Native

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)都包含在组件内部。

React.js

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

React Native 正像它的名字,将 React 带到了原生世界,和 H5 不同的是,我们不再使用 CSS 和 HTML,而只有 js 为伴。我们也不再有那些 div, input 这些标签,而是由 View, TextInput 等等取代,更符合原生开发者们的习惯。布局上,幸而有强大的 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

Redux.js
Redux.js

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已经被黑的不成样了。

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

运行效果

先看看最后的效果,方便对应后面的解说。
第一个 Counter 项目很简单,就是一个文本框加上两个按钮,一个加1,一个减1。
第二个 Counters 项目在前者的基础上(使用了 Counter 组件),可以增加任意个计数器,还添加了带延迟的加1功能,来模拟耗时操作。

Counter

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

目录下,有以下文件:

Counter List Files

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 下载的依赖。

物理结构

Counter物理结构

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

动作流

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

Counter活动图

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

counterApp.js

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

app.js

而 Component 也不是直接调用 action 的 function,而是通过 bindActionCreators 注入到组件props中(这里是通过 react-redux 进行的,不是 redux 自身的东西,可以理解为 react 和 redux 之间的胶水):

action bind

通常我们会在智能组件的末尾使用:

export default connect(mapStateToProps, mapDispatchToProps)(CardDetail);

这样来把 state 以及 action 注入。

Counters

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

demo

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

物理结构

Counters物理结构

我们来详细讲一下 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 部分是怎么做的。

Counters render

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

动作流

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

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

Add Counter

看上去是不是跟上面的差不多?剩下的那个 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

更多阅读

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容