2020了,各家小程序发展的怎么样?

2017年,微信发布小程序,掀起小程序大战。

2018年,阿里、百度、字节跳动、手机厂商等各大巨头纷纷发布自己的小程序。

2019年,QQ也加入战局,各家全面开花。

时至2020年,各家小程序发展的到底怎么样?我们根据数据和访谈,形成了这份报告,希望能给开发者带来指引帮助。

作为开发者,我们关心的问题,说白了就2个:

各家有多大量?
各家流量质量怎么样?
想了解这些情况,需要一个相对权威的数据。DCloud有4百多万开发者,这些开发者把应用发布到微信、阿里、百度、头条、QQ等各种小程序,以及App、H5等平台,多应用去重后,目前有8.4亿月活设备。

这可能是当前体量最大的多平台数据了,基于这些数据,以及开发者调研,DCloud发布了本报告,作为开发者了解各小程序平台发展情况的参考。

一、各家小程序流量到底有多大

小程序还有流量红利吗?微信的红利是不是已经过去了?不少人存有这种疑问。

如果以2018年微信小程序的火爆对比,那么2019年的微信确实没有那么大的红利了。随着用户对群里的小程序由新鲜转向反感,以及微信的严格封杀策略,很多开发者开始抱怨在微信平台裂变不起来了。

当然微信小程序平台自身的流量仍在增长,只是增长主要来自于品牌主,比如自带流量的政府、商户,而不是独立开发商了。

压下葫芦起了瓢,其他小程序平台给开发者提供了更大的爆发机会。

我们来看看各家的流量天花板,也就是小程序宿主应用的月活,微信月活10亿、字节跳动(抖音+今日头条+西瓜+火山)月活10亿、QQ和支付宝月活都是6亿、百度App月活也有4亿。但各家的小程序月活并不是这个顺序,因为: 小程序的月活 = 宿主月使用时长 * 小程序使用频次

各家小程序的宿主和小程序流量数据平台宿主月活小程序月活微信10亿日活3亿,未公布月活支付宝6亿+5亿百度4亿+3亿字节跳动10亿+3亿QQ6亿+未公布

平台宿主月活里,百度最低,只有4亿。这让人一度担心百度小程序能不能起来。从最终结果来看,百度小程序的月活并不低。因为它的“小程序使用频次”高。用户使用百度App的主需求,就是为了查信息。而百度的百科、贴吧、知道等业务已经全部转为了小程序。

其他平台,都有自己的核心功能,小程序属于外延生态。宿主月活虽高,但其中使用小程序的人未必很多。当然支付宝有个例外,很多人打开支付宝,就是为了用蚂蚁森林小程序,而不是为了用支付宝支付,出现了神奇的反客为主效应。。。

在近期,有3个平台的小程序表现尤为抢眼,分别是字节跳动、微信、支付宝。

字节跳动的App群,从春节开始,流量暴增,然后一直维持在高位。这是一个令人佩服的成绩,在这段时间,字节跳动给小游戏分发了大量流量,宅家的人们一边消费着可观的短视频流量,一边玩小游戏消遣时间。

在春节后,各地防疫战打响,每个城市都开始上线抗疫相关的项目,健康登记、出入管理、口罩预约、实名乘车……引发了一大波新应用热潮。这些小程序仅承载在微信和支付宝的平台上。其他小程序平台没有抓到这拨红利。倒不是因为微信或支付宝的给量能力强,流量来自于政府和商户。而他们之所以选择微信和支付宝平台,完全是因为民众对于扫码的使用习惯依赖。人们看到一个码,不是用微信扫,就是用支付宝扫。其他小程序平台还没有给民众培养出扫码的使用习惯。

支付宝小程序,从1月起原本是大幅下滑的状态,线下商户陆续放假,严重依赖线下渠道的支付宝小程序受到很大影响。直到现在,随着线下商户复工才有了起色。所幸健康码挽救了支付宝小程序的日活。不过老实讲,健康码对于微信和阿里的商业价值都不大,也只能当做培养用户使用习惯和拉近政府关系了。支付宝小程序不擅长线上分发应用流量,线下商户连接价值才是支付宝小程序的核心,赋能商户、提升交易,是它的真正使命。

值得支付宝警惕的是,一些餐饮行业的开发者,使用uni-app多端框架开发应用后,明明可以发布为支付宝小程序,却仅仅发布了H5端,让顾客在线下使用支付宝扫码后打开H5页面。根据DCloud的访谈,这些开发者认为支付宝里扫码打开小程序,并不会比打开H5页强多少,而且小程序审核麻烦,导致很多支付宝小程序的流量跑到了支付宝内置浏览器里了。这需要支付宝改进策略,为小程序开发者提供更多的吸引力,同时减少开发者的麻烦。

百度小程序,恰恰是支付宝小程序的对立面,它是纯纯的线上流量。对于很多中小开发者,从百度获取流量,比任何其他平台都容易。做好seo、甚至付费买点流量,总会让你有用户的。不少中小开发者反馈,提交到多个平台小程序,发现百度的流量是大于其他家的。尤其是内容类小程序。在春节和疫情期间,百度小程序的流量有所下滑,近期随着复工复产,又开始快速增长。

