再谈移动端动态化方案
近几年,移动互联网发展迅猛,几乎每天一变,各种新技术、新思想犹如雨后春笋般,层出不穷。虽然移动端动态化方案已经发展了很多年,但是每天都有新的变化,所以笔者在这里重新和大家聊一下移动端动态化方案。
前言
在移动开发领域,动态化方案一直争议不断,而最近 Airbnb 和 Udacity 弃用 React Native 也把这个问题推向了舆论中心。不论是独立的移动开发者,还是大的互联网软件公司,都对这类方案褒贬不一;褒的是快速迭代和跨平台,而贬的是性能效率和抹平平台差异过程中的各种坑。动态化方案究竟是一个能让开发者托付终身的大道,还是路见不平一声吼的临时方案。笔者作为一个深耕移动开发数年的老兵,将从技术、架构、成本等各个角度,对移动端动态化方案进行深入阐述。
移动端动态化的由来
“动态化”并不是最近几年才产生的名词,而是从c互联网诞生的初期,这个词就已经出现了。大家所认知的早期互联网,其实就是各种各类的“动态网站”,内容数据和页面外观都不是固定的,都是随着服务器端的更新而更新的,让用户可以很及时地看到最新的内容。因此,动态化可以说是互联网的标志,是互联网最核心的特性之一。
而移动互联网的普及,移动端被各类原生应用所占据,而这些应用更近似于 Software,依托于应用市场进行更新,只有其中的数据是实时的。这样,每次产品的更新,必须依赖用户的主动更新,从而造成了一定的用户成本,不利于产品的快速迭代,降低应用的试错能力。因此,移动端动态化方案逐渐走进大家的视野,并被大家所关注。
从一开始基于 WebView 的 Hybrid 方案 PhoneGap、Titanium,到现在与原生相结合的 React Native 、Weex,甚至 Flutter,都被或多或少地使用到不同的移动应用中。以“去哪儿旅行” App 为例,机票业务 90% 以上的项目都已经使用 React Native 构建,酒店主流程业务也都使用 React Native 构建,另外还有过百项目使用 Hybrid 方案。据不完全统计,平均每天至少有 25 个使用动态化方案的项目进行发布,相对于用户主动更新的频率,这样的业务迭代速度,才是互联网公司所需要的。
原生开发能不能动态化?准确的说是能的,而且 Android 平台各公司都有很完善的动态化方案,甚至 Google 今年还提供了 Android App Bundles 让开发者们可以更好地支持动态化。而反观 iOS,由于 Apple 官方担忧动态化的风险,因此并不太支持动态化(去年还封杀了 JSPatch 等一类动态修复方案),因此比较通用的原生动态化方案几乎没有,只有各大厂自己实现的一些动态化框架。
Android App Bundles 图示 :
Apple 这样的做法,其实也有情可原。试想,你从应用市场下载了一个“抖音”,然后过几天打开,发现 App 内的内容变成了“拼多多”,还变不回去,是有多恶心。Apple 如此严格要求 App,也是 Apple Store 比各类 Android 市场上的 App 质量普遍要高的原因之一。
从 Web 开始
既然动态化起源于 Web,那么移动端为什么不直接使用 Web 进行构建呢?而且在当前设备性能有大幅提升、浏览器内核得到有效优化的情况下。
首先,历史原因。移动互联网兴起之初,移动设备上的浏览器内核版本和性能都比较低,移动网络速度较慢,再加上国内 Android 厂商为了所谓的“优化”,各种阉割、改造浏览器内核,造成纯的 Web 页面,不管从交互流畅度、功能完善度,还是从兼容各个平台所需的人力成本来讲,Web 都不是最好的选择。因此,绝大多数互联网产品都以 App 的形式存在,而用户通过几年的适应,已经习惯了 App 的“先下载再使用”的方式,对于纯 Web 的“即开即用”的形式并不是十分感冒。
其次,虽然 Google 为了推动 Web 的发展,提出了两个非常卓越的思路 —— AMP 和 PWA,但是由于其他厂商的跟进较慢(例如 Apple),以及浏览器(包括内核)的版本迭代存在一定的长尾效应,短时间内形成良好的 Web App 的使用环境是比较困难的。
最后是涉及安全性较高的功能,例如支付。通过 Web API 直接调用移动设备的支付功能,或者调用指纹或人脸验证,都是行不通的。虽然较为安全的 Https 协议已经被广泛使用,调用验证和支付的 API 已经写入或即将写入 Web 标准,但是不管是手机厂商还是系统厂商,都没有行动起来,去为 Web 提供相应的功能 API。不论是安全上真的有缺陷,还是厂商认为 Web 不可控,但对于一个商业闭环,支付是不可缺少的一部分,这也制约了 Web App 的发展。
到混合之路
既然 Web App 行不通,那就尝试和 Native 进行结合。由于设备的繁多和定制化的差异,基于设备操作系统原生开发的成本逐渐升高,最明显的示例就是同样的功能逻辑在不同的设备上要用不同的编程语言、不同的代码结构去构建,学习成本、开发成本和维护成本大幅增加,因此引入一种开发更高效、成本更低的解决方案势在必行,而这个方案就是 Hybrid 混合开发,它让 Web 的跨平台特性被利用得淋淋尽致。
Hybrid 这个名词越来越多地出现在人们的视野中是源于混合动力汽车(Hybrid Electrical Vehicle,简称 HEV ),由汽油和电池一起提供动力,结合了油车续航长、补充快和电车清洁、低耗能的两方面优势,达到了发动机和电动机的最佳匹配。在互联网技术中的,Hybrid 方案也具有同样的特性——结合两种混合技术的优势,例如 Web 技术的跨平台、快速部署与原生 Native 的功能、性能相结合。
大多时候,圈内人谈论到 Hybrid 时,一般是指移动端内嵌 WebView 的开发方案。实则不然,广泛地讲,包括 React-Native 方案、各厂商的小程序方案都属于移动端 Hybrid 解决方案的范畴;而 NM.js、Electron 等则属于 PC 端 Hybrid 解决方案。对于智能设备,大多基于 Android 系统,因此,智能设备上使用的 Hybrid 方案与移动端基本一致。
具体来看,移动端混合开发中的 Hybrid 方案,主要有三种形式:
- 基于 Web 的解决方案,例如:Cordova、微信浏览器、各公司的 Hybrid 方案。
- 非基于 Web UI 但业务逻辑基于 JavaScript 的解决方案,例如:React-Native, Weex,Flutter。
- 基于 Web 且 UI 层与逻辑层分离的解决方案,此类型的代表是微信小程序,将UI展现逻辑和业务逻辑分离到多个 JavaScript Context 里,提高运行效率,效果很好。
其中,第一种方案,以 Web 为基础,实现成本最低,只是 Native 提供 WebView 运行 Web 页面,并提供相应的 API,平台兼容性问题,大部分交给 Web 来处理(Native 基本上只需要处理 API 层的兼容);而且业务的开发方式保持 Web 的形式,对于前端是友好的。缺点也显而易见,Web 的性能受到浏览器内核的影响。
第二种方案中最具代表的是 Facebook 的 React-Native,也在前言中提到过,推崇者无数的同时,粉转黑的也大有人在。它几乎将平台兼容性的问题全部交给了 Native,业务的 JavaScript 中是纯粹的业务逻辑。其优势,主要在性能,通过原生组件与 React 组件化设计相配合的方式提供高性能的 UI,但 JavaScript Context 和 Native 通信成了这套方案的瓶颈。
通信过慢?不。单次通信并不慢,在某些频繁通信场景中,通信阻塞的问题会显现。例如数据量较多的列表,业务需要对现有的列表进行扩展,这时需要 JavaScript 响应各类滚动事件,这样通信量会大幅增加,造成通信阻塞。这里,有人会说,为啥 Native 组件不定制好?“魔高一尺道高一丈”,预先的设计肯定不能满足产品的所有需求。当然,Native 可以满足新的需求,这就需要依赖 App 的升级,需求不能在老版本 App 中实现。另外,React 的虚拟 Dom 机制,不能准确提供给 Native 元素复用的信息,也是影响性能的一个方面。
第三种,基于 Web 且 UI 层与逻辑层分离的解决方案。微信小程序的性能是有目共睹的,但是微信小程序中由于各方面限制,复杂应用并不多,当一个应用足够庞大,性能又会如何呢(当然这种应用可能不会出现)。基于这种思路,我们也实现了一个类似架构的方案 YIS,基于 JavaScript Context 和 Web UI 的方案,业务逻辑都跑在 JavaScript Context 中,UI 在单独的 WebView 上绘制。经过验证,性能相当不错。相比于上一种,由于 UI 是 Web 实现的,故这些 UI 组件是可以动态更新的,可以更好地支持业务需求。不过,性能瓶颈仍是问题,毕竟 Web UI 的性能天花板要比 Native 低。有人说,随着移动设备性能的提升,浏览器内核的完善,Web UI 的性能问题会被解决。但是,笔者觉得,Web UI 性能的提升同时,Native UI 的性能会变得更好,可能会出现更“炫酷”的交互,例如科幻电影里,从手机中投射出 3D 视图,到那时,可能又会出现其他的性能问题。
所以,使用 Web UI 还是 Native UI 的争论,暂时停不下来。其实,没有最好的方案,根据自身的业务场景、公司情况,选择最适应的方案才是正道。
终以 JSCore 为核
不论是使用 Web UI 还是使用 Native UI,由于 JavaScript 是单线程,业务代码会与 UI 渲染同时占用线程资源,因此业务逻辑与 UI 分离,在现在看来是必须要做的,这样会让业务尽量动态化的同时,性能得到很大提升。
运行业务逻辑的 JavaScript Context ,通常被统称成为 JSCore。通俗来讲,JSCore 就是一个即没有 DOM 也没有 BOM 的浏览器内核,和 WebWorker 近似,只能运行纯的 JavaScript 逻辑,并通过 Native提供的 API,进行网络请求、本地存储等操作。
当然,在实际的设计中,可以用一些例如“空间换时间”的方法,来优化 JSCore 的性能,例如预初始化 JSCore、预加载框架的 JavaScript 代码等,占用一些固有的内存,来保证业务的启动速度。
JSCore 的模式,其实更像是 MVC、MVVM 盛行带来的红利。MVC、MVVM 的核心思想都是让开发者更关注业务本身,更关心业务逻辑,而剥离出来的 View 层,就可以通过组件化的形式,映射到其他的 Context 中,从而只保留非 UI 渲染逻辑在 JSCore 中,提高性能。
Web 不死,PWA 向前
Web 已死?No!同时,App 也不是未来。
在移动端上,Web 还用很多瓶颈有待解决,但作为最灵活、跨平台性最好的技术,它缺是不可能被磨灭的。各种就 App 的动态化技术,都支持了兼容 Web 的方式(例如 rn-web),但是,其带来的开发和适配成本也是不小的。至少,一个短时的活动项目,用 Web 构建,可以在各个平台的浏览器中,各个 App 的 WebView 中运行,其成本远小于其他方案。再者,Web 对 SEO 的友好,也是 App 无法比拟的,这也是 Web 的一大优势。
来自 App 的压力,推动着 Web 技术的发展,其中,Google 推出的 PWA 是最有颠覆性的。随着 Apple 新 Safari 和微软 Win10 对 PWA 的着力支持,PWA 的前景越来越明朗。在桌面有入口、支持控制资源缓存、支持消息推送、具备离线能力,当 PWA 应用可以完成 App 的一切,Web 或者又要重新站上顶峰。(真心推荐微博的 PWA 版本,比客户端好用多了)
前路慢慢
动态化方案,虽然由于种种原因成为了现在这个阶段中比较流行的方案,但是,动态化方案仍有很多没有解决的弊端,待我们去解决。而且,在我们解决的过程中,也会不断涌现其他各种方案。
总之,动态化方案的出现,不是为了替代谁,更多是为了给用户更好的体验,同时让业务可以更快的迭代,并在不断的尝试中,给用户带来更好的产品。
“路漫漫其修远兮,吾将上下而求索”。