寻找性能之王 - 七大热门小程序框架豪横评

2023-12-16 17:30

本文主要是介绍寻找性能之王 - 七大热门小程序框架豪横评,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者:董宏平(hiyuki),滴滴出行小程序负责人,mpx框架负责人及核心作者

随着小程序在商业上的巨大成功,小程序开发在国内前端领域越来越受到重视,为了方便广大开发者更好地进行小程序开发,各类小程序框架也层出不穷,呈现出百花齐放的态势。但是到目前为止,业内一直没有出现一份全面、详细、客观、公正的小程序框架测评报告,为小程序开发者在技术选型时提供参考。于是我便筹划推出一系列文章,对业内流行的小程序框架进行一次全方位的、客观公正的测评,本文是系列文章的第一篇——运行时性能篇。

在本文中,我们会对下列框架进行运行时性能测试(排名不分先后):

  • wepy2(https://github.com/Tencent/wepy) @2.0.0-alpha.20

  • uniapp(https://github.com/dcloudio/u...) @2.0.0-26120200226001

  • mpx(https://github.com/didi/mpx) @2.5.3

  • chameleon(https://github.com/didi/chame...) @1.0.5

  • mpvue(https://github.com/Meituan-Di...) @2.0.6

  • kbone(https://github.com/Tencent/kb...) @0.8.3

  • taro next(https://github.com/NervJS/taro) @3.0.0-alpha.5

其中对于kbone和taro next均以vue作为业务框架进行测试。

运行时性能的测试内容包括以下几个维度:

  • 框架运行时体积

  • 页面渲染耗时

  • 页面更新耗时

  • 局部更新耗时

  • setData调用次数

  • setData发送数据大小

框架性能测试demo全部存放于https://github.com/hiyuki/mp-... 中,欢迎广大开发者进行验证纠错及补全;

测试方案

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

动态测试场景

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

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

动态测试demo

动态测试demo

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

静态测试场景

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

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

静态测试demo

静态测试demo

测试流程及数据

以下所有耗时类的测试数据均为微信小程序中真机进行5次测试计算平均值得出,单位均为ms。Ios测试环境为手机型号iPhone 11,系统版本13.3.1,微信版本7.0.12,安卓测试环境为手机型号小米9,系统版本Android10,微信版本7.0.12。

为了使数据展示不过于混乱复杂,文章中所列的数据以Ios的测试结果为主,安卓测试结论与Ios相符,整体耗时比Ios高3~4倍左右,所有的原始测试数据存放在https://github.com/hiyuki/mp-...

由于transform-runtime引入的core-js会对框架的运行时体积和运行耗时带来一定影响,且不是所有的框架都会在编译时开启transform-runtime,为了对齐测试环境,下述测试均在transform-runtime关闭时进行。

框架运行时体积

由于不是所有框架都能够使用webpack-bundle-analyzer得到精确的包体积占用,这里我通过将各框架生成的demo项目体积减去native编写的demo项目体积作为框架的运行时体积。


demo总体积(KB)框架运行时体积(KB)
native270
wepy26639
uniapp11487
mpx7851
chameleon136109
mpvue10376
kbone395368
taro next183156

该项测试的结论为:
native > wepy2 > mpx > mpvue > uniapp > chameleon > taro next > kbone

结论分析:

  • wepy2和mpx在框架运行时体积上控制得最好;

  • taro next和kbone由于动态渲染的特性,在dist中会生成递归渲染模板/组件,所以占用体积较大。

页面渲染耗时(动态测试)

我们使用刷新页面操作触发页面重新加载,对于大部分框架来说,页面渲染耗时是从触发刷新操作到页面执行onReady的耗时,但是对于像kbone和taro next这样的动态渲染框架,页面执行onReady并不代表视图真正渲染完成,为此,我们设定了一个特殊规则,在页面onReady触发的1000ms内,在没有任何操作的情况下出现setData回调时,以最后触发的setData回调作为页面渲染完成时机来计算真实的页面渲染耗时,测试结果如下:


页面渲染耗时
native60.8
wepy264
uniapp56.4
mpx52.6
chameleon56.4
mpvue117.8
kbone98.6
taro next89.6

该项测试的耗时并不等同于真实的渲染耗时,由于小程序自身没有提供performance api,真实渲染耗时无法通过js准确测试得出,不过从得出的数据来看该项数据依然具备一定的参考意义。

该项测试的结论为:
mpx ≈ chameleon ≈ uniapp ≈ native ≈ wepy2 > taro next ≈ kbone ≈ mpvue

结论分析:

  • 由于mpvue全量在页面进行渲染,kbone和taro next采用了动态渲染技术,页面渲染耗时较长,其余框架并无太大区别。

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

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

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

mpvue中使用了当前时间戳(new Date)作为超时依据对setData进行了超时时间为50ms的节流操作,该方式存在严重问题,当vue内单次渲染同步流程执行耗时超过50ms时,后续组件patch触发的setData会突破这个节流限制,以50ms每次的频率对setData进行高频无效调用。在该性能测试demo中,当优惠券数量超过500时,界面就会完全卡死。为了顺利跑完整个测试流程,我对该问题进行了简单修复,使用setTimeout重写了节流部分,确保在vue单次渲染流程同步执行完毕后才会调用setData发送合并数据,之后mpvue的所有性能测试都是基于这个patch版本来进行的,该patch版本存放在https://github.com/hiyuki/mp-...

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

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


1002003004005006007008009001000
native84.669.871.67577.278.882.893.293.4105.4
wepy2118.4168.6204.6246.4288.6347.8389.2434.2496539
uniapp121.21009698.297.899.6104102.4109.4107.6
mpx110.487.282.28380.679.686.690.689.296.4
chameleon116.8115.4117119.6122125.2133.8133.2144.8145.6
mpvue112.8121.2140169198.8234.2278.8318.4361.4408.2
kbone556.4762.4991.61220.61468.81689.61933.22150.423892620.6
taro next470604.6759.6902.41056.212281393.41536.21707.81867.2

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


delete(all)add(1000)update(1)update(all)delete(1)
native32.8295.692.292.283
wepy256.8726.449.2535530.8
uniapp43.6584.454.8144.8131.2
mpx41.8489.652.6169.4165.6
chameleon39765.695.6237.8144.8
mpvue103.6669.4404.4414.8433.6
kbone120.249782356.42419.42357
taro next126.63930.61607.81788.62318.2

该项测试中初期我update(all)的逻辑是循环对每个列表项进行更新,形如listData.forEach((item)=>{item.count++}),发现在chameleon框架中执行界面会完全卡死,追踪发现chameleon框架中没有对setData进行异步合并处理,而是在数据变动时直接同步发送,这样在数据量为1000的场景下用该方式进行更新会高频触发1000次setData,导致界面卡死;对此,我在chameleon框架的测试demo中,将update(all)的逻辑调整为深clone产生一份更新后的listData,再将其整体赋值到this.listData当中,以确保该项测试能够正常进行。

该项测试的结论为:
native > mpx ≈ uniapp > chameleon > mpvue > wepy2 > taro next > kbone

结论分析:

  • mpx和uniapp在框架内部进行了完善的diff优化,随着数据量的增加,两个框架的新增耗时没有显著上升;

  • wepy2会在数据变更时对props数据也进行setData,在该场景下造成了大量的无效性能损耗,导致性能表现不佳;

  • kbone和taro next采用了动态渲染方案,每次新增更新时会发送大量描述dom结构的数据,与此同时动态递归渲染的耗时也远大于常规的静态模板渲染,使得这两个框架在所有的更新场景下耗时都远大于其他框架。

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

刷新页面后我们使用新增不可用券(1000)创建后台数据,观察该操作是否会触发setData并统计耗时


back add(1000)
native45.2
wepy2174.6
uniapp89.4
mpx0
chameleon142.6
mpvue134
kbone0
taro next0

mpx进行setData优化时inspired by vue,使用了编译时生成的渲染函数跟踪模板数据依赖,在后台数据变更时不会进行setData调用,而kbone和taro next采用了动态渲染技术模拟了web底层环境,在上层完整地运行了vue框架,也达到了同样的效果。

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


1002003004005006007008009001000
native8869.871.280.879.484.489.893.299.6108
wepy2121173.4213.6250298345.6383434.8476.8535.6
uniapp135.4112.4110.6106.4109.6107.2114.4116118.8117.4
mpx112.686.284.686.89087.291.288.892.493.4
chameleon178.4178.2186.4184.6192.6203.8210217.6232.6236.8
mpvue139151173.4194231.4258.8303.4340.4384.6429.4
kbone559.8746.6980.61226.81450.61705.41927.22154.82367.82617
taro next482.6626.2755909.610851233.213841568.61740.61883.8

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


delete(all)add(1000)update(1)update(all)delete(1)
native43.4299.889.28987.2
wepy243.2762.450533522.4
uniapp57.8589.862.6160.6154.4
mpx45.8490.852.8167166
chameleon93.8837184.6318220.8
mpvue124.8696.2423.4419430.6
kbone121.44978.22331.22448.42348
taro next129.83947.21610.41813.82290.2

该项测试的结论为:
native > mpx > uniapp > chameleon > mpvue > wepy2 > taro next > kbone

结论分析:

  • 具备模板数据跟踪能力的三个框架mpx,kbone和taro next在有后台数据场景下耗时并没有显著增加;

  • wepy2当中的diff精度不足,耗时也没有产生明显变化;

  • 其余框架由于每次更新都会对后台数据进行deep diff,耗时都产生了一定提升。

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

由于mpvue和taro next的渲染全部在页面中进行,而kbone的渲染方案会额外新增大量的自定义组件,这三个框架都会在优惠券数量达到2000时崩溃白屏,我们排除了这三个框架对其余框架进行大数据量场景下的页面更新耗时测试

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


10002000300040005000
native332.6350412.6498.2569.4
wepy2970.21531.42015.22890.63364.2
uniapp655.2593.4655675.6718.8
mpx532.2496548.6564601.8
chameleon805.4839.6952.81086.61291.8

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


back add(5000)
native117.4
wepy2511.6
uniapp285
mpx0
chameleon824

10002000300040005000
native349.8348.4430.4497594.8
wepy2112818722470.43263.44075.8
uniapp715666.8709.2755.6810.2
mpx538.8501.8562.6573.6595.2
chameleon1509.21672.41951.82232.42586.2

该项测试的结论为:
native > mpx > uniapp > chameleon > wepy2

结论分析:

  • 在大数据量场景下,框架之间基础性能的差异会变得更加明显,mpx和uniapp依然保持了接近原生的良好性能表现,而chameleon和wepy2则产生了比较显著的性能劣化。

局部更新耗时

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


toggleSelect(ms)
native2
wepy22.6
uniapp2.8
mpx2.2
chameleon2
mpvue289.6
kbone2440.8
taro next1975

该项测试的结论为:
native ≈ chameleon ≈ mpx ≈ wepy2 ≈ uniapp > mpvue > taro next > kbone

结论分析:

  • 可以看出所有使用了原生自定义组件进行组件化实现的框架局部更新耗时都极低,这足以证明小程序原生自定义组件的优秀性和重要性;

  • mpvue由于使用了页面更新,局部更新耗时显著增加;

  • kbone和taro next由于递归动态渲染的性能开销巨大,导致局部更新耗时同样巨大。

setData调用

我们将proxySetData的count和size选项设置为true,开启setData的次数和体积统计,重新构建后按照以下流程执行系列操作,并统计setData的调用次数和发送数据的体积。

操作流程如下:

  1. 100逐级递增可用券(0->500)

  2. 切换至不可用券

  3. 新增不可用券(1000)

  4. 100逐级递增可用券(500->1000)

  5. 更新可用券(all)

  6. 切换至可用券

操作完成后我们使用getCountgetSize方法获取累积的setData调用次数和数据体积,其中数据体积计算方式为JSON.stringify后按照utf-8编码方式进行体积计算,统计结果为:


countsize(KB)
native14803
wepy235141124
mpvue162127
uniapp14274
mpx8261
chameleon2515319
kbone2210572
taro next92321

该项测试的结论为:
mpx > uniapp > native > chameleon > wepy2 > taro next > mpvue > kbone

结论分析:

  • mpx框架成功实现了理论上setData的最优;

  • uniapp由于缺失模板追踪能力紧随其后;

  • chameleon由于组件每次创建时都会进行一次不必要的setData,产生了大量无效setData调用,但是数据的发送本身经过diff,在数据发送量上表现不错;

  • wepy2的组件会在数据更新时调用setData发送已经更新过的props数据,因此也产生了大量无效调用,且diff精度不足,发送的数据量也较大;

  • taro next由于上层完全基于vue,在数据发送次数上控制到了9次,但由于需要发送大量的dom描述信息,数据发送量较大;

  • mpvue由于使用较长的数据路径描述数据对应的组件,也产生了较大的数据发送量;

  • kbone对于setData的调用控制得不是很好,在上层运行vue的情况依然进行了22次数据发送,且发送的数据量巨大,在此流程中达到了惊人的10MB。

页面渲染耗时(静态测试)

此处的页面渲染耗时与前面描述的动态测试场景中相同,测试结果如下:


页面渲染耗时
native70.4
wepy286.6
mpvue115.2
uniapp69.6
mpx66.6
chameleon65
kbone144.2
taro next119.8

该项测试的结论为:
chameleon ≈ mpx ≈ uniapp ≈ native > wepy2 > mpvue ≈ taro next > kbone

结论分析:

  • 除了kbone和taro next采用动态渲染耗时增加,mpvue使用页面模板渲染性能稍差,其余框架的静态页面渲染表现都和原生差不多。

结论

综合上述测试数据,我们得到最终的小程序框架运行时性能排名为:
mpx > uniapp > chameleon > wepy2 > mpvue > taro next > kbone

一点私货

虽然kbone和taro next采用了动态渲染技术在性能表现上并不尽如人意,但是我依然认为这是很棒的技术方案。虽然本文从头到位都在进行性能测试和对比,但性能并不是框架的全部,开发效率和高可用性仍然是框架的重心,开发效率相信是所有框架设计的初衷,但是高可用性却在很大程度被忽视。从这个角度来说,kbone和taro next是非常成功的,不同于过去的转译思路,这种从抹平底层渲染环境的做法能够使上层web框架完整运行,在框架可用性上带来非常大的提升,非常适合于运营类简单小程序的迁移和开发。

我主导开发的mpx框架 https://github.com/didi/mpx 选择了另一条道路解决可用性问题,那就是基于小程序原生语法能力进行增强,这样既能避免转译web框架时带来的不确定性和不稳定性,同时也能带来非常接近于原生的性能表现,对于复杂业务小程序的开发者来说,非常推荐使用。在跨端输出方面,mpx目前能够完善支持业内全部小程序平台和web平台的同构输出,滴滴内部最重要最复杂的小程序——滴滴出行小程序完全基于mpx进行开发,并利用框架提供的跨端能力对微信和支付宝入口进行同步业务迭代,大大提升了业务开发效率。

❤️ 看完三件事如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:

点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)

关注我的博客 https://github.com/SHERlocked93/blog,让我们成为长期关系

关注公众号「前端下午茶」,持续为你推送精选好文,也可以加我为好友,随时聊骚。

在看点这里

作者:董宏平(hiyuki),滴滴出行小程序负责人,mpx框架负责人及核心作者

随着小程序在商业上的巨大成功,小程序开发在国内前端领域越来越受到重视,为了方便广大开发者更好地进行小程序开发,各类小程序框架也层出不穷,呈现出百花齐放的态势。但是到目前为止,业内一直没有出现一份全面、详细、客观、公正的小程序框架测评报告,为小程序开发者在技术选型时提供参考。于是我便筹划推出一系列文章,对业内流行的小程序框架进行一次全方位的、客观公正的测评,本文是系列文章的第一篇——运行时性能篇。

在本文中,我们会对下列框架进行运行时性能测试(排名不分先后):

  • wepy2(https://github.com/Tencent/wepy) @2.0.0-alpha.20

  • uniapp(https://github.com/dcloudio/u...) @2.0.0-26120200226001

  • mpx(https://github.com/didi/mpx) @2.5.3

  • chameleon(https://github.com/didi/chame...) @1.0.5

  • mpvue(https://github.com/Meituan-Di...) @2.0.6

  • kbone(https://github.com/Tencent/kb...) @0.8.3

  • taro next(https://github.com/NervJS/taro) @3.0.0-alpha.5

其中对于kbone和taro next均以vue作为业务框架进行测试。

运行时性能的测试内容包括以下几个维度:

  • 框架运行时体积

  • 页面渲染耗时

  • 页面更新耗时

  • 局部更新耗时

  • setData调用次数

  • setData发送数据大小

框架性能测试demo全部存放于https://github.com/hiyuki/mp-... 中,欢迎广大开发者进行验证纠错及补全;

测试方案

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

动态测试场景

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

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

动态测试demo

动态测试demo

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

静态测试场景

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

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

静态测试demo

静态测试demo

测试流程及数据

以下所有耗时类的测试数据均为微信小程序中真机进行5次测试计算平均值得出,单位均为ms。Ios测试环境为手机型号iPhone 11,系统版本13.3.1,微信版本7.0.12,安卓测试环境为手机型号小米9,系统版本Android10,微信版本7.0.12。

为了使数据展示不过于混乱复杂,文章中所列的数据以Ios的测试结果为主,安卓测试结论与Ios相符,整体耗时比Ios高3~4倍左右,所有的原始测试数据存放在https://github.com/hiyuki/mp-...

由于transform-runtime引入的core-js会对框架的运行时体积和运行耗时带来一定影响,且不是所有的框架都会在编译时开启transform-runtime,为了对齐测试环境,下述测试均在transform-runtime关闭时进行。

框架运行时体积

由于不是所有框架都能够使用webpack-bundle-analyzer得到精确的包体积占用,这里我通过将各框架生成的demo项目体积减去native编写的demo项目体积作为框架的运行时体积。


demo总体积(KB)框架运行时体积(KB)
native270
wepy26639
uniapp11487
mpx7851
chameleon136109
mpvue10376
kbone395368
taro next183156

该项测试的结论为:
native > wepy2 > mpx > mpvue > uniapp > chameleon > taro next > kbone

结论分析:

  • wepy2和mpx在框架运行时体积上控制得最好;

  • taro next和kbone由于动态渲染的特性,在dist中会生成递归渲染模板/组件,所以占用体积较大。

页面渲染耗时(动态测试)

我们使用刷新页面操作触发页面重新加载,对于大部分框架来说,页面渲染耗时是从触发刷新操作到页面执行onReady的耗时,但是对于像kbone和taro next这样的动态渲染框架,页面执行onReady并不代表视图真正渲染完成,为此,我们设定了一个特殊规则,在页面onReady触发的1000ms内,在没有任何操作的情况下出现setData回调时,以最后触发的setData回调作为页面渲染完成时机来计算真实的页面渲染耗时,测试结果如下:


页面渲染耗时
native60.8
wepy264
uniapp56.4
mpx52.6
chameleon56.4
mpvue117.8
kbone98.6
taro next89.6

该项测试的耗时并不等同于真实的渲染耗时,由于小程序自身没有提供performance api,真实渲染耗时无法通过js准确测试得出,不过从得出的数据来看该项数据依然具备一定的参考意义。

该项测试的结论为:
mpx ≈ chameleon ≈ uniapp ≈ native ≈ wepy2 > taro next ≈ kbone ≈ mpvue

结论分析:

  • 由于mpvue全量在页面进行渲染,kbone和taro next采用了动态渲染技术,页面渲染耗时较长,其余框架并无太大区别。

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

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

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

mpvue中使用了当前时间戳(new Date)作为超时依据对setData进行了超时时间为50ms的节流操作,该方式存在严重问题,当vue内单次渲染同步流程执行耗时超过50ms时,后续组件patch触发的setData会突破这个节流限制,以50ms每次的频率对setData进行高频无效调用。在该性能测试demo中,当优惠券数量超过500时,界面就会完全卡死。为了顺利跑完整个测试流程,我对该问题进行了简单修复,使用setTimeout重写了节流部分,确保在vue单次渲染流程同步执行完毕后才会调用setData发送合并数据,之后mpvue的所有性能测试都是基于这个patch版本来进行的,该patch版本存放在https://github.com/hiyuki/mp-...

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

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


1002003004005006007008009001000
native84.669.871.67577.278.882.893.293.4105.4
wepy2118.4168.6204.6246.4288.6347.8389.2434.2496539
uniapp121.21009698.297.899.6104102.4109.4107.6
mpx110.487.282.28380.679.686.690.689.296.4
chameleon116.8115.4117119.6122125.2133.8133.2144.8145.6
mpvue112.8121.2140169198.8234.2278.8318.4361.4408.2
kbone556.4762.4991.61220.61468.81689.61933.22150.423892620.6
taro next470604.6759.6902.41056.212281393.41536.21707.81867.2

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


delete(all)add(1000)update(1)update(all)delete(1)
native32.8295.692.292.283
wepy256.8726.449.2535530.8
uniapp43.6584.454.8144.8131.2
mpx41.8489.652.6169.4165.6
chameleon39765.695.6237.8144.8
mpvue103.6669.4404.4414.8433.6
kbone120.249782356.42419.42357
taro next126.63930.61607.81788.62318.2

该项测试中初期我update(all)的逻辑是循环对每个列表项进行更新,形如listData.forEach((item)=>{item.count++}),发现在chameleon框架中执行界面会完全卡死,追踪发现chameleon框架中没有对setData进行异步合并处理,而是在数据变动时直接同步发送,这样在数据量为1000的场景下用该方式进行更新会高频触发1000次setData,导致界面卡死;对此,我在chameleon框架的测试demo中,将update(all)的逻辑调整为深clone产生一份更新后的listData,再将其整体赋值到this.listData当中,以确保该项测试能够正常进行。

该项测试的结论为:
native > mpx ≈ uniapp > chameleon > mpvue > wepy2 > taro next > kbone

结论分析:

  • mpx和uniapp在框架内部进行了完善的diff优化,随着数据量的增加,两个框架的新增耗时没有显著上升;

  • wepy2会在数据变更时对props数据也进行setData,在该场景下造成了大量的无效性能损耗,导致性能表现不佳;

  • kbone和taro next采用了动态渲染方案,每次新增更新时会发送大量描述dom结构的数据,与此同时动态递归渲染的耗时也远大于常规的静态模板渲染,使得这两个框架在所有的更新场景下耗时都远大于其他框架。

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

刷新页面后我们使用新增不可用券(1000)创建后台数据,观察该操作是否会触发setData并统计耗时


back add(1000)
native45.2
wepy2174.6
uniapp89.4
mpx0
chameleon142.6
mpvue134
kbone0
taro next0

mpx进行setData优化时inspired by vue,使用了编译时生成的渲染函数跟踪模板数据依赖,在后台数据变更时不会进行setData调用,而kbone和taro next采用了动态渲染技术模拟了web底层环境,在上层完整地运行了vue框架,也达到了同样的效果。

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


1002003004005006007008009001000
native8869.871.280.879.484.489.893.299.6108
wepy2121173.4213.6250298345.6383434.8476.8535.6
uniapp135.4112.4110.6106.4109.6107.2114.4116118.8117.4
mpx112.686.284.686.89087.291.288.892.493.4
chameleon178.4178.2186.4184.6192.6203.8210217.6232.6236.8
mpvue139151173.4194231.4258.8303.4340.4384.6429.4
kbone559.8746.6980.61226.81450.61705.41927.22154.82367.82617
taro next482.6626.2755909.610851233.213841568.61740.61883.8

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


delete(all)add(1000)update(1)update(all)delete(1)
native43.4299.889.28987.2
wepy243.2762.450533522.4
uniapp57.8589.862.6160.6154.4
mpx45.8490.852.8167166
chameleon93.8837184.6318220.8
mpvue124.8696.2423.4419430.6
kbone121.44978.22331.22448.42348
taro next129.83947.21610.41813.82290.2

该项测试的结论为:
native > mpx > uniapp > chameleon > mpvue > wepy2 > taro next > kbone

结论分析:

  • 具备模板数据跟踪能力的三个框架mpx,kbone和taro next在有后台数据场景下耗时并没有显著增加;

  • wepy2当中的diff精度不足,耗时也没有产生明显变化;

  • 其余框架由于每次更新都会对后台数据进行deep diff,耗时都产生了一定提升。

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

由于mpvue和taro next的渲染全部在页面中进行,而kbone的渲染方案会额外新增大量的自定义组件,这三个框架都会在优惠券数量达到2000时崩溃白屏,我们排除了这三个框架对其余框架进行大数据量场景下的页面更新耗时测试

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


10002000300040005000
native332.6350412.6498.2569.4
wepy2970.21531.42015.22890.63364.2
uniapp655.2593.4655675.6718.8
mpx532.2496548.6564601.8
chameleon805.4839.6952.81086.61291.8

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


back add(5000)
native117.4
wepy2511.6
uniapp285
mpx0
chameleon824

10002000300040005000
native349.8348.4430.4497594.8
wepy2112818722470.43263.44075.8
uniapp715666.8709.2755.6810.2
mpx538.8501.8562.6573.6595.2
chameleon1509.21672.41951.82232.42586.2

该项测试的结论为:
native > mpx > uniapp > chameleon > wepy2

结论分析:

  • 在大数据量场景下,框架之间基础性能的差异会变得更加明显,mpx和uniapp依然保持了接近原生的良好性能表现,而chameleon和wepy2则产生了比较显著的性能劣化。

局部更新耗时

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


toggleSelect(ms)
native2
wepy22.6
uniapp2.8
mpx2.2
chameleon2
mpvue289.6
kbone2440.8
taro next1975

该项测试的结论为:
native ≈ chameleon ≈ mpx ≈ wepy2 ≈ uniapp > mpvue > taro next > kbone

结论分析:

  • 可以看出所有使用了原生自定义组件进行组件化实现的框架局部更新耗时都极低,这足以证明小程序原生自定义组件的优秀性和重要性;

  • mpvue由于使用了页面更新,局部更新耗时显著增加;

  • kbone和taro next由于递归动态渲染的性能开销巨大,导致局部更新耗时同样巨大。

setData调用

我们将proxySetData的count和size选项设置为true,开启setData的次数和体积统计,重新构建后按照以下流程执行系列操作,并统计setData的调用次数和发送数据的体积。

操作流程如下:

  1. 100逐级递增可用券(0->500)

  2. 切换至不可用券

  3. 新增不可用券(1000)

  4. 100逐级递增可用券(500->1000)

  5. 更新可用券(all)

  6. 切换至可用券

操作完成后我们使用getCountgetSize方法获取累积的setData调用次数和数据体积,其中数据体积计算方式为JSON.stringify后按照utf-8编码方式进行体积计算,统计结果为:


countsize(KB)
native14803
wepy235141124
mpvue162127
uniapp14274
mpx8261
chameleon2515319
kbone2210572
taro next92321

该项测试的结论为:
mpx > uniapp > native > chameleon > wepy2 > taro next > mpvue > kbone

结论分析:

  • mpx框架成功实现了理论上setData的最优;

  • uniapp由于缺失模板追踪能力紧随其后;

  • chameleon由于组件每次创建时都会进行一次不必要的setData,产生了大量无效setData调用,但是数据的发送本身经过diff,在数据发送量上表现不错;

  • wepy2的组件会在数据更新时调用setData发送已经更新过的props数据,因此也产生了大量无效调用,且diff精度不足,发送的数据量也较大;

  • taro next由于上层完全基于vue,在数据发送次数上控制到了9次,但由于需要发送大量的dom描述信息,数据发送量较大;

  • mpvue由于使用较长的数据路径描述数据对应的组件,也产生了较大的数据发送量;

  • kbone对于setData的调用控制得不是很好,在上层运行vue的情况依然进行了22次数据发送,且发送的数据量巨大,在此流程中达到了惊人的10MB。

页面渲染耗时(静态测试)

此处的页面渲染耗时与前面描述的动态测试场景中相同,测试结果如下:


页面渲染耗时
native70.4
wepy286.6
mpvue115.2
uniapp69.6
mpx66.6
chameleon65
kbone144.2
taro next119.8

该项测试的结论为:
chameleon ≈ mpx ≈ uniapp ≈ native > wepy2 > mpvue ≈ taro next > kbone

结论分析:

  • 除了kbone和taro next采用动态渲染耗时增加,mpvue使用页面模板渲染性能稍差,其余框架的静态页面渲染表现都和原生差不多。

结论

综合上述测试数据,我们得到最终的小程序框架运行时性能排名为:
mpx > uniapp > chameleon > wepy2 > mpvue > taro next > kbone

一点私货

虽然kbone和taro next采用了动态渲染技术在性能表现上并不尽如人意,但是我依然认为这是很棒的技术方案。虽然本文从头到位都在进行性能测试和对比,但性能并不是框架的全部,开发效率和高可用性仍然是框架的重心,开发效率相信是所有框架设计的初衷,但是高可用性却在很大程度被忽视。从这个角度来说,kbone和taro next是非常成功的,不同于过去的转译思路,这种从抹平底层渲染环境的做法能够使上层web框架完整运行,在框架可用性上带来非常大的提升,非常适合于运营类简单小程序的迁移和开发。

我主导开发的mpx框架 https://github.com/didi/mpx 选择了另一条道路解决可用性问题,那就是基于小程序原生语法能力进行增强,这样既能避免转译web框架时带来的不确定性和不稳定性,同时也能带来非常接近于原生的性能表现,对于复杂业务小程序的开发者来说,非常推荐使用。在跨端输出方面,mpx目前能够完善支持业内全部小程序平台和web平台的同构输出,滴滴内部最重要最复杂的小程序——滴滴出行小程序完全基于mpx进行开发,并利用框架提供的跨端能力对微信和支付宝入口进行同步业务迭代,大大提升了业务开发效率。

点赞是最大的支持 

这篇关于寻找性能之王 - 七大热门小程序框架豪横评的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/501283

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听

黑神话,XSKY 星飞全闪单卷性能突破310万

当下,云计算仍然是企业主要的基础架构,随着关键业务的逐步虚拟化和云化,对于块存储的性能要求也日益提高。企业对于低延迟、高稳定性的存储解决方案的需求日益迫切。为了满足这些日益增长的 IO 密集型应用场景,众多云服务提供商正在不断推陈出新,推出具有更低时延和更高 IOPS 性能的云硬盘产品。 8 月 22 日 2024 DTCC 大会上(第十五届中国数据库技术大会),XSKY星辰天合正式公布了基于星

AI Toolkit + H100 GPU,一小时内微调最新热门文生图模型 FLUX

上个月,FLUX 席卷了互联网,这并非没有原因。他们声称优于 DALLE 3、Ideogram 和 Stable Diffusion 3 等模型,而这一点已被证明是有依据的。随着越来越多的流行图像生成工具(如 Stable Diffusion Web UI Forge 和 ComyUI)开始支持这些模型,FLUX 在 Stable Diffusion 领域的扩展将会持续下去。 自 FLU

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

EMLOG程序单页友链和标签增加美化

单页友联效果图: 标签页面效果图: 源码介绍 EMLOG单页友情链接和TAG标签,友链单页文件代码main{width: 58%;是设置宽度 自己把设置成与您的网站宽度一样,如果自适应就填写100%,TAG文件不用修改 安装方法:把Links.php和tag.php上传到网站根目录即可,访问 域名/Links.php、域名/tag.php 所有模板适用,代码就不粘贴出来,已经打

跨系统环境下LabVIEW程序稳定运行

在LabVIEW开发中,不同电脑的配置和操作系统(如Win11与Win7)可能对程序的稳定运行产生影响。为了确保程序在不同平台上都能正常且稳定运行,需要从兼容性、驱动、以及性能优化等多个方面入手。本文将详细介绍如何在不同系统环境下,使LabVIEW开发的程序保持稳定运行的有效策略。 LabVIEW版本兼容性 LabVIEW各版本对不同操作系统的支持存在差异。因此,在开发程序时,尽量使用