04.跨端开发

混合开发/跨端开发实践

原生与 Web 的不足

原生应用程序在我们的日常生活中颇受欢迎,但它们在很多层面上也有改善的空间。

  • 在用户从原生应用程序获取服务之前,他 / 她往往要先下载 - >安装 - >注册应用程序才行。由于存储能力的限制,用户只能在设备上保留数量有限的原生应用。在不同的原生应用程序之间共享数据并不容易。

  • 要开发原生应用时,开发人员可能需要学习一些新的编程语言。为了向各个平台的原生应用程序提供相同的服务,开发人员可能需要为不同的平台维护重复的产品。

Web 是能够避免这些问题的一个理想平台,但到目前为止,它仍然不够完美。

  • 与原生应用相比,Web 应用很难利用系统提供的功能。此外 Web 应用程序的性能很难媲美或超过功能类似的原生应用程序。

  • 在移动设备上,用户经常要获取浏览器之外的服务或内容;显然,他们希望系统中的所有应用程序在用户帐户、登录状态和用户交互上保持一致。此外,有时用户可能希望与应用程序共享某些数据(如果他们真的信任它),但是一些经常请求的信息(例如当前设备的个人移动电话号码或联系人列表)很难在 Web 应用上提供许可。

混合开发

WebView

Cordova 的基础是 html 和 js 运行在 webview 容器里面,通过 Cordova 提供的接口与硬件通讯;所以它的效率天生收到限制,而且也受到了各个厂商对 webkit 内核的好坏;比如之前基于国产某 Cloud 的程序,在华为手机上显示就不正常,花费了不少精力修改;

RN 的效率由于是将 View 编译成了原生 View,所以效率上要比基于 Cordova 的 HTML5 高很多,但是它也有效率问题,RN 的渲染机制是基于前端框架的考虑,复杂的 UI 渲染是需要依赖多个 view 叠加.比如我们渲染一个复杂的 ListView,每一个小的控件,都是一个 native 的 view,然后相互组合叠加.想想此时如果我们的 list 再需要滑动刷新,会有多少个对象需要渲染.所以也就有了前面所说的 RN 的列表方案不友好; Flutter  吸收了前两者的教训之后,在渲染技术上,选择了自己实现(GDI),由于有更好的可控性,使用了新的语言 Dart,避免了 RN 的那种通过桥接器与 Javascript 通讯导致效率低下的问题,所以在性能方面比 RN 更高一筹;有经验的开发者可以打开 Android 手机开发者选项里面的显示边界布局,发现 Flutter 的布局是一个整体.说明 Flutter 的渲染没用使用原生控件进行渲染

HTML5 于 2007 年在 W3C 立项,与 iPhone 发布同年。乔布斯曾期待 HTML5 能帮助 iPhone 打造起应用生态系统。但 HTML5 的发展速度并不如预期,它虽然成功地实现了打破 IE+Flash 垄断局面的目标,却没有达到承载优秀的移动互联网体验的地步。

于是在 iPhone 站稳脚跟后,发布了自己的 App Store,开启了移动互联网的原生应用时代。随后的 Android,本来是基于 Linux 的 OS,与之同期的 MeeGo 等竞争对手采用 C + HTML5 的双模应用生态策略,然而 C 的开发难度太大,HTML5 体验又不行。Android 依靠 Java 技术生态,在竞争中脱颖而出。于是在移动互联网初期,应用生态被定了基调 —— 原生开发。

依赖于像 APICloud 这样的第三方工具,开发者是可以较好地屏蔽底层开发细节;而在 React Native 与 Flutter 开发中,我们仍需要去改造或实现许多的原生代码。

image

软件是关于如何操作大量晶体管和电路 (两者统称为硬件) 的指令的集合。直接运行在硬件上的原始指令对我们人类来说是几乎无法理解的, 特别是考虑到当今计算机的复杂性和规模。

要使得软件可以理解和操作的话,计算机科学家将其划分为多个层,这些层均是由框架构成的,每个框架都运行在另一个框架之上。在所有框架中,越接近硬件的框架,我们就说它更“原生”。

通常,更原生的框架中的程序能够获取更多的硬件功能,以及使用硬件更加自由。由于在不同语言之间进行模拟和翻译的开销较低,通常它的运行效率更高。但现实是残酷的,它的代码通常更难编写和理解。

