1、跨平台技术
目前主流的跨平台技术实现方案有以下:
1、H5 + 原生(Cordova、Ionic、微信小程序)
2、JavaScript 开发 + 原生 (React Native、Weex)
3、自绘 UI + 原生 (Qt mobile、Flutter)
2、简介
1、H5 + 原生(Hybrid)
概述:
这类框架主要原理就是将 App 中需要动态变动的内容通过 HTML5(简称 H5)来实现,通过原生的网页加载控件 WebView (Android)或 WKWebView(iOS)来加载。这种方案中,H5 部分是可以随时改变而不用发版,动态化需求能满足;同时,由于 H5 代码只需要一次开发,就能同时在 Android 和 iOS 两个平台运行,这也可以减小开发成本,也就是说,H5 部分功能越多,开发成本就越小。这种 H5 + 原生 的开发模式为混合开发 ,采用混合模式开发的 App 我们称之为混合应用或 HTMLybrid App ,如果一个应用的大多数功能都是 H5 实现的话,我们称其为 Web App 。
目前混合开发框架的典型代表有:Cordova、Ionic 。大多数 App 中都会有一些功能是 H5 开发的,至少目前为止,HTMLybrid App 仍然是最通用且最成熟的跨端解决方案。
特别提示:小程序,目前国内各家公司小程序应用层的开发技术栈是 Web 技术栈,而底层渲染方式基本都是 WebView 和原生相结合的方式。
混合开发技术点:
原生开发可以访问平台所有功能,而混合开发中,H5 代码是运行在 WebView 中,而 WebView 实质上就是一个浏览器内核,其 JavaScript 依然运行在一个权限受限的沙箱中,所以对于大多数系统能力都没有访问权限,如无法访问文件系统、不能使用蓝牙等。所以,对于 H5 不能实现的功能,就需要原生去做了。
混合框架一般都会在原生代码中预先实现一些访问系统能力的 API , 然后暴露给 WebView 以供 JavaScript 调用。这样一来,WebView 中 JavaScript 与原生 API 之间就需要一个通信的桥梁,主要负责 JavaScript 与原生之间传递调用消息,而消息的传递必须遵守一个标准的协议,它规定了消息的格式与含义,我们把依赖于 WebView 的用于在 JavaScript 与原生之间通信并实现了某种消息传输协议的工具称之为 WebView JavaScript Bridge , 简称 JsBridge,它也是混合开发框架的核心。
小结:
混合应用无非就是在原生中预先实现一系列 API 供 JavaScript 调用,让 JavaScript 有访问系统功能的能力。
混合应用的优点是:动态内容可以用 H5 开发,而 H5 是 Web 技术栈,Web 技术栈生态开放且社区资源丰富,整体开发效率高。缺点是性能体验不佳,对于复杂用户界面或动画,WebView 有时会不堪重任。
2、JavaScript 开发 + 原生:React Native、Weex
React Native (简称 RN )是 Facebook 于 2015 年 4 月开源的跨平台移动应用开发框架,是 Facebook 早先开源的 Web 框架 React 在原生移动应用平台的衍生产物,目前支持 iOS 和 Android 两个平台。RN 使用 JSX 语言(扩展后的 JavaScript,主要是可以在 JavaScript 中写 HTML 标签)和 CSS 来开发移动应用。
React 是一个响应式
的 Web 框架,两个重要的概念:DOM 树
与响应式编程
。
DOM树与控件树
文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标志语言的标准编程接口,一种独立于平台和语言的方式访问和修改一个文档的内容和结构。换句话说,这是表示和处理一个 HTML 或 XML 文档的标准接口。简单来说,DOM 就是文档树,与用户界面控件树对应,在前端开发中通常指 HTML 对应的渲染树,但广义的 DOM 也可以指 Android 中的 XML 布局文件对应的控件树,而术语DOM操作
就是指直接来操作渲染树(或控件树), 因此,可以看到其实 DOM 树和控件树是等价的概念,只不过前者常用于 Web 开发中,而后者常用于原生开发中。
响应式编程
React 中提出一个重要思想:状态改变则 UI 随之自动改变。而 React 框架本身就是响应用户状态改变的事件而执行重新构建用户界面的工作,这就是典型的 响应式
编程范式,下面我们总结一下 React 中响应式原理:
1、开发者只需关注状态转移(数据),当状态发生变化,React 框架会自动根据新的状态重新构建 UI。
2、React 框架在接收到用户状态改变通知后,会根据当前渲染树,结合最新的状态改变,通过 Diff 算法,计算出树中变化的部分,然后只更新变化的部分(DOM操作),从而避免整棵树重构,提高性能。状态变化后 React 框架并不会立即去计算并渲染 DOM 树的变化部分,相反,React 会在 DOM 树的基础上建立一个抽象层,即虚拟DOM
树,对数据和状态所做的任何改动,都会被自动且高效的同步到虚拟 DOM ,最后再批量同步到真实 DOM 中,而不是每次改变都去操作一下 DOM。
为什么不能每次改变都直接去操作 DOM 树?
这是因为在浏览器中每一次 DOM 操作都有可能引起浏览器的重绘或回流。如:如果 DOM 只是外观风格发生变化,如颜色变化,会导致浏览器重绘界面;如果 DOM 树的结构发生变化,如尺寸、布局、节点隐藏等导致,浏览器就需要回流。而浏览器的重绘和回流都是比较昂贵的操作,如果每一次改变都直接对 DOM 进行操作,这会带来性能问题,而批量操作只会触发一次 DOM 更新,会有更高的性能。
React Native
React Native 是 React 在原生移动应用平台的衍生产物,那两者主要的区别是什么呢?
主要的区别在于虚拟 DOM 映射的对象是什么。React 中虚拟 DOM 最终会映射为浏览器 DOM 树,而 RN 中虚拟 DOM 会通过 JavaScriptCore 映射为原生控件。
JavaScriptCore 是一个 JavaScript 解释器,它在 React Native 中主要作用如下:
1、为 JavaScript 提供运行环境。
2、是 JavaScript 与原生应用之间通信的桥梁,作用和 JsBridge 一样,事实上,在 iOS 中,很多 JsBridge 的实现都是基于 JavaScriptCore 。
RN 中将虚拟 DOM 映射为原生控件的过程主要分两步:
1、布局消息传递; 将虚拟 DOM 布局信息传递给原生;
2、原生根据布局信息通过对应的原生控件渲染;
至此,React Native 便实现了跨平台。 相对于混合应用,由于 React Native 是原生控件渲染
,所以性能会比混合应用中 H5 好一些,同时 React Native 提供了很多原生组件对应的 Web 组件,大多数情况下开发者只需要使用 Web 技术栈就能开发出 App。
Weex
Weex 是阿里巴巴于 2016 年发布的跨平台移动端开发框架,思想及原理和 React Native 类似,底层都是通过原生渲染的,不同是应用层开发语法 (即 DSL,Domain Specific Language):Weex 支持 Vue 语法和 Rax 语法,Rax 的 DSL(Domain Specific Language) 语法是基于 React JSX 语法而创造,而 RN 的 DSL 是基于 React 的,不支持 Vue。
小结
JavaScript 开发 + 原生渲染
的方式主要优点如下:
1、采用 Web 开发技术栈,社区庞大、上手快、开发成本相对较低。
2、原生渲染,性能相比 H5 提高很多。
3、动态化较好,支持热更新。
不足如下:
1、渲染时需要 JavaScript 和原生之间通信,在有些场景(如:拖动)可能会因为通信频繁导致卡顿。
2、JavaScript 为脚本语言,执行时需要解释执行 (JIT 即 Just In Time,指在执行时实时生成机器码),执行效率和编译类语言(AOT 即 Ahead Of Time,指在代码执行前已经将源码进行了预处理,这种预处理通常情况下是将源码编译为机器码或某种中间码)仍有差距。
3、由于渲染依赖原生控件,不同平台的控件需要单独维护,并且当系统更新时,社区控件可能会滞后;除此之外,其控件系统也会受到原生 UI 系统限制,例如,在 Android 中,手势冲突消歧规则是固定的,这在使用不同人写的控件嵌套时,手势冲突问题将会变得非常棘手。这就会导致,如果需要自定义原生渲染组件时,开发和维护成本过高。
3、自绘 UI + 原生:Qt Mobile、Flutter
思路:通过在不同平台实现一个统一接口的渲染引擎来绘制 UI,而不依赖系统原生控件,所以可以做到不同平台 UI 的一致性。
注意,自绘引擎解决的是 UI 的跨平台问题,如果涉及其他系统能力调用,依然要涉及原生开发。这种平台技术的优点如下:
1、性能高;由于自绘引擎是直接调用系统 API 来绘制 UI,所以性能和原生控件接近。
2、灵活、组件库易维护、UI 外观保真度和一致性高;由于 UI 渲染不依赖原生控件,也就不需要根据不同平台的控件单独维护一套组件库,所以代码容易维护。由于组件库是同一套代码、同一个渲染引擎,所以在不同平台,组件显示外观可以做到高保真和高一致性;另外,由于不依赖原生控件,也就不会受原生布局系统的限制,这样布局系统会非常灵活。
不足:
1、动态性不足;为了保证 UI 绘制性能,自绘 UI 系统一般都会采用 AOT 模式编译其发布包,所以应用发布后,不能像 Hybrid 和 RN 那些使用 JavaScript(JIT)作为开发语言的框架那样动态下发代码。
2、应用开发效率低:Qt 使用 C++ 作为其开发语言,而编程效率是直接会影响 App 开发效率的,C++ 作为一门静态语言,在 UI 开发方面灵活性不及 JavaScript 这样的动态语言,另外,C++ 需要开发者手动去管理内存分配,没有 JavaScript 及 Java 中垃圾回收(GC)的机制。
Flutter
Flutter 是 Google 发布的一个用于创建跨平台、高性能移动应用的框架。Flutter 和 Qt mobile 一样,都没有使用原生控件,相反都实现了一个自绘引擎,使用自身的布局、绘制系统。两者对比如下:
生态:Flutter 生态系统发展迅速,社区非常活跃。
技术支持:Google 大力推广。
开发效率:一套代码,多端运行;并且在开发过程中 Flutter 的热重载可帮助开发者快速地进行测试、构建 UI、添加功能并更快地修复错误。在 iOS 和 Android 模拟器或真机上可以实现毫秒级热重载,并且不会丢失状态。