微信小程序的原生框架和taro对比

3,658 阅读10分钟

微信小程序的原生框架和taro对比

小程序历史(为什么会出现?)

2007年 iPhone H5

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

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

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

但是 :大家都在怀念 HTML

- HTML 优点:无需安装更新、即点即用,直达二级页面的特点,一直让人迷恋

- HTML缺点:不止是功能不足,性能体验是它更严重的问题,而体验问题,不是简单地扩展 JS 能力能搞定的。

2013 百度轻应用

方式是通过给 WebView 扩展原生能力,补充 JS API,让 HTML5 应用可以实现更多功能

2015 微信 JS SDK

国内事实上最大的手机浏览器,微信为它的浏览器内核扩充了大量 JS API,让开发者可以用 JS 调用微信支付、扫码等众多 HTML5 做不到的功能。
但每次点击要等半天白屏,让人用着很痛苦

Hybrid 应用

通过工具、引擎优化、开发模式调整,让开发者可以通过 JS 写出更接近原生 App 体验的应用。

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

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

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

那么 C/S 应用的动态性是否可以解决呢?

流应用概念,把之前 Hybrid 应用里的运行于客户端的 JS 代码,先打包发布到服务器,制定流式加载协议,手机端引擎动态下载这些 JS 代码到本地,并且为了第一次加载速度更快,实现了应用的边下载边运行。

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

2015 第一个小程序 360 称之为 360 微应用

2016 微信小程序,最初的名字实际上是微信应用号,之后改名为小程序

再之后阿里巴巴、手机厂商联盟、百度、今日头条,陆续推出了自己的小程序平台,小程序时代滚滚而来

2018年9月,微信率先推出云开发

小程序架构

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

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

网页开发渲染线程和脚本线程是互斥的,这也是为什么长时间的脚本运行可能会导致页面失去响应,而在小程序中,二者是分开的,分别运行在不同的线程中。

架构引发的性能坑点

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

逻辑 视图层 通讯阻塞

数据 、组件 的差量更新

同层渲染 和 混合渲染

逻辑层/视图层通讯阻塞

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

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

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

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

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

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

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

  • 用户拖动列表项,视图层触发touchmove 事件,经Native层中转通知逻辑层(逻辑层、视图层不是直接通讯的,需Native中转),即下图中的⓵、⓶两步

  • 逻辑层计算需移动的位置,然后再通过 setData 传递位置数据到视图层,中间同样会由微信客户端(Native)做中转,即下图中的⓷、⓸两步

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

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

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

小程序代码构成

目前各小程序都有对主包的大小进行限制,微信小程序限制为 2M。这是因为初次进入的速度对于用户的体验非常地关键,而主包体积越大下载的时间就最长。因此小程序框架的大小也成为了开发前框架选型的重要参考指标之一,倘若框架体积过大,就会压缩业务逻辑的可用空间。

数据/组件差量更新

小程序架构存在通讯阻塞问题,厂商为解决这个问题,创造了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有极高的调用代价,自己需手动调整代码,合并数据,减少数据通讯次数。

部分小程序三方框架已内置数据合并的能力,开发者无需关注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

组件差量更新

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

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

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

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

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

框架性能 PK (平均值)

包大小

原生 < Wepy < Taro < Mpvue < Uni-app < Chameleon

微信小程序的性能

Taro > Mpvue > Uni-app > Wepy > Chamelon > 未优化过的原生代码

框架使用后比原生的性能更好,这简直逆天了,后来研究发现:

微信原生框架耗时主要在 setData 调用上,开发者若不单独优化,则每次都会传递大量数据;而 Uni-app、Taro 等都在调用 setData 之前自动做 diff 计算,每次仅传递变动的数据。

生态

小程序

小程序的逻辑层和渲染层是分开的,逻辑层运行在 JSCore 中,并没有一个完整浏览器对象,因而缺少相关的DOM API和BOM API。这一区别导致了前端开发非常熟悉的一些库,例如 jQuery、 Zepto 等,在小程序中是无法运行的。同时 JSCore 的环境同 NodeJS 环境也是不尽相同,所以一些 NPM 的包在小程序中也是无法运行的。

Taro

中不但能自由引用 npm 包,而且还大量支持 React 社区中沉淀的优秀工具和库,如 react-redux、mobx-react 等。

到这里大家可能会问,Taro 性能真的优于原生吗?其实并不然,针对每个场景,我们都可以用原生写出性能最佳的代码。但是这样做工作量太大,实际项目开发中需要掌握效率与优化之间的平衡。Taro 的优势在于能让我们在书写更有效率的代码、拥有更丰富的生态的同时,还带来了不错的性能。