React Native 与 TypeScript 在企业开发中的实践

React Native 与 TypeScript 在企业开发中的实践

为什么选择 React Native?

  • 大前端时代的综合考虑


当我们重新审视当下的技术发展,不难发现,使用 OC 或者 Swift 开发 iOS 不难,使用 Java 或者 kotlin 也不难。

不仅不难,由于 Apple 和 Google 的优化开发体验,反而有越来越容易的趋势。
那我们为什么还需要选择 React Native 呢?

基于传统的开发而言,当我们需要推出一款产品或服务的时候,可能需要开发 iOS、Android、Web 三端的产品。

当我们使用韦恩图来复盘分析时,会发现这三端有大量的业务逻辑代码是重复的。
它们都是直接面向客户的前端,也都具有同样的行为模式,可能 UI 界面会有些许区别,但整体逻辑确确实实都是一致的。

最具业务价值的代码被重复编写,且需要重复测试,这都带来了不确定性,也使得三个本是一体的项目变成了三个项目。

这样的问题需要解决,所以大前端时代了。
而秉承着 “Learn Once, Write EveryWhere” 这一精神口号的 React 成为了大前端时代最初也是最佳的选择。
既可以保证大前端都采用同样的架构,又可以使用同样的语言编写最重要也最具价值的业务逻辑。
  • 人员配置的最优选择

在传统开发中,需要配置 Web、iOS、Android 各端的开发人员。
但在 React Native 的加持下,跨端开发不再有艰深的壁垒,同样的语言,同样的架构,使得整个大前端组的人员得到了高度的复用,对项目长期的维护和公司的成本都是整体利好的。
  • 开发效率的最佳选择

React Native 采用同 HTML 的声明式标签式语言开发 UI 界面,使得工程师学习的成本非常小。
在编写新界面时,也不需要编译,直接刷新即可。如果使用 Redux,且缓存整个路由树的话,还能得到近似网页的,刷新即可看到当前界面的修改的效果。
潜在的问题
React Native 本身提供的 API 非常有限,所以最优的情况是要在开发队伍中常备配置具有原生 API 开发能力的工程师,如果不能,就让他们能够提供一定时间的应援。

React Native 本身需要工程师对 React 有一定的学习和理解。如果对 React 没有清晰且整体的认识,容易写出 anti-pattern 的代码。

为什么选择 TypeScript?


  • TypeScript 与 Flow

TypeScript 可以在编译时对 JS 代码进行静态类型检查。
但这句话还不足以反映称 TypeScript 的优势,因为 Flow 也可以。
TypeScript 与 Flow 最大的不同在于,Flow 不强迫用户导入依赖的类型文件,而 TypeScript 要求用户导入依赖的类型文件。
换句人话而言,如果你使用了 Lodash,Flow 本身不会强制对 Lodash 相关的代码进行检查,而 TypeScript 会强制你引入 @types/lodash 来解决类型文件缺失的问题。

所以当你的代码处于 TypeScript 时,你的所有代码,包括通过 npm 引入的代码,都是进行过类型检查的。这是非常安全的。
因为我不止一次见到过,大型项目中,已经没有人能准确知道一个函数到底是接收或传递什么变量,到底需要几个参数。

注释的缺失在动态语言中是非常致命的。
引入 TypeScript 可以很好的解决代码的可读性和可维护性的难题。

TypeScript 另一个优势是在于对 IDE 的友好。
由于强迫所有代码,包括第三方都引入了类型文件,那 IDE 在进行推导提示的时候就会非常准确了,包括 autoimport 和函数类型检查都比 JavaScript 来的准确的多。
这也是 Flow 所不能比拟的。

所在在新的项目中,越早的引入 TypeScript,对以后的维护或重构都有非常大的好处。
  • TypeScript 也不能做的

TypeScript 不能解决由于工程师本身对语言或框架的理解不足而导致的错误。
这句话非常的绕哈,换句普通的人话来讲就是,如果你对 JavaScript 或 React 本身理解不够深入,错误使用导致的错误,那 TypeScript 是没救的。

