Skip to content

跨端开发

为何需要跨端开发?

本质上,跨平台开发是为了增加代码复用,降低开发成本。在开发人员不充足时提供一套可选的方案。

跨端主流技术

React-Native: Facebook出品. 具备良好的扩展性(支持原生)与性能,且生态完善。技术栈为react+js。RN技术架构为原生Android+IOS+RN渲染,后续如果有需要,可以随时集成到原生应用中。

uniApp: 国内公司出品. 生态与性能与RN/Flutter相比较差,扩展安卓/IOS原生时需按照uniapp规范单独编写插件加载。优点是技术栈为Vue+js,前端研发学习成本低,但构建移动端应用时 底层跨平台编译的技术栈为weex,因活跃度太差目前weex已被apache投票停止维护。应用复杂时(例如IM通讯),可能会遇到性能瓶颈

Flutter: Google出品. 未深入研究,但考虑到框架成熟度,生态完整性和社区活跃度,前端研发学习成本,内部技术体系建设等多方面原因谨慎选择

  • 对比

image

React Native

learn once, write anywhere

架构理念

使用react语法的开发模式,但UI渲染、动画效果、网络请求等均由原生端实现。开发者编写的js代码,通过 react native 的**中间层(C++)**转化为原生控件

react native 的跨平台是实现主要由三层构成,其中 C++ 实现的动态链接库(.so),作为中间层桥接,实现了js端原生端双向通信与js解析

image

实现原理

  • JS代码中所写控件最终都会映射成Native的控件渲染。通信的数据在中间层会被转为String字符串传输,通讯交互会经历序列化与反序列化(产生性能损耗)

  • 运行时java层为逻辑入口,启动C++层的javascript解析器,执行js通过c++中间层传递来的渲染指令,从而构建NativeUI页面

image

js
// 一些常用的RN组件与Android原生控件之间的对应关系
Text -> TextView
Image -> ImageView
TextInput -> EditText
CheckBox -> AppCompatCheckBox
RefreshControl -> SwipeRefreshLayout
ScrollView -> ScrollView
Slider -> SeekBar
Switch -> SwitchCompat
  • 跨线程通信: Js Thread和原生之间交互与通讯必然是异步

在RN中,js运行在独立线程。JS Thread作为单线程逻辑,不可能处理耗时的操作。那么如fetch、图片加载、 数据持久化等操作,在Android中实际对应的是 okhttp 、Fresco 、SharedPreferences等。【通讯损耗

打包

JS代码会被打包成一个bundle文件,自动添加到App的资源目录下。打包最后会通过metro模块压缩bundle文件

bundle文件只会打包js代码,自然不会包含图片等静态资源,所以打包后的静态资源,其实是被拷贝到对应的平台资源文件夹中

图片等静态资源的映射规则:比如项目根目录下的 img/pic/logo.png 的资源,编译时会根据大小merged到对应的是drawable目录下,重命名称为img_pic_logo.png

打包脚本目录为/node_modules/react-native/local-cli

运行

在 RN App 里,所有的 JS 代码都会打包成一个 JS Bundle 文件保存在本地运行,当 RN App 运行时,一般会有三个线程:

  • 1、 JavaScript 线程:用于运行 JS Bundle
  • 2、 Native/UI 线程:运行 Native Modules 和处理 UI 渲染、用户手势等操作
  • 3、 Shadow 线程:在渲染之前计算元素的布局

引擎:V8、Hermes、JSCore

  • 不存在浏览器兼容性问题

React Natvie 现如今的方案是iOS用JSC(最优解:系统内嵌),Android用Hermes【开发人员可自行切换】

参考卤蛋实验室

JSCore

传统JavaScript引擎编译阶段只完成babel转义和minify压缩,产物index.android.bundle是经过压缩的JavaScript脚本文件,解释与执行的任务都需要在运行时完成(如V8引擎,还会在运行时将JavaScript编译为本地机器码)很明显缺点就是在运行时需要边解释边执行,甚至需要占用系统资源执行编译任务

Hermes(0.68开始默认)

Hermes引擎使用了aot编译的方式,将解释和编译过程前置到编译阶段,Hermes预编译后则是二进制文件,运行时只完成机器码的执行,大大提高了运行效率

Hermes Engine在React-native 0.60.2 版本后支持;构建产物大小与内存占用均有所减少

  • 预编译字节码(引擎加载二进制代码效率高于运行JS脚本)

  • 无JIT编译器(减小了引擎大小,优化内存占用,但直接运行JS脚本的性能差于V8和JSC)

参考卤蛋实验室

通讯:JSBridge vs JSInterface

JSBridge

使用 Bridge Module 来让 JS 和 Native 线程进行通信,每次利用 Bridge 发送数据时,都需要转换为 JSON, 而收到数据时也需要进行解码。这就意味着 JavaScript 和 Native 直接是隔离的,也就是 JS 线程不能直接调用 Native 线程上的方法

JSInterface

JSI 的全名是 JavaScript Interface,一个用 C++ 写的框架,作用是支持 JS 直接调用 Native 方法,而不是现在通过 Bridge 异步通讯,省去异步通讯的开销.

JSI 允许 JS 代码保存对 Native Modules 的引用,并且 JS 可以通过引用直接去调用 Native 上的方法

特点

  • JSI 将支持其他 JS 引擎
  • JSI 允许线程之间的同步相互执行,不需要 JSON 序列化等耗费性能的操作
  • JSI 是用 C++ 编写,以后如果针对电视、手表等其他系统,也可以很方便地移植

新架构

  • JSInterface: 全新的JS与Native Modules通讯中间层,可直接调用原生方法,省去异步通讯开销

  • Fabric Renderer: 全新的渲染系统,它将取代当前的 UI Manager; 于0.68版本开始支持【Node > 14】

  • Turbo Modules: 减少不必要的初始模块加载,缩短 RN 应用的启动时间; 于0.68版本开始支持

  • Codegen: 用于保证 JS 代码和 C++ 的 JSI 可以正常通信的静态类型检查器

参考