各家巨头,除了利用自己的主App的体量,也在通过搞联盟来扩大流量池。百度和阿里,都计划通过联合多个头部App组成流量联盟,来帮助开发者获取更大流量。但从目前运行的情况看,这类计划的效果不太明显。阿里小程序和百度小程序的流量,基本都集中在支付宝和百度App上。不管是开发者还是用户,都还没有对流量联盟的其他小程序形成大范围认知和使用习惯。

阿里系小程序里,值得期待的新兵是淘宝小程序。近期淘宝小程序已经开始崭露头角,相信随着产品和运营的完善,淘宝小程序会开辟一片新蓝海,因为赋能卖家,确实是一个大市场。

QQ小程序发布较晚,它没有采取同为后来者的字节跳动小程序那般中心化疯狂配量模型,目前处于稳定增长中,但整体体量暂时和其他几家还不在一个量级。

再分享一个数据:各个平台的应用数量。除了月活流量外,我们还应该关注一个指标,是应用数量。uni-app的开发者发布到各个平台的“应用数量”,看下图:

虽然使用uni-app框架可以发布到所有小程序平台,但大多数开发者不会全平台提交应用。上面的饼图有2种解读:

微信小程序的应用数量远远超过其他平台,是绝对数量级的差距。说明它已经成为一种生活方式而不只是一个流量平台。
微信小程序非常拥挤,大量开发者聚集在微信平台,但得不到足够的流量
百度和字节跳动,应用数量少,但小程序月活体量却非常大,有更多红利机会。

如果你是一个自带流量的品牌主,比如政府单位,那微信小程序是很好的选择。如果你是一个独立开发者,现在在微信里获取流量其实已经很难了,不妨试试其他平台。与微信、支付宝的给量逻辑不同,百度和字节跳动是卖流量的。在微信里获客,一般是给用户利益,比如拼团砍价;在百度、字节跳动里获客,是给平台利益,花钱投放立即带来用户。从难度来讲,在微信里已经变得很难,而在百度和字节跳动则很简单。

百度里小程序的权重高于H5,投资到百度小程序上比投放H5页面更有性价比。至于字节跳动,不得不佩服它家的流量实在太大了,大到什么地步呢?这么说吧,只要肯投钱,快速获取上千万用户很easy。

当然,不花钱投放,一样可以得到流量,那对应的要求就是你的内容过硬。

以DCloud的开发者社区App为例,每天在百度小程序上得到的流量是最大的。很多用户在百度上搜索技术问题,被导流到DCloud社区的百度小程序中,拉新效果远优于其他小程序、App和H5。

在2020年,手机厂商的快应用也出现了更积极的变化。过去快应用的数量很少,不过几千款,与微信的百万级应用数差距很大。最近快应用在原生渲染引擎之外,推出了兼容小程序架构的新应用引擎。对快应用平台而言,新策略有助于增加快应用的开发者数量。

对开发者而言,这或许是拿到一个巨大流量的新机会。

uni-app在第一时间也跟踪了快应用的新引擎适配,欢迎开发者关注。

二、各家小程序平台的流量质量怎么样?

有量,还不够。开发者还关心流量质量。流量质量怎么看?我们来看这两个指标:1、单用户页面访问数量;2、次日留存

大家都知道,小程序的流量质量,比App要差。但具体差多少呢?我们把App的数据请出来,一起比一下。

不同平台用户访问应用的页面数量
可以看到,App的一个日活用户,平均访问49个页面,远超小程序。虽然小程序的拉新能力甩开App几条街,但深度用户,还是更喜欢用App。

在各家小程序里,微信小程序的表现最接近App,领先其他小程序不少。

从这个数据可以明显看出,用户对在支付宝、百度、字节跳动的App中重度使用小程序的习惯没有养成。基于这个现状,开发者若在百度、字节跳动平台推广小程序,需要注意缩短流程,在尽可能短的页面完成自己的业务。比如获取注册表单、获取商机线索、直购低价商品等。而另一方面,也需要微信以外的其他小程序运营平台,努力学习微信,改进运营策略。尤其是百度、字节跳动和QQ小程序,还比较重视应用内广告的收益,如果页面访问次数上不去,广告位的曝光量就上不去,导致美梦落空。

我们再来看第二个指标:次日留存。

各平台次日留存表
首先,所有小程序的留存,都比App差不少。然后看各家小程序。微信小程序,在众多小程序里,留存遥遥领先,平均次日留存超过了10%。其他小程序的平均次日留存,都在5%以下。

为什么会有这么大差距。从留存入口来看,微信的下拉二楼、发现选项卡的小程序栏目,已经成功的培养起了用户的使用习惯。用户第一次不管从哪里得到一个小程序,第二次还可以比较顺畅的找到它,从而形成留存。但其他平台在这方面做得不好。百度App的二楼,经常和信息流刷新冲突。支付宝的二次使用入口,打开很慢,远不及微信的体验。字节跳动、QQ的二次使用入口则非常深。