另一方面,对于原生化更少的框架来说,通常编写代码更为简单。编码语言也更容易理解和简洁(需要的代码少)。它的词汇更接近与我们人类的自然语言。它不需要我们十分了解硬件的构成以及它在幕后的工作方式。还有一个额外的好处,原生化较少的框架中的程序通常更具可移植性,程序可以在完全不同的硬件平台上运行而无需修改,因为它的词汇和底层概念不包含任何特定于原始硬件的内容。但是,这一切便利的代码就是通常会牺牲一些效率和自由度。

首先是原生阵营,例如安卓的 Java/Kotlin 和 IOS 的 Objective-C/Swift 。此阵营中的应用速度都很快,并且可以使用丰富的硬件功能。用户界面是针对目标平台(安卓或 IOS)的定制的,因此使用起来是流畅且愉悦的。但是,所有这些好处都被限制在一个平台上了。要开发应用的话,需要学习不同的框架。

Cordova/PhoneGap 和 Ionic 为代表的。这些框架可以让 Web 开发人员使用他们已经具备的 HTML、CSS 和 JavaScript 技能来开发应用。这些应用可以同时运行在安卓和 IOS 平台上(还可以有更多平台)。但是,相比于原生应用,这类应用会没有那么流畅,能访问的硬件功能也有限。最重要的是,这些应用的用户界面太烂了!因为这些框架使用的 WebView 来渲染 UI,所以我们将其称之为 WebView 框架。

React Native 直接使用了原生 UI 组件,而 WebView 框架是使用 HTML/CSS 的 Web UI 来模拟原生 UI 。 软件是关于如何操作大量晶体管和电路 (两者统称为硬件) 的指令的集合。直接运行在硬件上的原始指令对我们人类来说是几乎无法理解的, 特别是考虑到当今计算机的复杂性和规模。

要使得软件可以理解和操作的话,计算机科学家将其划分为多个层,这些层均是由框架构成的,每个框架都运行在另一个框架之上。在所有框架中,越接近硬件的框架,我们就说它更“原生”。

通常,更原生的框架中的程序能够获取更多的硬件功能,以及使用硬件更加自由。由于在不同语言之间进行模拟和翻译的开销较低,通常它的运行效率更高。但现实是残酷的,它的代码通常更难编写和理解。

另一方面,对于原生化更少的框架来说,通常编写代码更为简单。编码语言也更容易理解和简洁(需要的代码少)。它的词汇更接近与我们人类的自然语言。它不需要我们十分了解硬件的构成以及它在幕后的工作方式。还有一个额外的好处,原生化较少的框架中的程序通常更具可移植性,程序可以在完全不同的硬件平台上运行而无需修改,因为它的词汇和底层概念不包含任何特定于原始硬件的内容。但是,这一切便利的代码就是通常会牺牲一些效率和自由度。

首先是原生阵营,例如安卓的 Java/Kotlin 和 IOS 的 Objective-C/Swift 。此阵营中的应用速度都很快,并且可以使用丰富的硬件功能。用户界面是针对目标平台(安卓或 IOS)的定制的,因此使用起来是流畅且愉悦的。但是,所有这些好处都被限制在一个平台上了。要开发应用的话,需要学习不同的框架。

Cordova/PhoneGap 和 Ionic 为代表的。这些框架可以让 Web 开发人员使用他们已经具备的 HTML、CSS 和 JavaScript 技能来开发应用。这些应用可以同时运行在安卓和 IOS 平台上(还可以有更多平台)。但是,相比于原生应用,这类应用会没有那么流畅,能访问的硬件功能也有限。最重要的是,这些应用的用户界面太烂了!因为这些框架使用的 WebView 来渲染 UI,所以我们将其称之为 WebView 框架。

原生桥接

React Native 直接使用了原生 UI 组件,而 WebView 框架是使用 HTML/CSS 的 Web UI 来模拟原生 UI 。

小程序

小程序是一种新的移动应用程序格式,是一种依赖 Web 技术(尤其是 CSS 和 Javascript),但也集成了原生应用程序功能的混合解决方案。小程序的出现有着明显的商业诉求,因为马太效应,一些超大流量的 App 诞生了。这些大流量 App 集成了许多功能,但显然公司再多员工,也无法所有功能全是自己弄,于是产生小程序这种“外包”的手段。

它的一些特性有助于填补 Web 和原生平台之间的鸿沟:它不需要安装;具备多个 Web 视图以提高性能;它提供了一些通过原生路径访问操作系统功能或数据的机制;它的内容通常更值得信赖,因为应用程序需要由平台验证;小程序可以分发到多个小程序平台(Web、原生应用,甚至是 OS)。这些平台还为小程序提供了入口,帮助用户轻松找到所需的应用。

