yangmingshan / mp-framework-benchmark

mp-framework-benchmark
MIT License
5 stars 0 forks source link

小程序框架性能大比拼

[!NOTE] 此 benchmark fork 至 hiyuki/mp-framework-benchmark,原作者为董宏平(hiyuki),本文中的第一人称也指原作者。在此表示感谢。

本文的所有数据均使用 iPhone XR 测得(iOS v17.5.1,微信 v8.0.49),测试全程手机电量维持 100%,并连接着充电器。每项测试跑三次取平均数,数据单位为毫秒(ms)。

在本文中,我们将对小程序原生框架、Vue Mini (v1.0.0-rc.11) 以及 Taro (v3.6.32) 进行性能测试(Taro 使用 React),测试内容包括以下几个维度:

测试代码存放于以下仓库中:

太长不看版

测试方案

为了使测试结果真实有效,我基于常见的业务场景构建了两种测试场景,分别是动态测试场景和静态测试场景。

动态测试场景

动态测试中,视图基于数据动态渲染,静态节点较少,视图更新耗时是该测试场景中的主要测试点。

动态测试 Demo 模拟了实际业务中常见的长列表 + 多 Tab 场景,该 Demo 中存在两份优惠券列表数据,一份为可用券数据,另一份为不可用券数据,其中同一时刻视图中只会渲染展示其中一份数据,可以在上方的操作区模拟对列表数据的各种操作及视图展示切换(切换 Tab)。

在动态测试中,我在外部通过函数代理的方式在初始化之前将 App、Page 和 Component 构造器进行代理,通过 mixin 的方式在 Page 的 onLoad 和 Component 的 created 钩子中注入 setData 拦截逻辑,对所有页面和组件的 setData 调用进行监听,并统计小程序的视图更新耗时。该测试方式能够做到对框架代码的零侵入,能够跟踪到小程序全量的 setData 行为并进行独立的耗时计算,具有很强的普适性,代码具体实现可以查看:https://github.com/hiyuki/mp-framework-benchmark/blob/master/utils/proxy.js

静态测试场景

静态测试模拟业务中静态页面的场景,如运营活动和文章等页面,页面内具备大量的静态节点,而没有数据动态渲染,初始 ready 耗时是该场景下测试的重心。

静态测试 Demo 使用了我去年发表的一篇技术文章的 html 代码进行小程序适配构建,其中包含大量静态节点及文本内容。

测试流程及数据

框架体积

我们统计每个框架 Hello World 小程序的体积:

Hello World (KB)
Native 1
Vue Mini 26
Taro 280

动态测试

页面更新耗时(无后台数据)

这里后台数据的定义为 data 中存在但当前页面渲染中未使用到的数据,在这个 Demo 场景下即为不可用券的数据,当前会在不可用券为 0 的情况下,对可用券列表进行各种操作,并统计更新耗时。

更新耗时的计算方式是从数据操作事件触发开始到对应的 setData 回调完成的耗时。

理论上来讲 Native 的性能在进行优化的前提下一定是所有框架的天花板,但是在日常业务开发中我们可能无法对每一次 setData 都进行优化,以下性能测试中所有的 Native 数据均采用修改数据后全量发送的形式来实现。

第一项测试我们使用 新增可用券(100) 操作将可用券数量由 0 逐级递增到 1000:

100 200 300 400 500 600 700 800 900 1000
Native 82.7 68.3 72 78.7 84.7 93.3 93.7 101.3 109.3 113.3
Vue Mini 101 100.3 104.7 114.7 130 129.7 146.3 157 169.7 179.7
Taro 450.3 629.7 827.7 1021 1227.3 1446.3 1676.3 1867.7 2058 2266

然后我们按顺序逐项点击 删除可用券(all) -> 新增可用券(1000) -> 更新可用券(1) -> 更新可用券(all) -> 删除可用券(1)

Delete (All) Add (1000) Update (1) Update (All) Delete (1)
Native 55.7 430 82.7 86 80
Vue Mini 66.3 587 147.3 134.3 146.3
Taro 137.7 3997.3 2057.3 2491.7 2551.7