当然,这里还有另一个关键问题,对于非微信的其他小程序平台,它们还有一个尴尬,二次使用的入口,到底做的多方便为好?字节跳动和百度,都是卖流量为生的公司。开发商投放了搜索关键字或信息流广告,如果后续用户可以方便的在二次使用入口获取这个应用,那对于字节跳动和百度来说,第一次卖流量的钱就会越来越少。这和微信不同,微信不卖初次获取的流量,它的变现是后向的,来自于持续使用的微信支付、应用内广告。后向支付和广告收益如何平衡?初次流量销售收益和小程序留存如何平衡?这是2个需要它们妥善取舍的问题。

三、结语

本文给开发者提供了数据参考,也给小程序平台提出了一些问题。

衷心希望各家小程序平台能解决好问题,取得更大的发展;

更祝愿开发者把握住新红利,在大潮中实现自己的梦想。

uni-app将始终在开发者通往成功的道路上助力!

小程序的未来方向

序言:DCloud CTO 崔红保在 GMTC 全球大前端技术大会(深圳站)2019 分享了 《小程序的未来方向》,介绍了小程序技术架构,分析了通讯阻塞、混合渲染、同层渲染等深层次问题,这里是针对演讲整理的文字版,分享给大家,Enjoy~

简单介绍一下我自己:

分享大纲

简要介绍今天的分享大纲,罗马不是一天建成的,小程序也不是一天发明的;小程序这种介于H5和Native App之间的特殊应用形态,从探索到成熟,经历了哪些过程,我们首先带大家回顾梳理一下,然后从现有技术架构出发,分析小程序当下几个主要性能坑点,各家小程序引擎为解决这些坑点,做了哪些完善工作;比如大家知道小程序是以web渲染为主、原生渲染为辅,那引入原生渲染后,引发了哪些新的问题?为解决这些,微信提出了同层渲染的方案,同层渲染在技术层面上又是如何实现的?最后从当前已知问题出发,对于小程序未来的技术更迭,抛出一些我们认为的可能方向,供大家参考。

小程序历史

HTML5 于 2007 年在 W3C 立项,与 iPhone 发布同年。

乔布斯曾期待 HTML5 能帮助 iPhone 打造起应用生态系统。

但 HTML5 的发展速度并不如预期,它虽然成功地实现了打破 IE+Flash 垄断局面的目标,却没有达到承载优秀的移动互联网体验的地步。

于是在 iPhone 站稳脚跟后,发布了自己的 App Store,开启了移动互联网的原生应用时代。

大家知道现在手机端主要是iOS、Android两大系统,实际上在早期有3大系统竞争,还有一个就是诺基亚的Meego系统,MeeGo 采用 C + HTML5 的双模应用生态策略,然而 C 的开发难度太大,HTML5 体验又不行,所以后来 MeeGo就掉队了;与之对应,Android 依靠 Java 技术生态,在竞争中脱颖而出。

于是在移动互联网初期,应用生态被定了基调 —— 原生开发。

在那个时候,硬件不行,也没有其他办法,原生开发才能在低配硬件上带来商用体验。

但大家都在怀念 HTML,那种无需安装更新、即点即用,直达二级页面的特点,一直让人迷恋。

国内有一批做浏览器的厂商,尝试去改进 HTML5,比如百度在2013年的百度世界大会上发布了轻应用,方式是通过给 WebView 扩展原生能力,补充 JS API,让 HTML5 应用可以实现更多功能。

不过这类业务没有取得成功,HTML5 的问题不止是功能不足,性能体验是它更严重的问题,而体验问题,不是简单地扩展 JS 能力能搞定的。

这类业务发展的顶峰,是微信在2015年初发布的微信 JS SDK,作为国内事实上最大的手机浏览器,微信为它的浏览器内核扩充了大量 JS API,让开发者可以用 JS 调用微信支付、扫码等众多 HTML5 做不到的功能。

但微信团队对这套方案的体验仍然不满意,微信钱包栏目里打车、理财等很多应用虽然嵌入了 JS SDK,但每次点击要等半天白屏,让人用着很痛苦,他们在业内开始寻找新的解决方案。

与浏览器不同,Hybrid 应用是另一个细分领域,开发者使用JS编写应用,为了让 JS 应用更接近原生应用的功能体验,这个行业的从业者做出了很多尝试。我们DCloud公司是业内主流Hybrid App引擎提供方之一,我们提出了改进 HTML5 的“性工能”障碍的解决方案 —— 通过工具、引擎优化、开发模式调整,让开发者可以通过 JS 写出更接近原生 App 体验的应用。

多 WebView 模式,原生接管转场动画、下拉刷新、Tab 分页,预载 WebView……各种优化技术不停迭代,终于让 Hybrid 应用取得了性能体验的突破。

Hybrid 应用和普通的轻应用相比,还有一个巨大的差别:一个是 Client/Server,一个是 Browser/Server。简单来说,Hybrid 应用是 JS 编写的需要安装的 App,而轻应用是在线网页。