小程序是国内前端技术的一次厚积薄发:底层运行的迷你 React 的虚拟 DOM,内置组件是使用 Web Component,API 来源于 Hybird 的桥方法,打包使用 webpack,调试台是 Chrome console 的简化版,WXML、WXSS 的语法高亮也应该是 webpack 或 VS Code 的插件,模块机制是 Node.js 的 CommonJS……其中最值得一提的是微信开发者工具,以后开发者工具成了各种小程序/快应用的标配。

但微信小程序一开始的复用能力非常弱,没有类继承,不能使用 npm, 不支持 Less、Sass,因此基于它的转译框架就应运而生。第一代转译框架是 wept、WePY、mpvue,它们无一例外是 Vue 风格的。因为 WXML 的模板指令与 Vue 非常相似,只是改一下就能兼容。当时也出现了一个 MINA 的框架,听说是微信团队开发的,可以单独架起 Node.js 后端,让小程序运于浏览器中,方便做单元测试。

第一代转译框架主要是基于 Template 标签实现组件机制,自定义组件机制是以后的事了。这就造成了利用第一代转译框架编写的小程序项目很难升级。那时候是个人开发者的天堂,这些框架都是某一大牛独力开发的。

第二代转译框架是大公司主导的,因为需要兼容的小程序越来越多,百度、支付宝、字节跳动、小米、华为等公司都推出自己的小程序和快应用。个人开发者很难凭个人力量去开发转译框架,这时候各大团队纷纷推出自己的轮子:如京东的 Taro、滴滴的 Chameleon 网易的 Megalo、去哪儿网的 nanachi、百度的 Okam 等。

在这个时期,Angular 显然落伍了,一是 Angular 升级太快,国内的高手还没有消化好,新一版的 Angular 又发布了。二是国内缺乏迷你 Angular 的轮子,导致庞大的 Angular 无法塞进小程序中。

国外谷歌发布了 Flutter 跨平台转译框架,但是它的编写语言是 Dart,它也无法跨界到小程序中。

未来不仅国内一线巨头争夺小程序,二三线的巨头也可能会加入小程序的混战中,例如有人称 360 也在打造自己的小程序平台。小程序这种新的流量变现模式深刻地影响了国内的互联网布局。

终端开发离不开三大要素——界面表现(结构、外观)层、逻辑处理层与系统接口层(网络、存储与媒体等)。

开发者编写代码时在初始化阶段(生命周期)调用“界面表现层”界面模型的接口绘制界面,当用户触摸界面时,“界面表现层”将事件发送给用户“逻辑处理层”,后者经过条件判断再处理并反馈到用户界面,处理过程可能需要调用“系统接口层”,反馈过程需要调用“界面表现层”的接口。常规的终端开发架构模式下,无论是 Web 端、Android 端还是 iOS 端的项目开发,都强依赖各端的环境接口,特别是依赖界面相关模型设计。iOS 系统下绘制界面基于 Objective-C 语言环境下的 UIKit 框架;Android 系统下用户绘制界面基于 Java 语言环境,由 LayoutInflater 处理 XML 结构层次树;Web 端使用 DOM 模型和 CSS 来描述绘制界面。

MVVM 中的关键是它通过 ViewModel 这一层将界面和逻辑层彻底隔离开来,负责关联界面表现和逻辑处理层的响应事件(update/notify)关系,这一“隔离层”上下通信足够规范、足够纯净单一。Model 进行逻辑处理是纯业务响应逻辑,任何一种语言都可以实现,你可以用 Android 的 Java,也可以用 iOS 的 Objective-C。

React Native、Weex 与快应用的 MVVM,开发者编写的代码在虚拟机(V8、JavaScriptCore)里面运行,虚拟机容器里面包含扩展的系统基础接口。运行时,将描述界面的数据(主要是 CSS+DSL 所描述内容)通过通信层传递给 Android、iOS 端的渲染引擎,用户触摸界面时,通过通信层传递给虚拟机里面的业务处理代码,业务处理代码可能调用网络、储存与媒体等接口,最后再次反馈到界面。

Flutter 和 RN 的最大区别在于将“JavascriptCore/V8+JS”替换成“C++ 实现的 engine+Dart 实现的 Framework+静态类型 Dart+编译成机器码”。

小程序本质上和 Weex、React Native 的设计思路基本一样,最大区别在于前者还是用浏览器 WebView 做渲染引擎,而后者是单独实现了渲染引擎(所以大量的 CSS 布局模型不支持)。

Links