页面更新耗时(有后台数据)

刷新页面后我们使用 新增不可用券(1000) 创建后台数据

Add Invalid (1000)
Native 35.7
Vue Mini 91.7
Taro 0

Taro 仅会在界面更新时调用 setData,因此此处时间为 0,下同。

然后我们执行和上面无后台数据时相同的操作进行耗时统计,首先是递增 100:

100 200 300 400 500 600 700 800 900 1000
Native 84 82.7 74.3 78.7 80.7 91.7 99 106 114 130
Vue Mini 105.7 99 111.7 116.3 127.3 138 139.7 152.3 163.7 177.3
Taro 468.7 630 822.3 1028.3 1217 1438.7 1662 1860 2041.3 2271.7

然后按下表操作顺序逐项点击并统计:

Delete (All) Add (1000) Update (1) Update (All) Delete (1)
Native 61.3 425.7 91.3 82.3 83.7
Vue Mini 65 591 135 141 139.7
Taro 141 4033.3 2075 2483.7 2570.3

页面更新耗时(大数据量场景)

首先还是在无后台数据场景下使用 新增可用券(1000) 将可用券数量递增至 5000:

1000 2000 3000 4000 5000
Native 458.7 446.3 537.3 615 743.3
Vue Mini 620.3 719.3 814.3 932 1058.3
Taro 4109 崩溃⽩屏 崩溃⽩屏 崩溃⽩屏 崩溃⽩屏

然后刷新页面点击 新增不可用券(5000) 将后台数据量增加至 5000,再测试可用券数量递增至 5000 的耗时:

Add Invalid (5000)
Native 121.7
Vue Mini 338
Taro 0
1000 2000 3000 4000 5000
Native 471.3 451 557.7 639 803.3
Vue Mini 648.3 724 799 994.3 1108.7
Taro 4103.3 崩溃⽩屏 崩溃⽩屏 崩溃⽩屏 崩溃⽩屏

页面更新耗时(切换 Tab)

首先我们点击 新增不可用券(1000) 将后台数据量增加至 1000,然后我们点击 切换到不可用券,测量两次操作的耗时:

Add Invalid (1000) Switch to Invalid
Native 33.3 436.7
Vue Mini 91 551.3
Taro 0 3751.3

局部更新耗时

我们分别在可用券数量为 100 和 1000 的情况下,点击任意一张可用券触发选中状态,以测试局部更新性能:

Select (100) Select (1000)
Native 6 4.3
Vue Mini 5.3 5
Taro 242.7 2030.7

静态测试

Page Render
Native 94
Vue Mini 103
Taro 148.3

测试结论

Vue Mini 的 Hello World 小程序仅比 Native 多 25KB,这就是 Vue Mini 的全部运行时,体积不到 Taro 的十分之一。相较于小程序 2M 的体积限制,Vue Mini 运行时仅占 1%。

通过上面的数据不难看出,不论是静态渲染还是动态更新,Vue Mini 都有着接近小程序原生框架的表现。Vue Mini 多数耗时仅比 Native 多几十毫秒,局部更新的耗时更是相差无几,仅在大数据量场景能拉开上百毫秒。一般一百毫秒是用户能察觉的最小时间尺度,也就是说从用户视角来看,使用 Vue Mini 几乎没有可感知的性能损耗。

相较于 Taro,Vue Mini 在性能上只能说是遥遥领先。在小数据量场景,Vue Mini 比 Taro 快 4 倍以上。在中数据量及以上场景,Vue Mini 更是比 Taro 快 10 倍以上。当数据量达到 2000 时 Taro 更是直接崩溃白屏无法工作,而 Vue Mini 则可以轻松处理 5000 的数据量。更重要的是这种性能差距对用户来说是可感知的,例如在 100 张可用券的情况下,选中一张券 Taro 需要 242 毫秒,这个时间用户已经可以很明显的感觉到操作延迟。而 Vue Mini 只需要 5 毫秒,对用户来说操作毫无延迟十分跟手。