C/S 的应用在每次页面加载时,仅需要联网获取 JSON 数据;而 B/S 应用除了 JSON 数据外,还需要每次从服务器加载页面 DOM、样式、逻辑代码,所以 B/S 应用的页面加载很慢,体验很差。

可是这样的 C/S 应用虽然体验好,却失去了 HTML5 的动态性,仍然需要安装、更新,无法即点即用、直达二级页面。

那么 C/S 应用的动态性是否可以解决呢?对此,DCloud首先提出了流应用概念,把之前 Hybrid 应用里的运行于客户端的 JS 代码,先打包发布到服务器,制定流式加载协议,手机端引擎动态下载这些 JS 代码到本地,并且为了第一次加载速度更快,实现了应用的边下载边运行。

就像流媒体的边下边播一样,应用也可以实现边用边下。

在这套方案的保障下,终于解决了之前的各种难题:让 JS 应用功能体验达到原生,并且可即点即用、可直达二级页面。

这套技术,需要让客户端引擎提前预置在手机上,就像流媒体的普及,建立在 Flash 的装机量巨大的基础上,那么普及这个客户端引擎就变得很重要。

2015 年,360 和 DCloud 合作,在 360 手机助手里内嵌了这个客户端引擎,推出了业内第一个商用的小程序,360 称之为 360 微应用,这个实际上是国内小程序形态的最早原型。

之后,DCloud曾找过几个大厂探索过流应用的合作方式,并在部分厂商中发行上线,比如金立的应用市场、中国移动的MM市场等;但各大公司的利益诉求不同,流应用想借助大厂流量快速推广的愿望,困难重重。

接着就是微信小程序,最初的名字实际上是微信应用号,之后改名为小程序,2016年9月份内测,2017年1月正式发行,再之后阿里巴巴、手机厂商联盟、百度、今日头条,陆续推出了自己的小程序平台,小程序时代滚滚而来。

小程序火爆了,开发者的困扰也跟着来了,各家小程序规范各不相同,每个平台单独开发一套小程序,人力成本极大,DCloud看到开发者这些真实痛点,随之提供了小程序跨端开发框架,这就是uni-app

2018年9月,微信率先推出云开发,这个功能我们认为是小程序发展历史上的一个重要节点,它可以让前端工程师从前到后将所有业务闭环实现,减少前后端的沟通成本、人力成本、运维成本,属于开发模式的重大升级。和之前前端同学既可通过JS/CSS编写前端UI,又可通过node.js写后端业务,这种所谓全栈开发模式相比,云开发有更好的优势,因为前端同学对于DB优化、弹性扩容、攻击防护、灾备处理等方面还是有经验欠缺的,但云开发将这些都封装好了,真正做到仅专注业务实现,其它都委托云厂商服务。

小程序架构

这是一个比较通用的小程序架构,目前几家小程序架构设计大致都是这样的(快应用的区别是视图层只有原生渲染)。

大家知道小程序是一个逻辑、视图层分离的架构。

逻辑层就是上图左上角这块,小程序中开发的所有页面JS代码,最后都会打包合并到逻辑层,逻辑层除了执行开发者的业务JS代码外,还需处理小程序框架的内置逻辑,比如App生命周期管理。

视图层就是上图右上角这块,用户可见的UI效果、可触发的交互事件在视图层完成,视图层包含web组件、原生组件两种,也就是小程序是原生+web混合渲染的模式,这块后面会详细讲。

逻辑层最后运行在JS CORE或V8环境中;JS CORE既不是DOM环境,也不是node环境,你是无法使用JS中的DOM或BOM对象的,你能调用的仅仅是ECMAScript标准规范中所给出的方法。

那如果你要发送网络请求怎么办?window.XMLHttpRequest 是无法使用的(当然即使可以调用,在iOS的WKWebView中也存在更严格的跨域限制,会有问题)。这时候,网络请求就需要通过原生的网络模块来发送,JS CORE和原生之间呢,就需要这个JS Bridge来通讯。

架构引发的性能坑点

小程序这种架构,最大的好处是新页面加载可以并行,让页面加载更快,且不卡转场动画;但同时也引发了部分性能坑点,今天主要介绍 3 点:

逻辑层/视图层通讯阻塞

我们从swipeaction这个例子讲起,需求是用户在列表项上向左滑动,右侧隐藏的菜单跟随用户手势平滑移动

若想在小程序架构上实现流畅的跟手滑动,是很困难的,为什么?

我们再回顾一下上面的小程序架构,小程序的运行环境分为逻辑层和视图层,分别由2个线程管理,小程序在视图层与逻辑层两个线程间提供了数据传输和事件系统。这样的分离设计,带来了显而易见的好处:

环境隔离,既保证了安全性,同时也是一种性能提升的手段,逻辑和视图分离,即使业务逻辑计算非常繁忙,也不会阻塞渲染和用户在视图层上的交互

但同时也带来了明显的坏处:

  • 视图层(webview)中不能运行JS,而逻辑层JS又无法直接修改页面DOM,数据更新及事件系统只能靠线程间通讯,但跨线程通信的成本极高,特别是需要频繁通信的场景

