React Native 混合开发与实现
关于
前言
随着 React
的盛行,其移动开发框架 React Native
也收到了广大开发者的青睐,以下简称 RN。通过 RN 我们能够使用 JavaScript 语言来实现跨平台移动应用的开发,打开了前端工程师通往移动平台的大门。用 RN 官方的介绍来概括它的特点就是:Learn once, write anywhere
。
如果你了解 React,那么学习 RN 的话应该会非常轻松。因为 RN 和 React 使用了相同的开发语言 JavaScript 和相同的设计理念 React,在 React 的基础上添加了原生平台的底层支持。这样,不同平台的适配就交由 RN 去处理,而开发者只需要关注 RN 平台应用开发本身。
本文将从 RN 混合开发(与 iOS、Android 平台交互)的原理和实现进行介绍,结合流程图的方式让大家进一步的了解 RN 开发的思想和底层逻辑。
原理与实现
1. 从 Hello world 开始
先来看一个使用 RN 实现的简单的 Hello world 展示:
![React Native 混合开发与实现](https://img.blogweb.cn/article/9770469fa6124785a5bb09c109f8d337.webp)
上方我们不难看到一些很熟悉的 React 语法,但除此之外我们还能看到其引入了 react-native
库中的 AppRegistry API 和 Text (文本)组件,这便是 RN 提供给我们用于调用原生平台的 APIs 和 组件,其能够在不同移动设备上实现一致的功能和逻辑。最后展示在 APP 中的便是 Hello world 文本,而至于 AppRegistry API 后面会做相应介绍。
2. 解刨 React Native 应用的结构
那么看完 Hello world 示例后,我们应该大致知道了 RN 应用的一个结构,我们用图例的方式进行解刨说明,如下图所示:
![React Native 混合开发与实现](https://img.blogweb.cn/article/783bf5c6ede649428138e345992c8de2.webp)
从图中可以看到,我们整个的 RN 应用可以分为两层展示:
- JavaScript Code 层
- Native Code 层
也可以理解为所谓的应用层和底层。应用层通过 JavaScript 桥接层
与底层平台进行交互,获取底层平台的原生 APIs、UI 组件及一些自定义组件等。比如 Hello world 示例中引入的 AppRegistry API 和 Text 组件便是很好的说明。
这样的分层能够使应用层的开发变得简单、高效和跨平台,对于应用的稳定性、运行时的性能来说将和原生平台保持接近。
3. 原生平台调用 React Native 组件
大致了解完 React Native 应用的结构后,我们不妨再来认识下原生平台是如何调用 React Native 组件的。我们 RN 的代码要跑在原生 APP 中那必然需要原生 APP 加载运行对应的 RN 组件,以实现混合开发和交互的功能。这里就要来介绍下刚刚搁置的 AppRegistry API 了。
![React Native 混合开发与实现](https://img.blogweb.cn/article/ad77742bb17c4924bc4a09372595e40d.webp)
一般我们的 RN 项目都会有一个入口文件,比如 index.js(老版本会存在两个:index.ios.js 和 index.android.js)用于注册根组件并提供给原生平台运行。这里的注册根组件就要通过 AppRegistry API 来实现。
我们需要在根组件里调用 AppRegistry 中的 registerComponent
方法进行组件的注册。注册完之后原生平台便可以通过 runApplication
方法来运行注册过的根组件。需要注意的是注册和运行的组件名称两者必须保持一致,这样才能够实现加载对应的组件。比如 Hello world 示例中我们注册的根组件名为 HelloWorldApp,并且注入相应的组件模块。另外同时一个入口文件中,我们也可以注册多个根组件。
4. 原生加载 React Native 界面
刚刚在介绍原生平台调用 RN 组件时提到了加载对应根组件的功能。那么是不是原生平台只有通过不断的调用运行 RN 注册的根组件才能实现不同页面的首次加载呢(这里的加载指原生打开 RN 页面)?答案是否定的。
![React Native 混合开发与实现](https://img.blogweb.cn/article/5beccf9699774f179abfb7133d0350de.webp)
除了上述通过调用不同的根组件来实现原生打开不同的 RN 界面外(图中第二点),我们还可以调用一个根组件来实现。唯一的区别在于我们需要调用时在 initialProperties 中添加区分不同界面的标识位来渲染不同的组件,就好比在 URL 上携带不同参数跳转到同一路由一样,根据路由上的参数在应用层进行对应组件的渲染。
在 RN 根组件中我们可以通过 this.props
获取原生平台携带过来的参数对象,如示例中的 viewName,再根据 viewName 实现 RN 内部组件的渲染,当然也可以结合 react-navigation
来实现路由模块的切换。至于最终选择哪种方式加载,决定权还是要看业务的划分和功能的定义。相比较而言第一种可能更加灵活和便捷。
5. React Native 与原生平台通信原理
在混合开发模式下,我们不可避免的需要和原生平台进行数据的通信,那么在 RN 中,我们如何与原生平台进行通信呢?如何获取原生平台提供的数据或将数据传递给原生平台呢?下面这张图便介绍了这一流程。
![React Native 混合开发与实现](https://img.blogweb.cn/article/a88a8e60eba8477f9e8e6908d66496ce.webp)
在 RN 中,我们可以引用 react-native 模块中的 NativeModules
API 来进行数据通信,调用的方法是 NativeModules.模块名称.接口名称,而原生平台返回数据到 RN 平台是基于回调,代码如下:
import { NativeModules } from 'react-native';
const userInfo = NativeModules.UserInfo; // 获取自定义用户信息模块
console.log(userInfo.userName); // 打印用户名
const router = NativeModules.Router; // 获取自定义路由模块
// 调用原生路由跳转方法
router.openHome('参数', (res) => {
console.log(res); // 打印返回数据
});
通过 NativeModules 我们可以灵活的获取或传递数据给原生平台,同时我们也可以根据业务需要编写不同的 Bridge
方法来实现数据通信模块的封装,比如用户信息模块、路由跳转模块及网络请求模块等。
6. Redux 架构
在 RN 项目中,除了与原生平台通信和交互的功能外,RN 平台自身也需要实现一些数据状态的管理。这里我们还得认识下 Redux 架构。
![React Native 混合开发与实现](https://img.blogweb.cn/article/8fcc4ec362f545a8aa43340e1269bbee.webp)
Redux 是一个用于管理 React 应用状态的容器,在 RN 中也同样适用。其采用单一数据流的方式来实现数据的管理,唯一改变 state 的方法是提交 action 操作。这样的架构使得我们的 RN 项目数据易于维护或扩容,改变数据的流程容易追踪和捕获。需要了解的具体关键字如下:
![React Native 混合开发与实现](https://img.blogweb.cn/article/342f90814aa944f6b634e1079e1d07d3.webp)
具体文档可以参考:cn.redux.js.org/
当然你也可以使用其他第三方库实现类似的架构,比如 mobx、dva 等。
7. CSS-in-JS
除了 Redux 架构,RN 中还加入了 CSS in JS
的概念,将原本关注点分离的理念转移到了关注点混合上,使得我们可以在 JS 中写 CSS 代码,但这并不违背之前关注点分离的理念。
![React Native 混合开发与实现](https://img.blogweb.cn/article/43cb4df5649743f98ad92d5a230da047.webp)
现在随着组件化概念的流行,对从组件层面维护 CSS 样式的需求日益增大,CSS-in-JS 就是在组件内部使用 JavaScript 对 CSS 进行了抽象,可以对其声明和加以维护。这样不仅降低了编写 CSS 样式带来的风险,也让开发变得更加轻松。它和 CSS Modules 的区别是不再需要 CSS 样式文件。
结合 JSX 语法,在 RN 中书写和维护 CSS 变得更加便捷,也是 Web 组件化不断发展的必然产物。
8. React Native 中 的 Flex 布局
另外,在开发 RN 项目时,官方推荐使用的布局方式是 Flex
布局,因为 Flexbox 可以在不同屏幕尺寸上提供一致的布局结构,这也解决了跨平台布局呈现的问题。
![React Native 混合开发与实现](https://img.blogweb.cn/article/b6a6a4fa68854a67833f4b3705a22f28.webp)
相比我们客户端使用的 Flex 布局,RN 中的 Flex 布局有稍许的不同,比如 flexDirection 的默认值是 column 而不是 row,flex 也只能指定一个数字值等。关于 Flex 布局的介绍可以参考:Flex 布局教程:语法篇、Flex 布局教程:实例篇
9. React Native 的热部署
最后我们介绍下 RN 中的热部署,这也是选择 RN 开发 APP 的一个重要原因之一。相比传统 APP 更新,大都需要第三方审核的流程,而这个流程可能会很慢或者不及时,遇到需要紧急修复的 bug 无法及时更新而导致直接的经济损失是很常见的问题,而 RN 的热部署可以一定程度上解决或减轻这一问题的影响。那么其实现原理是怎样的呢?
![React Native 混合开发与实现](https://img.blogweb.cn/article/da46cd5143c54b48bcab65ecd1247ee8.webp)
上图左侧部分便展现了用户访问 RN 应用的热部署流程。首先用户访问 APP,APP 会向 RN 服务器请求资源包,如果资源包未更新则读取本地缓存资源,如果开发者为了解决 bug 重新更新了服务器上的资源包,那么 APP 拉去后会缓存起来,待用户下次进入后再进行更新。这便是 RN 热部署的流程。
在本地开发时,我们不难发现当我们在运行起来的 RN 项目中修改代码时,再次从 APP 进入 RN 页面,本地终端会再次加载一次更新后的资源数据,这也是 RN 热部署的体现。
同样线上的热部署则需要将我们打包后的 RN 资源上传到服务器上供 APP 读取来实现。
![React Native 混合开发与实现](https://img.blogweb.cn/article/77f8f9f43f04496fb0fb1c0b070a6acd.webp)
我们可以手动执行打包、上传发布流程,当然为了减少人为干预,实现前端自动化,我们也可以把这块流程交给构建平台去自动打包部署,这便需要搭建一个后台系统进行管理。
结语
本文介绍了 React Native 混合开发的原理与实现逻辑。只有先了解原理,才能高效的投入项目的开发中,而关于 RN 自身的功能实现大家可以直接阅读官方文档,这里我也额外提供一些关于 RN 的参考资料:
注:本文部分图例参考自《React Native 移动开发实战》一书
转载自:https://juejin.cn/post/6844903913427206152