React Native: Learn once,Write anywhere!
Java: Write once, Run anywhere!
跨平台的方案发展历史
-
web容器时代: 基于web等相关技术通过浏览器实现界面与功能。典型的框架有Cordova 、ionic、微信小程序 浏览器加载H5页面的过程如下:
初始化 webview -> 请求页面 -> 下载数据 -> 解析HTML -> 请求 js/css 资源 -> dom 渲染 -> 解析 JS 执行 -> JS 请求数据 -> 解析渲染 -> 下载渲染图片
如图: 在此过程中会经历一系列性能以及用户体验的问题
涉及到系统的能力(拍照,定位等)还需要做相应的JSBridge,这在提升开发效率的同时也损失了一些性能和用户体验。
-
泛web容器化时代: 采用web标准进行开发,核心在于页面的渲染绘制在运行时交给了原生系统。典型框架有React Native、Weex 以及快应用
在业务实现中,我们只用到了web标准中小部分内容,为了达到开发效率与性能稳定的平衡,就放弃了浏览器的控件渲染能力,而是采用原生组件的渲染。
-
自绘引擎时代:
做过React Native 或者 Weex 开发的小伙伴都知道,有时候我们为了实现一个控件,iOS 和Android 两个平台的代码都要进行适配,React Native没有提供统一的API; 甚至API发生变化,又得升级改造,尤其RN在开始的时候版本的迭代速度是很快的,这个改造过程非常痛苦,会遇到各种奇奇怪怪的bug。这个时候Flutter的出现,让很多人看到了一丝丝希望。
Flutter 从头到尾重写了一套跨平台的UI框架, 在页面布局方面做到了一套代码多个平台运行, 并且支持JIT 和 AOT
Flutter 是移动UI框架只是接管了渲染层**
Flutter 介绍
Flutter 是基于Google的物联网操作系统FuchsiaOS(这是Google 除了ChromeOS,Android外的第三个操作系统 )的UI框架, 基于Zircon内核,而不是Android 用的linux内核,他对硬件的要求较低。
Flutter 的架构设计
Embedder 是操作系统适配层,实现了渲染 Surface 设置,线程设置,以及平台插件等平台相关特性的适配。从这里我们可以看到,Flutter 平台相关特性并不多,这就使得从框架层面保持跨端一致性的成本相对较低。
Engine 层主要包含 Skia、Dart 和 Text,实现了 Flutter 的渲染引擎、文字排版、事件处理和 Dart 运行时等功能。Skia 和 Text 为上层接口提供了调用底层渲染和排版的能力,Dart 则为 Flutter 提供了运行时调用 Dart 和渲染引擎的能力。而 Engine 层的作用,则是将它们组合起来,从它们生成的数据中实现视图渲染。
Framework 层则是一个用 Dart 实现的 UI SDK,包含了动画、图形绘制和手势识别等功能。为了在绘制控件等固定样式的图形时提供更直观、更方便的接口,Flutter 还基于这些基础能力,根据 Material 和 Cupertino 两种视觉设计风格封装了一套 UI 组件库。我们在开发 Flutter 的时候,可以直接使用这些组件库。
Hot Reload
Flutter
支持亚秒级热重载,Android Studio
和VSCode
都支持Hot Reload
的特性。但需要区分的是,热重载和热更新是不同的两个概念,热重载是在运行调试状态下,将新代码直接更新到执行中的二进制。而热更新是在上线后,通过Runtime
或其他方式,改变现有执行逻辑AOT && JIT
JIT: Just in Time 即时编译 边解释边执行,开发测试效率高,但运行速度和执行性能则会因为运行时即时编译受到影响 一个程序在它运行的时候创建并且运行了全新的代码,而并非那些最初作为这个程序的一部分保存在硬盘上的固有的代码。
AOT: Ahead of Time 运行前编译 可以生成被直接执行的二进制代码,运行速度快、执行性能表现好,但每次执行前都需要提前编译,开发测试效率低。
在android里面,JIT会把程序实时编译为机器字节码,然后虚拟机读取,打开程序~而AOT是在安装的时候预先打包好了字节码放在手机内部储存里面,打开程序直接读取预先打包好的玩意就可以了
Flutter
支持AOT
(Ahead of time
)和JIT
(Just in time
)两种编译模式,JIT
模式支持在运行过程中进行Hot Reload
。刷新过程是一个增量的过程,由系统对本次和上次的代码做一次snapshot
,将新的代码注入到DartVM
中进行刷新。但有时会不能进行Hot Reload
,此时进行一次全量的Hot Reload
即可。而
AOT
模式则是在运行前预先编译好,这样在每次运行过程中就不需要进行分析、编译,此模式的运行速度是最快的。Flutter
同时采用了两种方案,在开发阶段采用JIT
模式进行开发,在release
阶段采用AOT
模式,将代码打包为二进制进行发布。在开发原生应用时,每次修改代码后都需要重新编译,并且运行到硬件设备上。由于
Flutter
支持Hot Reload
,可以进行热重载,对项目的开发效率有很大的提升。由于
Flutter
实现机制支持JIT
的原因,理论上来说是支持热更新以及服务器下发代码的。可以从服务器。但是由于这样会使性能变差,而且还有审核的问题,所以Flutter
并没有采用这种方案。Flutter使用的开发语言---Dart
Dart 是谷歌开发的一种通用编程语言,亮相于2011年的GOTO大会上 它被用于构建 Web、服务器、桌面和移动应用程序
Dart是面向对象的、类定义的、单继承的语言。它的语法类似C语言,可以转译为JavaScript,支持接口(
interfaces)
、混入(mixins)
、抽象类(abstract classes)
、具体化泛型(reified generics)
、可选类型(optional typing)
和sound type system
。最初设计Dart,是Google的一帮程序员出于对JavaScript的不满,决定自己搞一个新语言用来替换JavaScript的,所以刚开始Dart也就是用来作为浏览器脚本运行在浏览器中的。为了推广Dart,Google在Chrome中内置了DartVM的引擎。有了运行环境,加上用户群的可观数量,Dart最初也赢得了部分前端开发者的青睐。但是随着NodeJS 的火热,JavaScript焕发了第二春,可以说是在编程界无孔不入,几乎成就了JS程序员口中的“凡是能被JS实现的,最终都要被JS实现”的愿景,前端的开发模式因此而改变。再加上React、React-Native、Vue等框架的崛起,一句话总结就是JavaScript很忙。
Dart 逐渐被开发者遗忘了,2015年 Google将内置的Dart 引擎从Chrome中移除,但是Google 内部孵化了一个跨平台项目—Flutter ,同时Dart在服务端和浏览器前端也在不遗余力的发展(服务端可以编写命令行程序,前端可以编译成Javascript运行在浏览器中), Google将Dart 作为了操作系统Fuchsia的官方开发语言
Dart 的特性
- 单线程异步事件模型
- 强类型推断
- 独特的
isolate
来实现多线程 - 面向对象
- 全平台通吃
- …..
Javascript 和 Dart 的对比
Dart默认是单线程任务处理,如果不开启新的线程,任务默认在主线程中处理; Javascript也是单线程,但是浏览器是多进程.
同样是单线程,怎么实现并发?
作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM,这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?所以为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征. 另外为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
Javascript 是单线程,但是不代表他就是在一个单线程中完成 html 解析、 构建Dom树和Render树 、布局、 绘制等等一系列的操作。这些操作是由浏览器内核在多个线程的协同写完成的,比如: GUI 渲染线程、JS引擎线程、事件触发线程、定时器线程、异步请求线程...
JS引擎执行异步代码而不用等待,是因有为有 消息队列和事件循环. 消息就是注册异步任务时添加的回调函数
主线程在执行完当前循环中的所有代码后,就会到消息队列取出这条消息(也就是message函数),并执行它。到此为止,就完成了工作线程对主线程的通知,回调函数也就得到了执行。如果一开始主线程就没有提供回调函数,AJAX线程在收到HTTP响应后,也就没必要通知主线程,从而也没必要往消息队列放消息
Dart 是怎样实现并发的呢?在Flutter 中引入了isolate
的概念,isolate
(隔离区)与常规的线程有较大的区别(线程之间不共享内存)可以视为一个Worker,每一个Worker中维护着MicroTask 和 Event 队列,执行逻辑如下图:
更多细节我们后续有专门的章节为大家讲解。
Dart 与 Javascript的优势在哪里?
Javascript 优势在于 标准统一, 应用范围广,正如Atwood 定律描述: 凡是能用Javascript写出来的系统,最终都会用Javascript写出来。目前由于Node.js 、Vue、React、Angular等框架的出现,让Javascript成了前后端通吃的全栈开发语言。 Javascript 借鉴了其他语言的特性: 函数是一等公民 、 原型链等...
Dart 是Google Fuchsia系统的官方开发语言,能够解决Javascript本身存在的缺陷
Google 为何不直接采用Javascript作为跨平台开发语言而是采用Dart?
- Dart* 支持 AOT 和 JIT ,支持热重载, 在开发过程中可以动态下发代码,升开发效率; 而 JavaScript 支持 JIT
- Dart 有Google 爸爸的强力支持
- Javascript 存在性能以及与Native系统的通信需要做很多Bridge 的问题
- Javascript 标准的确认需要提交评审委员会审理,周期长太长,google还是希望能够尽快完成生态的布局
Flutter 会支持热更新
Flutter高性能的其中一个重要因素就是因为Dart在发布期是AOT,目前阶段支持的可能性比较小, 另外苹果也不允许除了JSCore之外的其他动态编译/解释执行环境。
Flutter 为何不支持直接操作摄像机、GPS等硬件?
按照我们的理解做一个APP大部分业务功能都是页面的布局渲染,很少牵涉到调用摄像机等底层的硬件设备。如果Flutter 作为一个UI渲染引擎,也要做这个的话其实就是做了一个操作系统了。
什么时候用Native 开发?什么时候用Flutter开发 ?
原生适用于需要频繁与操作系统底层能力进行交互, 大部分纯渲染交互的场景用Flutter就可以了
当前跨平台方案的对比
Tips
怎样判断一门语言是否是AOT还是JIT? 通常来说,看代码在执行前是否需要编译即可。如果需要编译,通常属于 AOT;如果不需要,则属于 JIT. AOT 的典型代表是 C/C++,它们必须在执行前编译成机器码; 而JIT 的代表则包含了如 lua、Javascript 、Python 等几乎所有的脚本语言
-
怎样判断一个技术能否成为大前端的主流? 判断一个技术是否能成为未来大前端主流技术发展的趋势,主要参考以下几方面:
- 是否能减少对底层宿主环境的依赖
- 能否隔离各终端系统差异
- 能否从原理和运行机制及生态等方面优于现有技术,或者为现有技术瓶颈提供新的解决方案
- 能够为开发者提供统一且标准化的能力
跨平台方案的选择要注意的方面
不能仅仅依赖编程语言、性能、技术架构等因素,要从开发效率、社区活跃度社区支持、构建发布、CI等工程化方面考虑
大前端的永恒主题: 效率、质量、性能 用尽量完整的技术栈和工具链去隔离各终端系统差异,向开发者提供统一而标准化的能力。结合这三大主题看看新技术是否能够推动去解决业务现阶段的实际问题,以及具体的落地场景。如果只是把”完成“落地作为目标,而忽略了我们具体要通过它去解决什么问题,可能是本末倒置了,所以我们要有升维思考 降维打击的思考力。
到此我们已经了解了Flutter 相关的背景知识,留给大家一个问题:Flutter是如何渲染的?
参考链接:
Flutter's Rendering Pipeline
深入理解跨平台方案的历史发展逻辑
React Native、Flutter等跨平台方案怎么选?
Android中的AOT、JIT
Code Push / Hot Update / out of band updates
javac 编译与JIT编译
Flutter热更新与热加载