基于这样的架构设计,我们回到swipeaction,分析一次touchmove的操作,小程序内部的响应过程:

  • 用户拖动列表项,视图层触发touchmove 事件,经Native层中转通知逻辑层(逻辑层、视图层不是直接通讯的,需Native中转),即下图中的⓵、⓶两步
  • 逻辑层计算需移动的位置,然后再通过 setData 传递位置数据到视图层,中间同样会由微信客户端(Native)做中转,即下图中的⓷、⓸两步

实际上,用户滑动过程中,touchmove的回调触发是非常频繁的,每次回调都需要4个步骤的通讯过程,高频率回调导致通讯成本大幅增加,极有可能导致页面卡顿或抖动。为什么会卡顿,因为通讯太过频繁,视图层无法在16毫秒内完成UI更新。

为解决这种通讯阻塞的问题,各家小程序都在逐步提供对应的解决方案,比如微信的WXS、支付宝的SJS、百度的Filter,但每家小程序支持情况不同,详细见下表。

另外,微信的关键帧动画、百度的animation-view Lottie动画,也是为减少频繁通讯的一种变更方式。

其实,通讯阻塞是业界普遍存在的一个问题,不止小程序,react nativeweex等同样存在通讯阻塞的问题。只不过react nativeweex的视图层是原生渲染,而小程序是web渲染。我们下面以weex为例来说明。

大家知道,weex底层使用的 JS-Native Bridge,这个 Bridge 使得 JS 和 Native 之间的通信会有固定的性能损耗。

继续以上述swipeaction为例,要实现列表项菜单的跟手滑动,大致需经如下流程:

  • 在UI视图上绑定 touch 事件(或 pan 事件)
  • 当手势触发时, Native UI层将手势事件通过 Bridge 传递给 JS逻辑层 , 这产生了一次 Native UI到 JS 逻辑的通信,即下图中的⓵、⓶两步
  • JS 逻辑在接收到事件后,根据手指移动的偏移量驱动界面变化,这又会产生一次 JS 到 Native UI的通信,即下图中的⓷、⓸两步

同样,手势回调事件触发的频率是非常高的,频繁的的通信带来的时间成本很可能导致界面无法在16ms中完成绘制,卡顿也就产生了。

weex为解决通讯阻塞,提供了BindingX解决方案,这是一种称之为Expression Binding的机制,简要介绍一下:
– 接收手势事件的视图,在移动过程中的偏移量以x,y两个变量表示
– 期望改变(跟随移动)的视图,变化的属性为translateXtranslateY,对应变化的偏移量以f(x),f(y)表达式表示
– 将”交互行为”以表达式的方式描述,并提前预置到Native UI层
– 交互触发时,Native UI根据其内置的表达式解析引擎,去执行表达式,并根据表达式执行的结果驱动视图变换,这个过程无需和JS逻辑通讯

伪代码 – 摘录自weex官网

{

   anchor: foo_view.ref                    // ----> 这是"产生手势的视图"的引用  
   props:
            [
                {
                    element: foo_view.ref, // ----> 这是"期望改变的视图"的引用
                    expression: f(x) = x,  // ----> 这是具体的表达式
                    property: translateX   // ----> 这是期望改变的属性
                },
                {
                    element: foo_view.ref,
                    expression: f(y) = y,  // ----> y 属性
                    property: translateY
                }
            ]
}

React Native 同样存在类似问题,为避免频繁的通信,React Native 生态也有对应方案,比如Animated组件及Lottie动画支持。以 Animated 组件为例,为实现流畅的动画效果,该组件采用了声明式的API,在 JS 端仅定义了输入与输出以及具体的 transform 行为,而真正的动画是通过 Native Driver 在 Native 层执行,这样就避免了频繁的通信。然而,声明式的方式能够定义的行为有限,无法胜任交互场景。

uni-app在App端同样面临通讯阻塞的问题,我们目前的方案是采用类似微信wxs的机制(内部叫renderjs),但放开了wxs中无法获取页面DOM元素的限制,比如下图中多个小球同时移动的canvas动画,uni-app在App端的实现方案是:

  • renderjs 中获取canvas对象
  • 基于web的canvas绘制动画,而非原生canvas绘制

Tips:大家需要注意,并不是所有场景都是原生性能更好,小程序架构下,如上多球同时移动的动画,原生canvas并不如在wxs(renderjs)中直接调用web canvas

下表总结了跨端框架在通讯阻塞方面的解决方案。

数据/组件差量更新

小程序架构存在通讯阻塞问题,厂商为解决这个问题,创造了wxs脚本语言及关键帧动画等方式,但这些都是厂商维度的优化方案。我们作为小程序开发者,在性能优化方面,又能做哪些工作呢?

小程序开发性能优化,核心就是setData的调用,你能做只有两件事情:
– 尽量少调用setData
– 每次调用setData,传递尽可能少的数据量,即数据差量更新

减少setData调用次数

假设我们有更改多个变量值的需求,示例如下:

