用戶
 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

掃一掃,登錄網站

小程序社區 首頁 工具/框架 查看內容

小程序框架運行時性能大測評

Rolan 2020-4-3 00:32

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

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

隨著小程序在商業上的巨大成功,小程序開發在國內前端領域越來越受到重視,為了方便廣大開發者更好地進行小程序開發,各類小程序框架也層出不窮,呈現出百花齊放的態勢。但是到目前為止,業內一直沒有出現一份全面、詳細、客觀、公正的小程序框架測評報告,為小程序開發者在技術選型時提供參考。于是我便籌劃推出一系列文章,對業內流行的小程序框架進行一次全方位的、客觀公正的測評,本文是系列文章的第一篇——運行時性能篇。

在本文中,我們會對下列框架進行運行時性能測試(排名不分先后):

其中對于kbone和taro next均以vue作為業務框架進行測試。

運行時性能的測試內容包括以下幾個維度:

  • 框架運行時體積
  • 頁面渲染耗時
  • 頁面更新耗時
  • 局部更新耗時
  • setData調用次數
  • setData發送數據大小

框架性能測試demo全部存放于 https://github.com/hiyuki/mp-... 中,歡迎廣大開發者進行驗證糾錯及補全;

測試方案

為了使測試結果真實有效,我基于常見的業務場景構建了兩種測試場景,分別是動態測試場景和靜態測試場景。

動態測試場景

動態測試中,視圖基于數據動態渲染,靜態節點較少,視圖更新耗時和setData調用情況是該測試場景中的主要測試點。

動態測試demo模擬了實際業務中常見的長列表+多tab場景,該demo中存在兩份優惠券列表數據,一份為可用券數據,另一份為不可用券數據,其中同一時刻視圖中只會渲染展示其中一份數據,可以在上方的操作區模擬對列表數據的各種操作及視圖展示切換(切tab)。

動態測試demo

在動態測試中,我在外部通過函數代理的方式在初始化之前將App、Page和Component構造器進行代理,通過mixin的方式在Page的onLoad和Component的created鉤子中注入setData攔截邏輯,對所有頁面和組件的setData調用進行監聽,并統計小程序的視圖更新耗時及setData調用情況。該測試方式能夠做到對框架代碼的零侵入,能夠跟蹤到小程序全量的setData行為并進行獨立的耗時計算,具有很強的普適性,代碼具體實現可以查看 https://github.com/hiyuki/mp-...

靜態測試場景

靜態測試模擬業務中靜態頁面的場景,如運營活動和文章等頁面,頁面內具備大量的靜態節點,而沒有數據動態渲染,初始ready耗時是該場景下測試的重心。

靜態測試demo使用了我去年發表的一篇技術文章的html代碼進行小程序適配構建,其中包含大量靜態節點及文本內容。

靜態測試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. 切換至可用券

操作完成后我們使用 getCount 和 getSize 方法獲取累積的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進行開發,并利用框架提供的跨端能力對微信和支付寶入口進行同步業務迭代,大大提升了業務開發效率。

鮮花
鮮花
雞蛋
雞蛋
分享至 : QQ空間
收藏
原作者: 董宏平 來自: 掘金
河北20选5大星走势图 世界股票指数 幸运赛车稳赢计划 配资公司 内蒙古快三豹子规律 7乐彩票下载 江苏十一选五走势图一定牛 百度 快乐彩12玩法规则 期货配资案中员工什么罪 安徽快三走势图分布图 湖北新11选5开奖公告 湖北快三走势图表今天 安微体育彩票新11选5 福建福彩快三开奖结果一定牛 上海时时乐彩票分析王 南宁股票配资贴吧 内蒙古快三软件下载