You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
21 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 36 | 跨平台开发的现状与应用
在2016年我参加了两场移动技术大会在当时的GMTC大会上原生开发看起来如日中天。
转眼到了2017年HTML5性能越来越好Facebook的React Native、阿里的Weex等跨平台方案在越来越多的公司中实践微信小程序更是给了原生开发最沉重的一击许多小公司可能不再需要开发自己的应用。
“Write once, run anywhere”人们对跨平台开发的尝试从来没有停止过。特别在这个终端碎片化的时代一份代码可以在同一个平台不同的系统版本甚至在不同的平台上运行对开发者的吸引力越来越大。
回想一下HTML5与Native开发的斗争已经持续快十年了那它们的现状是怎样的呢React Native和Weex方案有哪些优势又存在什么问题小程序是不是真的可以一统江湖焦虑的Native开发又应该如何在这个潮流之下谋发展呢
## 跨平台开发的现状
从2017年开始GMTC“移动技术大会”就更名为“大前端技术大会”。从现在看来前端开发和Native开发并没有谁取代谁而是正在融合融合之后的产物就是所谓的“大前端”。为了顺应这种趋势很多大公司的组织架构也做了相应的调整把前端团队和iOS、Android一起合并为大前端团队。
移动Web技术、React Native和Weex、小程序它们是目前最常用到的跨平台开发方案下面我们一起来看看它们的应用现状。**当然对于今年最为火热的Flutter技术专栏后面我会花专门的篇幅去介绍。**
**1\. Web**
从桌面时代开始以浏览器为载体的Web技术就具备跨平台、动态更新、扩展性强等优点。随着移动设备性能的增强Web页面的性能也逐渐变得可以接受。客户端中出现越来越多的内嵌Web页面很多应用也会把一些功能模块改为Web实现。
**浏览器内核**
一个Web页面是由HTML + CSS + JavaScript组成通过浏览器内核执行并渲染成开发者预期的界面。
![](https://static001.geekbang.org/resource/image/ae/6a/ae3ca2e7151208a5ce7bb45d7e3d976a.png)
浏览器内核主要包括两大块功能,它们分别是:
* **浏览器引擎**。浏览器引擎负责处理HTML和CSS遵照的是W3C标准。
* **JavaScript引擎**。JS引擎负责处理JS遵照的是ECMAScript标准。
它们两者互相独立但又有非常紧密的结合而且在不同浏览器内核中的实现也是不太一样的。但随着微软的Edge宣布将内核切换成Chromium目前这个战场主要就剩下苹果和Google两个玩家它们的浏览器引擎分别是Webkit和Blink其实Blink也是fork自WebkitJS引擎分别是[JavaScriptCore](https://trac.webkit.org/wiki/JavaScriptCore)和[V8](https://v8.dev/)。
对于浏览器的渲染流程可能很多Android开发并没有前端同学熟悉。一般来说HTML、CSS、JS以及页面用到的一些其他资源图片、视频、字体等都需要从网络下载。而HTML会被解析成DOMCSS会被解析成CSSOMJS会由JS引擎执行最后整合DOM和CSSOM之后合成为一棵Render Tree。
![](https://static001.geekbang.org/resource/image/42/98/4224c50dce3deebc1f4560f3cf65ae98.png)
当然整个浏览器的渲染流程不是三言两语就可以说清楚的,下面是我找的一些不错的参考资料,感兴趣的同学可以深入学习:
* 浏览器渲染:[一颗像素的诞生](https://mp.weixin.qq.com/s/QoFrdmxdRJG5ETQp5Ua3-A)强烈推荐看PPT [Life of a Pixel](https://docs.google.com/presentation/d/1boPxbgNrTU0ddsc144rcXayGA_WF53k96imRH8Mp34Y/edit#slide=id.g25ae9c179b_0_75)
* 浏览器引擎:[What is a browser engine?](https://hacks.mozilla.org/2017/05/quantum-up-close-what-is-a-browser-engine/)
* 浏览器系列教程:[How Browsers Work: Behind the scenes of modern web browsers](https://www.html5rocks.com/en/tutorials/internals/howbrowserswork/)
* Google Web开发者官网[Rendering on the Web](https://developers.google.cn/web/updates/2019/02/rendering-on-the-web)
**虽然[Chromium](https://www.chromium.org/Home)是开源的但是因为它的复杂性国内对它有深入研究的人非常少而拥有定制修改能力的人更是少之又少。因此这块需要投入大量的人力物力国内比较有名的是UC浏览器的U4内核以及腾讯浏览器的X5内核。**
**性能现状**
基于WebView的H5跨平台方案优点确实非常明显。但是性能是它目前最大的问题主要表现在以下两个方面
* **启动白屏时间**。WebView是一个非常重量级的控件无论是WebView的初始化还是整个渲染流程都非常耗时。这导致界面启动的时候会出现一段白屏时间体验非常糟糕。
* **响应流畅度**。由于单线程、历史包袱等原因页面的渲染和JavaScript的执行效率都不如原生。在一些重交互或者动画复杂的场景H5的性能还无法满足诉求。
所以在移动端H5主要应用在一些交互不太复杂的场景一般来说即使帧率不如原生但也基本符合要求。从我个人的感受来看H5当前最大的问题在于启动的白屏时间。
对于Android界面启动的过程我们在窗口动画还没结束的时候大部分时候就已经完成了页面的渲染。启动一个Activity界面我们一般要求在300毫秒以内。
![](https://static001.geekbang.org/resource/image/9f/14/9fd70d4a528d903355beba20c1c65e14.png)
回顾一下浏览器内核渲染的流程,我们其实可以把整个过程拆成三个部分:
* **Native时间**。主要是Activity、WebView创建以及WebView初始化的时间。虽然首次创建WebView的时间会长一些但总体Native时间是可控的。
* **网络时间**。这里包括DNS、TCP、SSL的建连时间和下载主文档的时间。当解析主文档的时候也需要同步去下载主文档依赖的CSS和JS资源以及必要的数据。
* **渲染时间**。浏览器内核构建Render Tree、Layout并渲染到屏幕的时间。
![](https://static001.geekbang.org/resource/image/b9/a1/b9685887787e43ed6de77dd6bb302ca1.png)
如上图所示我们会更加关心用户看到完整的页面的时间T2这里可以用**T2秒开率**作为启动速度的衡量标准。
**优化方法**
Native界面的T2秒开率做到90%以上并不困难相比之下大部分没有经过优化的Web页面的T2秒开率可能都在40%以下,差距还是非常明显的。
那又应该如何去优化呢?从前端的角度来看,常用的优化方法有:
* **加快请求速度**。整个启动过程中网络时间是最不可控的。这里的优化方法有很多例如预解析DNS、减少域名数、减少HTTP请求数、CDN分发、请求复用、懒加载、Gzip压缩、图片格式压缩。
* **代码优化**。主文档的大小越小越好([要求小于15KB](https://aosabook.org/en/posa/high-performance-networking-in-chrome.html)这里要求我们对HTML、CSS以及JS进行代码优化。以JS为例前端的库和框架真的太多了可能一不小心就引入了各种的依赖框架。对于核心页面我们要求只能使用原生JS或者非常轻量级的JS框架例如使用只有几KB的Preact代替庞大的React框架。
* **SSR**。对于浏览器的渲染流程我上面描述的是CSR渲染模式在这种模式下服务器只返回页面的基本框架。事实上还有一种非常流行的[SSRServer Side Rendering](https://developers.google.cn/web/updates/2019/02/rendering-on-the-web)渲染模式服务器可以一次性生成直接进行渲染的HTML。这样在T2之前我们可以做到只有一个网络请求但是带来的代价就是服务器计算资源的增加。一般来说我们会在服务器前置CDN来解决访问量的问题。
![](https://static001.geekbang.org/resource/image/7d/c0/7de6d6cf32dc307e3b9152bec1b6f7c0.png)
通过上面的这些优化特别是SSR这个“终极大招”页面的T2秒开率达到70%并不是非常困难的事情。
前端同学能做的都已经做了,接下来我们还可以做些什么呢?这个时候就需要客户端开发登场了。
* **WebView预创建**。提前创建和初始化WebView以及实现WebView的复用这块大约可以节省100200毫秒。
* **缓存**。H5是有多级的缓存机制例如Memory Cache存放在内存中一般资源响应回来就会放进去页面关闭就会释放。Client Cache也就是客户端缓存例如我们最常用的离线包方案提前将需要网络请求的数据下发到客户端通过拦截浏览器的资源请求实现加载。[Http Cache](https://developers.google.cn/web/fundamentals/performance/optimizing-content-efficiency/http-caching)是我们比较熟悉的缓存机制而Net Cache就是指DNS解析结果的缓存或预连接的缓存等。
![](https://static001.geekbang.org/resource/image/76/82/761d08be2655efb28f602083a35c7e82.png)
从性能上看Memory Cache > Client Cache >= Http Cache > Net Cache。所谓的缓存就是在用户真正点击打开页面之前提前把数据、资源下载到本地内存或者磁盘中并放到内核相应的缓存中。例如即使我们使用了SSR也可以在用户点击之前提前把服务器渲染好的HTML下载好这样用户真正打开页面的时候可以做到完全没有网络请求。
通过预请求的优化即使比较复杂的页面T2秒开率也可以达到80%以上。但是既然是预请求就会有命中率的问题,服务器也增加了没有真正命中的请求数。所以在客户端性能和服务器压力之间,我们需要找到一个平衡点。
那还有没有进一步优化的空间这个时候需要我们进一步往底层走需要我们有定制修改甚至优化内核的能力。例如很多接口官方的浏览器内核可能并没有暴露而腾讯和UC的内核里面都会有很多的特殊接口。
* **托管所有网络请求**。我们不仅可以托管浏览器的Get请求其他的所有Post请求也能接管这样我们可以做非常多的定制化优化。
* **私有接口**。我们可以暴露很多浏览器的一些非公开接口。以预渲染为例,我可以指定在内存直接渲染某个页面,当用户真正打开的时候,只需要直接做刷新就可以了,实现真正的“秒开”。
* **兼容性和安全**。Android的碎片化导致浏览器内核的兼容性实在令人头疼而且旧版本内核还存在不少的安全漏洞。在应用自带浏览器内核可以解决这些问题而且高版本的内核特性也会更加完善例如支持TLS 1.3、QUIC等。但是带来的代价是安装包增大20MB左右当然我们也可以采用动态下载的方式。
定制的自有页面 + 定制的浏览器内核 + 极致的优化即使是比较复杂的页面T2秒开率也可以达到90%以上平均T2时间可以做到400毫秒以下。
**2\. React Native和Weex**
基于WebView的H5跨平台方案经过近乎疯狂的性能优化看起来性能真的不错了。但是对于一些交互和动画复杂的场景例如左右滑屏、手势性能还是无法满足要求。
Facebook在2015年开源了[React Native](https://github.com/facebook/react-native)它抛弃了WebView利用JavaScriptCore来做桥接将JavaScript调用转为Native调用。也就是说React Native最终会生成对应的自定义原生控件走的是系统原生的渲染流程。
而阿里在2016年也开源了[Weex](https://github.com/apache/incubator-weex)它的思路跟React Native很像但是上层DSL使用的是Vue。对于Weex和React Native的架构介绍网上的文章非常多例如[《大前端的下一站何去何从?》](https://www.infoq.cn/article/9*CZfjFghPVqZJlc7ScM?utm_medium=hao.caibaojian.com&utm_source=hao.caibaojian.com)和[《Weex技术演进》](https://yq.aliyun.com/articles/444881?spm=a2c4e.11163080.searchblog.9.2eba2ec1hzUpLo)。
![](https://static001.geekbang.org/resource/image/49/54/498f043e2c7459e6e41f34ddfddf7b54.png)
但是世上哪有十全十美的方案React Native/Weex方案为了能达到接近原生开发的性能和交互体验必然要在跨平台和动态性上面做出了牺牲。
React Native和Weex向上对接了前端生态向下对接了原生渲染看起来是非常完美的方案。但是前端和客户端客户端中的Android和iOS它们的差异并不那么容易抹平强行融合就会遇到各种各样的坑。
“React Native从入门到放弃”是很多开发者的心声去年Airbnb、Udacity都相继[宣布放弃使用React Native](https://infoq.cn/article/2018/07/Udacity-Abandon-React-Native)。React Native/Weex并没有彻底解决跨平台的问题而且考虑到对外分享和降级容灾的需要我们依然需要开发一个H5版本的页面。
为了解决这个问题React Native的使用者需要引入一层非常重的中间层期望在这个中间层中帮助我们去抹平这些差异。例如京东的[JDReact](https://infoq.cn/article/jd-618-ReactNative-jingdong-practise)、携程的[Ctrip React Native](https://s.qunarzz.com/ymfe_conf/ppt/2017autumn_ctrip_liaoliqiang.pdf)。
![](https://static001.geekbang.org/resource/image/b2/11/b2f6f19bb6ae9a71546645dc52984411.png)
既然React Native和Weex在跨平台上面做了牺牲那它的性能和交互是不是能直接对齐Native开发呢非常遗憾 目前它们的性能我觉得主要还有两个瓶颈。
* **JS的执行时间**。React Native和Weex使用的[JavaScriptCore](https://trac.webkit.org/wiki/JavaScriptCore)引擎虽然它每年都在进步但是JS是解释性的动态语言它的执行效率相比AOT编译后的Java性能依然会在几倍以上的差距。
* **跨语言的通信成本**。既然要对接前端和原生两个生态就无法避免JS -> C++ -> Java/Objective-C 之间频繁的通信和转换,所以这里面会涉及各种序列化,对性能的影响比较大。
虽然相比H5方案在性能方面有了很大的提升但是React Native和Weex也要面对启动时间慢、帧率不如原生的性能问题。它属于一种比较中庸的方案当然也会有自己的应用场景。例如一些二级页面例如淘宝的分会场它们的业务也比较重要但是交互不会特别复杂同时希望保持一定的动态化能力。
当然Facebook已经意识到React Native的种种性能问题目前正在疯狂重构中希望让React Native更加轻量化、更适应混合开发接近甚至达到原生的体验。Facebook现在透漏的信息并不多感兴趣的同学可以参考[《庖丁解牛深入剖析React Native下一代架构重构》](https://mp.weixin.qq.com/s/dXZTqXOSi3fiOesDJ7gsFQ)。
**3\. 小程序**
2017年初张小龙宣布微信小程序诞生。如今小程序已经走过了两年在这两年间小程序的生态也在健康的发展。
每一个应用都有成为超级App的梦想各个大厂纷纷推出自己的小程序框架微信、厂商、支付宝、今日头条、百度、淘宝、[Google Play](https://www.infoq.cn/article/XTE9WzSL11iHmW*WBozi),小程序这个战场已然是“七国大乱战”。
![](https://static001.geekbang.org/resource/image/1c/76/1cd013191bd6937fec9b63a47bfb5676.png)
但是小程序并不属于一种跨平台开发方案大家更看重的是它的渠道优势考虑如何通过微信、支付宝这些全民App获得更多的流量和用户。从技术上看小程序的框架技术也是开放的我们可以采用H5方案也可以采用React Native和Weex甚至是Flutter。
从实践上看,我们一起来看看已经正式上线的微信小程序、快应用、支付宝小程序以及百度小程序的差异(技术方面大家公开得并不多,可以参考[《支付宝小程序框架》](https://mp.weixin.qq.com/s/VD0K47KdD-E5EYlnNz2Cbw))。
![](https://static001.geekbang.org/resource/image/df/61/df12348b0ebadaf13219a66cccb97261.jpg)
我们可以看到除了独树一帜的快应用其他小程序的技术方案基本都跟随了微信。但是考虑到H5在一些场景的性能问题利用浏览器内核提供的同层渲染能力在WebView之上支持一些原生的控件。如果哪一天微信小程序支持了所有的原生控件那也就成为了另外一套React Native/Weex方案。
“神仙打架百姓遭殃”如果我们想从所有的小程序厂商上面获得流量那就要开发七个不同的小程序。不过幸运的是支付宝小程序和快应用也希望已有的微信小程序能快速迁移到自己平台上所以它们的DSL设计都参考了微信的语法可以说微信推出的DSL已然成为了事实标准。
![](https://static001.geekbang.org/resource/image/e8/21/e8e3f8ab6c7867a85ded1741c1b53a21.png)
如上图所示,我们希望有一套可以整合所有小程序框架的解决方案,一次开发就可以生成不同的小程序。滴滴的[Chameleon](https://mp.weixin.qq.com/s/YtBXUATwI1pnuF6W69KJYA)和京东的[Taro](https://mp.weixin.qq.com/s/H4W1SD-E5_KhuR1CNfrD2g)都致力于解决这个问题目前它们都已经在GitHub上开源。
## 跨平台开发的应用
从移动开发诞生之初跨平台就已经是大家前赴后继不断追求的目标。我们可以看看nwind在2015年写的一篇文章[《聊聊移动端跨平台开发的各种技术》](http://fex.baidu.com/blog/2015/05/cross-mobile/)。如今四年过去了大部分观点依然成立并且从最后Dart的介绍中我们甚至可以看到现在Flutter的雏形。
**1\. 跨平台开发的场景**
Android、iOS、PC不同的平台人们的操作习惯、喜好都不尽相同。对于大公司来说完全的跨平台开发可能是一个伪命题不同的平台应用的UI和交互都不太一样。
那我们对跨平台苦苦追寻了那么多年,希望得什么呢?以我的经验来看,跨平台主要的应用场景有:
* **部分业务**。某个业务或者页面的跨平台共享,有的时候我们还希望可以做到跨应用。例如“全民答题”的时候,可以看到这个功能可以运行在头条系的各个应用中。一个公司共用同一套跨平台方案有非常重大的意义,业务可以在不同的应用中尝试。
* **核心功能**。C++才是生命力最顽强的跨平台方案,大公司也将越来越多的核心模块往底层迁移,例如网络库、数据上报、加解密、音视频等。
**2\. 跨平台开发对比**
H5的跨平台方案只要投入不太高的开发成本就能开发出性能、功能还不错的应用。但是如果想做到极致优化很容易发现开发者可控的东西实在比较少性能和功能都依赖浏览器的支持。
这个时候如果想走得更远,我们不仅需要了解浏览器的内部机制,可能还需要具备定制、修改浏览器内核的能力,这也是阿里、腾讯、头条和百度都要组建内核团队的原因。
原生开发则相反刚开始要投入很高的开发成本但是一旦开始有产出之后开发者能够有更的发挥空间而React Native和Weex方案更是希望打造兼顾跨平台、开发成本以及性能的全方位解决方案。
![](https://static001.geekbang.org/resource/image/2e/f7/2e5f5b95bccd1cd0a029b3062339a6f7.png)
从目前来看每一种方案都有着自己的使用场景无论是React Natve还是H5都无法完全取代Native开发。当然这里也有一个例外那就是如果我们不再开发应用全面投向小程序。小程序跟原生开发的竞争更多的是在渠道层面的竞争。
## 总结
现在好像有个观点说“Android开发没人要”大家都想转去做大前端开发是不是真的是这样呢事实上无论我们使用哪一种跨平台方案它们最终都要运行在Android平台上。崩溃、内存、卡顿、耗电这些问题依然存在而且可能会更加复杂。而且从H5极致体验优化的例子来看很多优化是需要深入研究平台特性和系统底层机制我们在“高质量开发”中学到的底层和系统相关的知识依然很重要。
对开发者来说唯一不变的就是学习能力。掌握了学习能力和钻研的精神就能够应对这些趋势变化。无论移动开发未来如何变化哪怕有一天AI真的能够自动写代码具备应变能力的人也丝毫不会惧怕的。
## 课后作业
跨平台开发也是一个很大很大的话题,今天我只能算是抛砖引玉。对于跨平台开发,你有什么看法?在你的应用中,使用了哪种跨平台开发方式?欢迎留言跟我和其他同学一起讨论。
欢迎你点击“请朋友读”,把今天的内容分享给好友,邀请他一起学习。我也为认真思考、积极分享的同学准备了丰厚的“学习加油礼包”,期待与你一起切磋进步哦。