change:function(){
    this.setData({a:1});
    ... //其它业务逻辑
    this.setData({b:2});
    ... //其它业务逻辑
    this.setData({c:3});
    ... //其它业务逻辑
    this.setData({d:4});
}

如上4次调用setData,会引发4次逻辑层、视图层数据通讯。这种场景,开发者需意识到setData有极高的调用代价,自己需手动调整代码,合并数据,减少数据通讯次数。

部分小程序三方框架已内置数据合并的能力,比如uni-app在 Vue runtime 上进行了深度定制,开发者无需关注setData的调用代价,可放心编写如下代码:

change:function(){
    this.a = 1;
    ... //其它业务逻辑
    this.b = 2;
    ... //其它业务逻辑
    this.c = 3;
    ... //其它业务逻辑
    this.d = 4;
}

如上4次赋值,uni-app运行时会自动合并成{"a":1,"b":2,"c":3,"d":4}一条记录,调用一次setData完成所有数据传递,大幅降低setData的调用频次,结果如下图:

减少setData调用次数,还有个注意点:后台页面(用户不可见的页面)应避免调用setData

数据差量更新

假设我们有一个 “列表页 + 上拉加载” 的场景,初始化列表项为 “item1 ~ item4”,用户上拉后要向列表追加4条新记录 “item5 ~ item8″,小程序代码如下:

page({
    data:{
        list:['item1','item2','item3','item4']
    },
    change:function(){
        let newData = ['item5','item6','item7','item8'];
        this.data.list.push(...newData); //列表项新增记录
        this.setData({
            list:this.data.list
        })
    }
})

如上代码,change方法执行时,会将list中的 “item1 ~ item8” 8个列表项通过setData全部传输过去,而实际上变化的数据只有”item5 ~ item8″。

开发者在这种场景下,应通过差量计算,仅通过setData传递变化的数据,如下是一个示例代码:

page({
    data:{
        list:['item1','item2','item3','item4']
    },
    change:function(){
        // 通过长度获取下一次渲染的索引
        let index = this.data.list.length;
        let newData = ['item5','item6','item7','item8'];
        let newDataObj = {};//变化的数据
        newData.forEach((item) => {
            newDataObj['list[' + (index++) + ']'] = item;//通过list下标精确控制变更内容
        });
        this.setData(newDataObj) //设置差量数据
    }
})

每次都手动计算差量变更数据是繁琐的,新手不理解小程序原理的话,也容易忽略这些性能点,给App埋下性能坑点。

此处建议开发者选择成熟的小程序三方框架,这些框架已经自动封装差量数据计算,对开发者更友好。比如uni-app借鉴了 westore JSON Diff库,在调用setData之前,会先比对历史数据,精确高效计算出有变化的差量数据,然后再调用setData,仅传输变化的数据,这样可实现传递数据量的最小化,提升通讯性能。如下是一个示例代码:

export default{
    data(){
        return {
            list:['item1','item2','item3','item4']
        }
    },
    methods:{
        change:function(){
            let newData = ['item5','item6','item7','item8'];
            this.list.push(...newData) // 直接赋值,框架会自动计算差量数据
        }
    }
}

Tips:如上change方法执行时,仅会将list中的”item5 ~ item8″4个新增列表项传输过去,实现了setData传输量的极简化

组件差量更新

下图是一个微博列表截图:

假设当前有200条微博,用户对某条微博点赞,需实时变更其点赞数据(状态);在传统模式下,一条微博的点赞状态变更,会将整个页面(Page)的数据全部通过setData传递过去,这个消耗是非常高的;而即使通过之前介绍,通过差量计算的方式获取变更数据,这个 Diff 遍历范围也很大,计算效率极低。

如何实现更高性能的微博点赞?这其实就是组件更新的典型场景。

合适的方式应该是,将每条微博封装成一个组件,用户点赞后,仅在当前组件范围内计算差量数据(可理解为Diff范围缩小为原来的1/200),这样效率才是最高的。

提醒大家注意,并不是所有小程序三方框架都已实现自定义组件,只有在基于自定义组件模式封装的框架,性能才会大幅提升;如果三方框架是基于老的template模板封装的组件开发,则性能并不会有明显改善,其 Diff 对比范围依然是Page页面级的。

混合渲染

大家知道,小程序当中有一类特殊的内置组件——原生组件,这类组件有别于 WebView 渲染的内置组件,他们是由原生客户端渲染的。

小程序中的原生组件,从使用方式上来说,主要分为三类:

  • 通过配置项创建的:选项卡、导航栏,还有下拉刷新
  • 通过组件名称创建的,比如:camera、canvas、input、live-player、live-pusher、map、textarea、video
  • 通过API接口创建的,比如:showModal、showActionSheet等

除了上面提到的这些之外,其它基本都是web渲染。所以说,小程序是混合渲染模式,web渲染为主,原生渲染为辅。

为什么要引入混合渲染

接下来的问题,为什么要引入原生渲染?以及为什么仅针对这几个组件提供了原生增强?其他组件为什么没有做原生实现?

