跨平台框架
什么是跨平台框架?
这里的多个平台一般是指 iOS 和 Android 。
为什么需要跨平台框架?
目前,移动开发技术主要分为原生开发和跨平台开发两种。其中,原生应用是指在某个特定的移动平台上,使用平台所支持的开发工具和语言,直接调用系统提供的 API 所开发的应用。
其优势体现在:
- 可以快速访问本平台的全部功能,比如摄像头、 GPS 等;
- 原生应用的速度快、性能高,而且可以实现比较复杂的动画和绘制效果,用户体验较好。
缺点也很明显,主要体现在:
- 开发成本较高,不同的平台必须维护不同的代码,人力成本也会随之增加;
- 有新的功能需要更新时,只能进行版本升级。
针对原生开发所面临的问题,目前已经诞生了许多跨平台框架
跨平台框架都有哪些?
根据其原理,主要分为三类:
- H5 + 原生( Cordova 、 Ionic 、微信小程序);
其主要原理就是将 APP 的一部分需要动态改变的内容通过 H5 来实现,通过原生的网页加载控件 WebView ( Android )或 WKWebView ( iOS )来加载。
- JavaScript 开发 + 原生渲染( React Native 、 Weex 、快应用);
采用 Web 技术栈,将 DOM 映射为原生控件树,体验好,接近原生。
- 自绘 UI + 原生( Flutter );
实现了一个自绘引擎,使用自身的布局、绘制系统,所以不同平台效果能高度统一。
Flutter 简介
Flutter is Google’s portable UI toolkit for building beautiful, natively-compiled applications for mobile, web, and desktop from a single codebase.
Flutter 是谷歌推出的便携式 UI 工具包,可以跨平台的构建精美的、原生体验的 Mobile 、 Web 、 Desktop 应用。
Flutter 的发展历程
2017年 Google I/O 大会上, Google 首次推出了一款新的用于创建跨平台、高性能的移动应用框架 —— Flutter 。
2018年2月, Flutter 发布了第一个 Beta 版本。
2018年5月,在2018年 Google I/O 大会上, Beta3 版本发布。
2018年6月, Flutter 发布了首个预览版本,这意味着 Flutter 进入了正式版(1.0)发布前的最后阶段。
2018年12月,1.0版本发布。
哪些企业在用 Flutter ?
- 阿里巴巴(闲鱼)
- 美团( B 端)
- ……
Flutter的的优点与缺点
优点:
- 性能高:由于自绘引擎是直接调用系统 API 来绘制 UI ,所以性能和原生控件接近。
- 灵活,易维护:由于 UI 渲染不依赖原生控件,不会受原生布局系统的限制,这样布局系统会非常灵活;且不需要根据不同平台的控件单独维护一套组件库,所以代码容易维护。
- 开发体验好:由于组件库是同一套代码、同一个渲染引擎,所以在不同平台,组件显示外观可以做到高保真和高一致性。
缺点:
- 动态性不足:为了保证 UI 绘制性能,自绘 UI 系统一般都会采用 AOT 模式编译其发布包,所以应用发布后,不能像 Hybrid 和 React Native 那些使用 JavaScript ( JIT )作为开发语言的框架那样动态下发代码。
React Native 简介
这句话是 React Native 官网 对 React Native 的概述,简单明了地概括了 React Native 的特点和优点。
React Native 的发展历程
哪些企业在用 React Native ?
- 腾讯( QQ 空间、 QQ 音乐)
- 百度(手机百度)
- 京东
- ……
React Native 的优点与缺点
优点:
- 社区庞大、上手快、开发成本相对较低。
- 原生渲染,性能相比 H5 提高很多。
- 动态化较好,支持热更新。
缺点:
- 渲染时需要 JavaScript 和原生之间通信,某些场景下如拖动可能会因为通信频繁导致卡顿。
- JavaScript 为脚本语言,执行时需要 JIT ,执行效率和 AOT 代码仍有差距。
- 由于渲染依赖原生控件,不同平台的控件需要单独维护,并且当系统更新时,社区控件可能会滞后;除此之外,其控件系统也会受到原生 UI 系统限制。
React Native 和 Flutter 的对比
—— | React Native | Flutter | 相同点 | 结论 |
---|---|---|---|---|
环境搭建 | npm 、 node 、 react-native-cli 等 | Flutter SDK 和 Android Studio / VSCode 上的 Dart 与 Flutter 插件 | JDK 、 Android SDK 、 Xcode 等 | 首次配置运行成功率: Flutter > React Native |
实现原理 | React Native 是一套 UI 框架,默认情况下 React Native 会在 Activity 下加载 JavaScript 文件,然后运行在 JavaScriptCore 中解析 Bundle 文件布局,最终堆叠出一系列的原生控件进行渲染 | Flutter 中绝大部分的 Widget 都与平台无关, 开发者基于 Framework 开发 App ,而 Framework 运行在 Engine 之上,由 Engine 进行适配和跨平台支持。这个跨平台的支持过程,其实就是将 Flutter UI 中的 Widget “数据化”,然后通过 Engine 上的 Skia 直接绘制到屏幕上 | 在 Android 和 iOS 上,默认情况下 Flutter 和 React Native 都需要一个原生平台的 Activity / ViewController 支持,且在原生层面属于一个“单页面应用” | 平台关联性: React Native > Flutter UI |
编程开发(语言) | JavaScript 是动态语言 | Dart 是伪动态语言的强类型语言 | 支持通过 var 定义变量,支持 async / await 语法糖,支持 Promise ( Future ) 等链式异步处理 | 开发便捷度: JavaScript > Dart 类型安全和重构代码: Dart > JavaScript |
编程开发(界面开发) | 延续了 React 的开发风格,不同之处就是更换标签名,并且样式和属性支持因为平台兼容做了删减 | 一切皆为 Widget 。控件嵌套和样式代码不分离 | 平台相关性: React Native > Flutter | |
编程开发(状态管理) | 在 Component 内初始化一个 this.state 变量,然后通过 this.state.name 访问,内部实现受 React diff 等影响 | 继承 StatefulWidget ,然后在 State 对象内通过变量直接访问和 setState 触发更新,内部实现受 isRepaintBoundary 、 markNeedsBuild 等影响 | 调用 setState 更新,且操作不是立即生效的 | |
编程开发(原生控件) | 整个渲染过程都在原生层中完成,所以接入原生控件并不难 | Flutter 的整体渲染脱离了原生层,直接和 GPU 交互,导致无法直接接入原生控件 | 混合开发支持: React Native > Flutter | |
插件开发 | npm 插件。好处:可以使用丰富的 npm 插件生态,同时减少前端开发者的学习成本。坏处: npm 包依赖复杂。每个项目都有一个 node_modules ,占用空间(现在, yarn pnp 提供了一种更加高效的模块查找机制,消除了项目中的 node_modules ,所有的依赖包都在一个公共目录里面) | pub 插件。 packages get 文件一般保存在电脑的统一位置,多个项目都引用着同一份插件 | 支持插件开发 | 体验: Flutter > React Native |
编译产物 | 编译后的文件主要是 bundle 文件,在 Android 中是 index.android.bunlde 文件,而在 iOS 下是 main.jsbundle | 在 Android 主要是: isolate_snapshot_instr (应用程序指令段)、 isolate_snapshot_data (应用程序数据段)、 vm_snapshot_data (虚拟机数据段)、 vm_snapshot_instr (虚拟机指令段)等产物。在 iOS 主要是 App.framework ,其内部也包含了 kDartVmSnapshotData、kDartVmSnapshotInstructions、kDartIsolateSnapshotData、kDartIsolateSnapshotInstructions 四个部分 | ||
性能 | Dart 支持的 isolate ,属于完完全全的异步线程处理,可以通过 Port 快捷地进行异步交互,这大大拓展了 Flutter 在 Dart 层面的性能优势。 | JavaScript 和 Dart 都是单线程应用,利用了协程的概念实现异步效果 | 理论性能: Flutter > React Native | |
稳定性 | Github 上 open issue 只有700多(2020.06),整体上稳定性已经达到生产环境的要求,但由于对原生组件的依赖,随着 Android、iOS 的系统迭代,未来仍需要持续的完善稳定性和兼容性 | Github 上 open issue 达到7000+(2020.06),在稳定性方面还有很长的路要走 | 稳定性: React Native > Flutter | |
发展未来 | 在0.59版本开始支持 React Hook 等特性,并将原本平台的特性控件从 React Native 内部剥离到社区,这样控件的单独升级维护可以更加便捷,同时让 React Native 与 React 之间的界限越发模糊。但是由于平台关联性太强,这些年发展较为缓慢 | Flutter UI 的平台无关性,让 Flutter 在跨平台的拓展上更为迅速 |
如何选择 React Native 或 Flutter
选择 React Native,如果你希望
- 从前端工程师或 Web 开发者的视角接触跨平台开发
- 拥有完善的工具链和成熟的生态系统
- 缩短产品的发布周期
选择 Flutter,如果你希望
- 从原生开发者的视角接触跨平台开发
- 关注产品性能和开发体验(无需处理平台差异)
React Native 工作原理
使用 React Native 开发应用,我们通过 React 来构建应用。用 React 实现的组件会被 React 框架组织成“虚拟 DOM ”的形式,然后将“虚拟 DOM ”的渲染和交互映射到原生的视图上面。
React 执行渲染(组件的装载和卸载、样式变化)操作后, JavaScript 会通知到 Native 端进行原生视图的渲染,原生视图的用户交互事件触发后,会通知 JavaScript 端声明的回调。
React Native 在与原生框架通信中,采用了 JavaScriptCore 作为 JavaScript Virtual Machine ( JS VM ),中间通过 JSON 文件与 Bridge 进行通信。若使用 Chrome 浏览器进行调试,那么所有的 JavaScript 代码都将运行在 Chrome 的 V8 引擎中,与原生代码通过 WebSocket 进行通信。
一个 JS VM 实例代表一个执行 JavaScript 的自包含( self-contained )的环境。你可以用这个类做两件事情: ① JavaScript 的并发执行; ② 桥接 JavaScript 和 Objective-C 或 Swift 的对象的内存管理。
JavaScript 代码并不是被编译成 Native 代码,而是在各个平台的 JS VM 中被执行的。在 iOS 中, JS VM 是 iOS 系统提供的 JavaScriptCore ;而在 Android 中,是 React Native 这个框架提供的,打包进 APK 中的的某个版本的 JavaScriptCore (或者是 Facebook 的 Hermes 引擎)。
JavaScriptCore 主要功能是解析执行 JavaScript 脚本。它支持在原生环境( Objective-C 、 Swift 、 C )中执行 JavaScript 代码,同时支持把原生对象注入到 JavaScript 环境中使用,结合这两点 JavaScriptCore 就具备 JavaScript & Native 交互的能力。 React Native 正是利用它的这一特性,在 JavaScriptCore 的基础上构建一座桥梁( Bridge ),使得 Native 与 JavaScript 可以高效且便捷地互相调用,这便是 React Native 框架的核心。
React Native 部署调试
项目运行
iOS 平台:
- 使用 Xcode 打开 ios/.xcodeproj ,选择模拟器后点击运行按钮
react-native run-ios --simulator "iPhone 8"
xcrun simctl list devices
可以获取到 Xcode 下所有可用的设备列表
Android 平台:
执行react-native start
,打开 Android Studio
- 选择 Configure -> AVD Manager , 点击运行按钮
- 打开项目, 点击运行按钮
开发调试
- 开发者菜单( iOS : Command + D, Android : Command + M)
- Chrome + React Developer Tools
React Developer Tools :
安装:npm install -g react-devtools
运行:react-devtools
- React Native Debugger
安装:
brew update && brew cask install react-native-debugger
- 真机调试
以 Android 为例:
- 开启 USB 调试
- 使用
adb devices
命令可以检查设备是否正确连接到 ADB ( Android Debug Bridge ),右边那列看到 device 说明你的设备已经被正确连接了。
注意,你每次只应当连接一个设备。如果你连接了多个设备(包含模拟器在内),后续的一些操作可能会失败。