架构
  • 架构的分解

传统而言,我们往往把前端的架构拆分成 MVC 来理解分析。
但 React 不是这么分析的,React 本身只是解决 UI 层的问题,而基于它的哲学思想则是基于将前端视作 UI 界面及数据流的总和。
如果放在考虑引入 Redux 的情况的话,整个前端可视作 UI + 数据流。
那从整体架构我们就只需要从这两端来思考即可。
数据流
  • 旧架构的设计

在旧的架构下,我们通过中间件来解决异步 Action 的问题。
则可能存在这样的情况,如果有多层中间件,比如 A、B、C 三层,每层中间件负责自己的业务逻辑,当 Action 穿过时,就会触发新的 Action 产生,在层级不多的情况下,这可能是可控的,但如果业务项目膨胀,大量的中间件,就可能产生无法跟踪的无序的 Action。每一个 Action 穿越而过都可能产生多个新的 Action,这就是副作用。
  • 副作用

滥用副作用会使得异步 Action 及中间件无法得到良好的测试。
而数据流关乎到前端最重要的业务逻辑,如果无法被测试覆盖到,就没有意义了。
所以这就引来了对副作用管理的讨论。
  • 副作用的管理

如何处理副作用成为了一个没有标准答案又必须解决的问题。
最终的结果是是测试必须能够覆盖。
当下最佳的两个解决方案分别是 redux-saga 和 redux-observable。
redux-saga 通过迭代器对副作用进行管理,而 redux-observable 选择了引入 RxJS,通过 stream 的方式处理异步操作,将业务逻辑封死在 epic 中,这样既可以通过一个 action 触发引发的 action 流来进行测试,也可以通过 RxJS 自身的操作符流进行测试。
由于测试覆盖的全面性,所以选择了使用 redux-observable。
  • 新架构的设计

所以在新的架构中,中间件不触发新的 action,只作记录跟踪。
而 action 触发 epic,业务逻辑都封装在 epic 中,epic 中多有个 observable,通过封装常用的业务单元成 observable 来提供代码复用率,同时 RxJS 提供了 observable 的测试方式,这使得业务单元 observable 及业务流 epic 都可测,可以做到 TDD。

组件


  • 组件的组织方式

根据我对多个项目的观察,发现了这样一个现象,就是大家对组件的组织方式没有一个整体的规划和思考。
如果使用了 Redux,那还需要一个组件从 App State 里面取值再传值给它下属的几个组件吗?
既然你使用了 Redux,这样处理是否有一种脱裤子放屁的感觉?

那我们可以这样对组件进行分类。
一种是需要从 App State 中取值,也需要改变 App State 值的组件,比如一个 banner,我们可以称之为 Redux State Component。
那与之相对的则是无状态组件,需要被大量复用,例如 Button。
那最后还需要一种组件,这种组件是一个容器,在大多数情况下是一个 React Stateless Component,本身没有业务逻辑,主要负责将所有组件进行拼装组合到一起,我们可以称之为 Container Component。
所以当我们拿到 UI 设计图时,应该先在脑子过一遍,将组件进行最细化拆分。

那我们很容易得出这样的组织方式



将页面分作目录,状态组件放在页面目录内,无状态的丢到目录外。

  • 组件的测试


将组件最细颗粒度拆分,就非常实现 TDD 进行测试了。
而最后组装成一个组件,就直接测试组装集成即可。

常用库


  • redux-observable

数据流处理
  • redux-persist

数据存储
  • react-intl

多语言
  • React-Navigation

路由,能非常简单的实现 Tab 、Stack、drawer 效果,这三者直接的混合组合也能非常容易的实现。
集成 Redux 后,能实现时间回溯功能。
  • date-fns

新时代的 moment,支持 ES6 module。
  • Jest

单元测试
  • enzyme

组件测试
  • tslint

代码风格检查
  • React Native Debugger

UI 及 Redux 调试工具

最后

内容有点多,一篇讲不完,可以再专门开个专栏写写。

编辑于 2017-05-22

文章被以下专栏收录