这就需要我们针对每个组件单独进行分析思考,这里举了几个例子:
– tabs/navigationbar:避免页面白屏,提升新窗口进入时的用户体验
– Video:全屏后的滑动控制(声音、进度、亮度等)
– map:更流畅的双指缩放、位置拖动
– input:web端的input,键盘弹出时,只有完成按钮,无法让键盘显示发送下一个这样的按键

提到input控件的原生化,可以稍微发散一下。

小程序中原生input控件的通用做法是,未获取焦点时以web控件显示,但在获取焦点时,绘制一个原生input,盖在web input上方,此时用户看见的键盘即为原生input所对应的键盘,原生弹出键盘是可自定义按钮(如上图中下一步、send按钮)的。这种做法存在一个缺陷:web和原生,毕竟不同渲染引擎,在键盘弹出和关闭时,对应input的placeholder会闪烁。

在Android平台,还有一种做法是基于webkit改造,定制弹出键盘样式;这种方案,在键盘弹出和关闭时,input控件都是web实现的,故不存在placeholder闪烁的问题。

混合渲染引发的问题

原生组件虽然带来了更丰富的特性及更好的性能,但同时也引入了一些新的问题,比如:

1.层级问题:原生永远在最高层,无法通过z-index设置不同元素的层级,无法与 view、image 等内置组件相互覆盖,不支持在picker-viewscroll-viewswiper等组件中使用

2.通讯问题:比如一个长列表中内嵌视频组件,页面滚动时,需通知原生的视频组件一起滚动,通讯阻塞,可能导致组件抖动或拖影

3.字体问题:在Android手机上,调整系统主题字体,所有原生渲染的控件的字体都会变化,而web渲染的字体则不会变化。如下图,系统rom字体为一款“你的名字”的三方字体,设置后,小程序顶部标题字体变了,底部选项卡字体也变了,但小程序中间内容区字体不变,这就是比较尴尬的一种情况,一个页面,两种字体。

当然,并不是所有小程序都存在这种问题,部分小程序通过修改自带的webview内核,实现了webview也可以使用rom主题字体,比如微信、qq、支付宝;其他小程序(百度、头条),webview仍然无法渲染为rom主题字体。

混合渲染改进方案

既然混合渲染有这些问题,对应就会有解决方案,目前已有的方案如下。

方案1:创造层级更高的组件

既然其它组件无法覆盖到原生组件上,那就创造出一种新的组件,让这个新组件可以覆盖到video或map上。cover-view/cover-image就是基于这种需求创造出来的新组件;其实它们也是原生组件,只不过层级略高,可以覆盖在 map、video、canvas、camera等原生组件上。

目前除了字节跳动外,其它几家小程序均已支持cover-view/cover-image

cover-view/cover-image 在一定程度上缓解了分层覆盖的问题,但也有部分限制,比如严格的嵌套顺序。

方案2:消除分层,同层渲染

既然分层有问题,那就消除分层,从2层变成1层,所有组件都在一个层中,z-index岂不就可生效了?

这个小目标说起来简单,具体实现还是很复杂的,下个章节具体介绍。

同层渲染

抛开小程序当前架构实现,解决混合渲染最直接的方案,应该更换渲染引擎,全部基于原生渲染,video/map和image/view均为原生控件,层级相同,层级遮盖问题自然消失。这正是uni-app在App端的推荐方案。

uni-app在App端支持weex原生渲染,至于uni-app如何抹平weex和小程序的各项差异,这是另外一个话题,后续可单独分享。

回归到当前web渲染为主、原生渲染为辅的主流小程序现状,如何实现同层渲染?

基于我们的分析研究,这里简单讲解一下同层渲染实现的方案,和微信真实实现可能会有出入(目前仅微信一家实现了同层渲染)。

iOS平台

小程序在 iOS 端使用 WKWebView 进行渲染,WKWebView 在内部采用的是分层的方式进行渲染,一般会将多个DOM节点,合并到一个层上进行渲染,因此DOM节点和层之间不存在一一对应关系。但是,一旦将一个 DOM 节点的 CSS 属性设置为 overflow: scroll 后,WKWebView 便会为其生成一个 WKChildScrollView,且WebKit 内核已经处理了WKChildScrollView与其他 DOM 节点之间的层级关系,这时DOM节点就和层之间有一一对应关系了。

小程序 iOS 端的同层渲染可基于 WKChildScrollView 实现,主要流程如下:
– 创建一个 DOM 节点并设置其 CSS 属性为 overflow: scroll
– 通知原生层查找到该 DOM 节点对应的原生 WKChildScrollView 组件
– 将原生组件挂载到该 WKChildScrollView 节点上作为其子 View

Android平台

小程序在 Android 端采用 chromium 作为 WebView 渲染层,和iOS的WKWebView不同,是统一渲染的,不会分层渲染。但chromium 支持 WebPlugin 机制,WebPlugin 是浏览器内核的一个插件机制,可用来解析<embed>。Android 端的同层渲染可基于 <embed> 加 chromium 内核扩展来实现,大致流程如下:

  • 原生层创建一个原生组件(如video)
  • WebView 创建一个 <embed> 节点并指定其类型为video
  • chromium 内核创建一个 WebPlugin 实例,并生成一个 RenderLayer
  • 原生层将原生组件的画面绘制到 RenderLayer 所绑定的 SurfaceTexture 上
  • chromium 渲染该 RenderLayer

