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),测试内容包括以下几个维度:
- 页面渲染耗时
- 页面更新耗时
- 局部更新耗时
测试代码存放于以下仓库中:
- https://github.com/yangmingshan/native-benchmark
- https://github.com/yangmingshan/vue-mini-benchmark
- https://github.com/yangmingshan/taro-benchmark
- Vue Mini 十分小巧,运行时仅 20+KB,不到 Taro 的十分之一。
- Vue Mini 的性能十分接近小程序原生框架,多数操作仅相差几十毫秒,用户几乎无法分辨。
- Vue Mini 的性能遥遥领先于 Taro,在中数据量及以上场景,Vue Mini 比 Taro 快 10 倍以上。
为了使测试结果真实有效,我基于常见的业务场景构建了两种测试场景,分别是动态测试场景和静态测试场景。
动态测试中,视图基于数据动态渲染,静态节点较少,视图更新耗时是该测试场景中的主要测试点。
动态测试 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 | 崩溃⽩屏 | 崩溃⽩屏 | 崩溃⽩屏 | 崩溃⽩屏 |
首先我们点击 新增不可用券(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 毫秒,对用户来说操作毫无延迟十分跟手。