一、混合开发
App 混合开发,指的是一个 App 部分功能用 Native 构建,其他功能使用跨端框架进行构建,最常见的场景是,Native 作为一个可工程,其实业务开发使用垮端框架进行开发。目前,比较流行的跨端框架有 H5、React Native、Flutter、布局动态化等。
而在以 Native 与 React Native 混合开发中,同一个 App 中,混合开发通常有以下几种形态:
那究竟有哪些公司在使用 React Native 呢?
首先,是一些大型 App 中,比如美团、携程、京东、58 等。这些大型 App 一般都非常复杂,整个框架需要包含模块化、组件化、插件化、跨端等能力。相比纯 React Native 工程,大型 App 的实际业务开发还需要考虑如何在已有原生项目中引入 React Native,作为原生框架的扩展能力。比如,部分业务可能对开发效率、多端一致性、动态更新能力有较强要求,就可以使用 React Native 来实现。
除了大型 App 外,如果想要在已上线的项目引入 React Native,也需要使用混合开发模式。因为原生改造成 React Native 并不是那么简单的事情,毕竟开发语言、页面管理、模块管理完全是另一套东西,而且一些原生页面,如启动页、首页等,出于性能考虑,大都还是会选择原生来实现,对一些业务要求不高的业务则使用 React Native 进行开发。
当然,对于一些新开发的轻量级 App,完全可以选择纯 React Native 模式。因为新开发的 App 没有技术债,可以从 0 到 1 享受 React Native 跨端的优势。那使用 React Native 模式有哪些优势呢?
- 开发效率高,一套代码可以在 Android、iOS 上运行;
- 更新部署方便,无须依赖应用市场发版,迭代更新速度快;
- 具备动态更新能力,特别是国内 App,以 Android 为例,受限于 Google Play,无法使用 Android App bundle,而插件化框架又存在稳定性问题,而 React Native 可以解决这一痛点。
当然,也有一些缺点:
- 性能不佳:H5 渲染链路长;React Native 依托于 JS bridge 交互 (旧版,最新架构使用 JSI);虽然 Flutter 绘制流程直接使用 Skia,但依赖于原生的能力仍需异步交互;
- 兼容性差:Android、iOS 各版本都存在各种兼容性问题,特别是 Android 碎片化严重;
- 问题排查成本高:跨端框架一般涉及 Native、FE、Server,中间做了大量的桥接转换,排查链路比纯 Native 长;
- 动态化能力受限:相比纯原生的插件化,跨端框架动态更新的业务如果涉及 Native 部分的组件更新,需要依赖 App 发版。
不过,虽然跨平台有自己的缺点,但是很多大型 App 都是会选择它,至于是选择 H5 + React Native + 布局动态化,或者 H5 + Flutter,需要依据业务场景、包大小、性能、运行内存、动态更新能力为标准进行选取。
那如果从 0 到 1 搭建一个混合项目呢?
二、环境搭建
混合开发需要原生环境的支持,所以请确保本地已经配置了原生 Android 和 iOS 的开发环境。
2.1 Android 混合开发
2.1.1 创建 Android 工程
首先,使用 Android Studio 创建一个新的 App 项目,如果你已经有了本地项目,可以跳过此步骤。填写完项目名称、包名、项目本地路径后,点击 “Finish” 按钮。你可以把这个项目名称取名为 “GeekTimeRNAndroid”。
2.1.2 添加 React Native 依赖
创建好本地工程后,我们就要给它添加依赖。其实,React Native 官方对集成到现有的原生应用提供了相应的 文档 。
按照官方文档的提示,我们需要“创建一个空目录用于存放 React Native 项目,然后在其中创建一个 /android 子目录,把你现有的 Android 项目拷贝到 /android 子目录中”。
当然,官方提供的方式是非常不错的,它偏向于 React Native 的工程管理模式。而我们在实际开发中,特别是已经上线的项目里面,React Native 功能和其他业务功能一样,一般会被当成原生工程的子模块来管理,所以我们这边选择偏向混合工程管理方式来集成。
下面是 RN 混合开发的几种存在方式:
可以看到,对比官方的接入模式,原生混合 RN 的工程模式有以下好处:
- 可以不侵入现有工程结构,React Native 模块会作为现有工程的一部分进行组织管理。
- 不会影响代码仓库管理,不用把 Android、iOS 放在同一代码仓库下进行管理。
- 混合模式方便我们进行组件功能复用,可以将 React Native 模块独立成组件,提供给其他 App 平台使用。
需要说明的是,使用上面的方式集成 React Native 模块时,需要添加 react-native 和 JavaScript 引擎的依赖。
- react-native:React Native 的核心框架;
- JavaScript 引擎:可选 JSC、Hermes,用于执行 JavaScript。
接下来,我们参考官方文档,添加 React Native 依赖,如下。
# 切换到刚刚新建好的工程目录 cd /Users/Mac/RN/RNAndroid # 执行添加 react-native 命令,yarn 或 npm 都可以 yarn add react-native
执行 yarn add react-native 命令后,默认会安装最新版本的 React Native 包。执行以上命令成功之后,我们会发现 RNAndroid 工程下多了一个 node_modules 目录,里面不仅有 react-native 框架,还有 JavaScript 引擎编译好的产物,包括 aar 和 pom 依赖文件。接下来,我们可以参考官方提供的方式,将 node_modules 目录配置为 repository,然后在工程中引入相关依赖。
不过,这种方式并不太推荐,其实我们只需要 react-native 和 JavaScript 引擎这两个产物就可以了。获取这两个产物后,在 Android 自己进行二次封装,然后发布到公司的远程仓库。
接着打开 node_module 中的 react-native 框架编译产物,位于…/RNAndroid/node_modules/react-native
目录,如下图。
node_module 中的 JSC 引擎编译产物位于…/RNAndroid/node_modules/JavaScriptc-android
目录。
除此之外,新版本的 RN 还接入了 Hermes 引擎,node_module 中的 Hermes 引擎编译产物位于…/RNAndroid/node_modules/hermes-engine
目录。
对于 JSC 引擎和 Hermes 引擎,我们需要注意以下两点常识:
- 在启动性能上,Hermes 比 JSC 更快。Hermes 采用的是 AOT 提前编译的方案,它支持字节码文件,相比于 JSC,Hermes 不用先将 JavaScript 文本编译为字节码,节约了编译字节码的耗时,自然启动性能更好。
- 在运行性能上,JSC 比 Herems 更快。JSC 使用的是 JIT 即时编译方案,该方案支持动态缓存热点代码,因此运行性能上更快。
但整体而言,由于 Hermes 引擎是专门为移动端定制的,在引擎大小、启动速度、运行内存、通信性能等方面都优于 JSC。
接下来,我们继续被 RNAndroid 工程添加 react native 相关的依赖,包括:
- react-native.arr 文件;
- react-native.aar 依赖的第三方库;
- JavaScript 引擎 aar 文件。
首先,我们添加 react-native.arr 文件。我们将上面的 react-native.arr 拷贝放置到 RNAndroid/libs 目录下,然后添加依赖。
implementation fileTree(dir: 'libs',includes: ['*.jar','*.aar']) implementation(name:'react-native-0.68.2', ext:'aar')
接着,我们再将 …/RNAndroid/node_modules/react-native/ 目录下的 react-native-0.68.2.pom 中的依赖库,按照 android gradle 依赖的方式进行添加,这些依赖主要是 react-native aar 本身远程依赖的第三方库。添加好的build.gradle 如下:
dependencies { implementation(name:'react-native-0.68.2', ext:'aar') implementation 'com.facebook.infer.annotation:infer-annotation:0.18.0' implementation 'javax.inject:javax.inject:1' implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'com.facebook.fresco:imagepipeline-okhttp3:2.5.0' implementation 'com.facebook.fresco:fresco:2.5.0' implementation 'com.facebook.soloader:soloader:0.10.3' implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation 'com.squareup.okhttp3:okhttp:4.9.2' implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.9.2' implementation 'com.squareup.okio:okio:2.9.0' ... //省略其他依赖 }
接下来,我们继续添加 JavaScript 引擎 aar 包。打开 …/RNAndroid/node_modules/hermes-engine
目录下的 hermes-cppruntime-release.aar & hermes-release.aar
,然后拷贝到 libs 目录,并在 build.gradle 中添加依赖。
dependencies { implementation(name:'android-jsc-r250230', ext:'aar') ... //省略其他依赖 }
2.1.3 权限配置
添加依赖后,接下来还需要进行权限的配置,打开混合工程的 AndroidManifest.xml 清单文件中,然后添加网络权限和访问开发者菜单。
<uses-permission android:name="android.permission.INTERNET" /> <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
至此,Android 混合工程中的 React Native 环境配置就已经完成了。接下来我们再看看,如何在 iOS 中进行 React Native 环境配置。