这个流程相当于给 WebView 添加了一个外置插件,且<embed>节点是真正的 DOM 节点,可将更多的样式作用于该节点上。

未来可能

如果要探讨小程序接下来的技术升级方向,我们认为应该在用户体验、开发效率两个方向上努力。

更优秀的用户体验

先说用户体验的问题,主要也是两个方面:
– 解决现有的性能坑点,比如前面分析的这几项,通讯阻塞、分层限制等,这里不再赘述
– 支持更多App的体验,更自由灵活的配置,比如高斯模糊

如果你也想快速搭建的自己的小程序引擎,并更优的解决如上体验问题,该怎么办?

这里放一个福利。

uni-app发行到App端,实际上就是一个完整的小程序引擎,DCloud会在近期将这个引擎完整开源,欢迎大家基于uni-app小程序SDK快速打造自己的小程序平台。

uni-app小程序SDK具备如下几个特征:
– 性能更高:支持native渲染,扩展wxs,更高的通讯性能
– 开放性更强:更灵活的配置,支持更多App的体验
– 开源不受限:无需签订任何协议,拿走就用
– 生态丰富:支持微信小程序自定义组件,支持所有uni-app插件,uni-app插件市场目前已有上千款成熟插件

开发效率

开发效率应该从跨端、跨云两个维度进行分析。

跨端开发

目前的小程序都带有明显的厂家属性,每个厂家各不相同。之前更遭,阿里内部有多套小程序(支付宝、淘宝、钉钉等),幸好阿里圆老板给力,目前已基本统一。但腾讯体系下,微信和QQ小程序依然是两队人马,两套规范。

小程序之前是手机端的,今年360出了PC端小程序。

接下来,会不会还有其它厂家推出自己的小程序?会不会有新的端的出现?比如面向电视的小程序、面向车载的小程序?

一切皆有可能。

逐水草而居是人类的本能,追求流量依然是互联网的制胜法宝。当前的小程序宿主,都是亿级流量入口,且各家流量政策不同。比如微信的流量最大的,但有各种限制;百度和头条系是支持广告投放的,通过广告投放,可以快速获得大量较为精准的用户;百度小程序还有个web化的功能,可以通过将web的搜索流量,转化成小程序的流量。

面对众多小程序平台及各自巨大的入口流量,开发者如何应对?

等待w3c的小程序标准统一,短期不太现实。当下,若想将业务快速触达多家小程序,借助跨端框架应该是唯一可行的方案。

跨云开发

开发商借助uni-app或其它跨端框架,虽然已可以开发所有前端应用。但仍然需要雇佣php或java等后台开发人员,既有后端人员成本,又有前/后端沟通成本。

腾讯、阿里、百度小程序虽陆续上线了云开发,但它们均只支持自己的小程序,无法跨端,分散的服务器对开发商更不可取。

故我们认为跨厂商的 serverless 是接下来的一个重点需求,开发者在一个云端存储所有业务数据及后端逻辑,然后将前端小程序发行到各家小程序平台,也就是“一云多端”模式。

总结

基于小程序的现状,我们也许可以总结一下小程序技术上的可能方向:
– 其它小程序拉齐与微信的差距,让开发者可以做出足够高性能的应用服务
– 所有小程序应拉齐和App的体验差距,虽然功能API方面仍有不足,但操作性能和交互体验,不应该弱于app
– 跨端框架 + serverless,让开发者更轻松,让企业更高效

OK,我的分享到此结束,若有错误,欢迎交流指正。

阿里支付宝小程序IDE 0.70 Stable版发布,内置uni-app框架

随着微信、阿里、百度、头条、QQ纷纷推出小程序,开发者的开发维护成本持续上升,负担过重。这点已经成为共识。

为了减轻开发者的负担,阿里团队在官方的小程序开发者工具中整合了多端框架。

经过之前1个月的公测,10月10日,阿里小程序正式发布0.70版开发者工具,通过 uni-app 实现多端开发,成为本次版本更新的亮点功能!如下摘自阿里小程序官方更新日志:

我们这个版本内置支持 uni-app 跨平台小程序框架的研发,开发者可以实现一次编写后,生成多个平台体系的小程序代码,避免在不同平台维护不同代码,降低开发和维护成本(点击这里查看教程):

更多更新日志参考:https://docs.alipay.com/mini/ide/0.70-stable

本次阿里小程序工具集成 uni-app,会让 uni-app 继续快速爆发,取得更大的成功。

后续DCloud还将深化与阿里的合作,在serverless等领域给开发者提供更多优质服务。

使用多端框架开发各端应用,是多赢的模式。开发者减轻了负担,获得了更多新流量。而小程序平台厂商,也能保证自己平台上的各种应用可以被及时的更新。