天天看點

我發現了 Vue.js 中的性能陷阱

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

我發現了 Vue.js 中的性能陷阱

我内心深處對遊戲的熱愛,讓我一直渴望能自己制作一些電子遊戲。幾個月前我開始将這種夢想變為現實,并第一次參加了全球遊戲大賽(Global Game Jam)。我和我的團隊使用 Vue.js 建構了一個名為“ ZeroDaysLeft ”的遊戲,其形式是 Web 端的單頁面應用程式。這款遊戲的主題是環境保護,我們考慮到商業活動對地球環境的影響,希望就這個話題做一些有益的探讨。使用 Vue.js 制作的遊戲并不多。我的團隊遲到了一天,然後用猜拳的方式選擇了我們要用的架構;我們飛快地寫完了代碼,并在周末結束時做出了遊戲的可運作版本。在本地測試時一切都很順利。自然,我們為自己第一次寫出來的遊戲作品感到自豪,并希望與世界分享它。

可是問題出現了——當我們建構好應用并開始查詢域時,記憶體占用爆表了。它幾乎沒法正常運作,不管換什麼機器都會卡住不動,即使在強大的基于 Intel i7 處理器的系統上程式也會崩潰。遊戲大賽的時間限制把我們拉回了現實,我們決定擱置生産性能問題,這樣起碼我們能做出一款能在自己的裝置上運作的完整遊戲。就像大部分的“已完成”項目一樣,第二天我們就把它抛在腦後了。

但我自己沒法釋懷。它一直困擾着我。問題是出在 Vue.js 上嗎?是 Netlify 嗎?還是因為我們的取巧代碼?我必須找出答案。

調查性能下降的原因

我首先使用 Lighthouse 進行了快速測試。所幸 Firefox 為此提供了一個浏覽器插件。下面就是我得到的結果。

我發現了 Vue.js 中的性能陷阱

89%的數字挺不錯的。實際上,與許多流行的網站相比,這個表現相當出色。這個測試指出了一些潛在問題,例如速度指數和第一次有意義且有内容的繪制步驟等。從理論上講,解決這些問題會進一步提高分數,但不一定能解決應用面臨的嚴重性能問題。

我們的遊戲中有一些圖像和音頻素材資源,但是兩者都不至于讓遊戲卡死在那裡。我們也可以對這些已經優化過的資源再過度優化一遍,但這可能根本就無濟于事。

這個測試無法讓我們真正找出可能導緻這一性能問題的原因。于是我開始想:“該不會是 Vue 的問題吧?”這種想法會冒出來也沒什麼理由,但要是不檢查一下就是蠢了。我檢查了已部署站點的控制台,結果空白一片。但警告往往不會在生産中顯示。當我在本地進行相同操作時,一堆 Vue 警告讓我吃了一驚。

我發現了 Vue.js 中的性能陷阱

像大多數開發人員一樣,我對控制台警告沒那麼在意,覺得它們隻是警告,而不代表錯誤;是以我一般會把注意力集中在其他地方。或許消除這些警告可以解決我的生産問題,我決定深入研究每個問題并修複它們。

所有這些警告均來自我建立的、用來顯示名為 Cards.vue 選項的元件,是以這個元件可能需要大量重寫。

我決定按順序解決這些控制台警告。

> [Vue warn]: Avoid using non-primitive value as key, use string/number value instead.
 found in 
 ---> <Cards> at src/components/Cards.vue           

Vue.js 有很多指令,讓我們能更直覺地使用架構,比如說 v-for 就可以快速将數組渲染為清單。使用它時,我們需要一個 :key 才能有效地重渲染元件。但我們将一個對象用作了一個鍵,這是非原始值,是以導緻了這個錯誤。我決定将 index.description 用作一個新鍵,因為它是一個字元串,并且在值發生更改時可以更好地重新渲染。

> [Vue warn]: Duplicate keys detected: '[object Object]'. This may cause an update error.
found in
---> <Cards> at src/components/Cards.vue           

将 :key 更改為一個字元串(index.description)來解決上一個錯誤,就能解決這個重複鍵的錯誤。我們隻能将字元串類型寫入 DOM,是以當我們傳遞一個要渲染的對象時,該對象将轉換為等效的字元串(即 [object Object]);并且因為這以前是我們的鍵,是以每個對象都将轉換為 [object Object](除非對象有不同的值),進而會出現重複鍵警告。現在既然鍵不是對象,警告就會消失,效率也會提升。

> [Vue warn]: You may have an infinite update loop in a component render function.
found in
---> <Cards> at src/components/Cards.vue           

就一個非常模糊的警告來說,這個警告似乎是最重要的:無限循環意味着記憶體消耗。這條消息并沒有告訴我們可能出了什麼問題,但它确實暗示了問題與元件中的 render 函數有關。也許是因為我們寫的代碼比較取巧,是以觸發了不間斷的更新,并占用了大量的計算能力,以至于使浏覽器和裝置崩潰。

這條警告至少告訴我們要檢查 Cards.vue,是以我的第一個想法是檢查元件中的反應屬性,因為這可能會導緻錯誤。反應屬性在更改後會觸發重新渲染。

我們正在顯示 index.days 和 index.description 中的資料。但我們不會更改這些資料,我們從 cardInfo 數組獲得 index。

我發現了 Vue.js 中的性能陷阱

我們使用這段代碼對數組中的元素進行随機排序,然後将前四個元素顯示為玩家選擇的選項。當使用者單擊一個選項時将調用 effects() 函數,它除了會計算一個動作如何影響遊戲狀态外,還使用 cardInfo 上的拼接原型删除前四個元素。

在 Vue 這種使用虛拟 DOM 的架構裡,用上諸如 cardInfo 之類的反應屬性後,每當資料屬性的值更改時都會觸發重新渲染。在我們的應用裡,我們會直接使用 sort() 原型來更改它,然後删除元素來重新排序。所有這些都會觸發“無限”的重新渲染,進而引發警告。

我決定更改資料過濾的邏輯,并停止對反應屬性 cardInfo 的多次更改。我安裝了 lodash.shuffle 并定義了一個計算屬性 shuffledList(),它将建立一個名為 list 的 cardInfo 副本。我對其應用了随機排序操作,并傳回了一個“frozen”結果,然後拆分開來顯示四張卡片。我們使用了 Object.freeze(),它将使我們傳回的對象不可變,進而完全停止了所有重新渲染操作。

至此,問題解決了。

掉進架構的坑

老實說,當我剛開始調查性能下降原因的時候,還覺得我肯定要優化很多資源才能解決問題。最後這個結果說明,在使用許多架構抽象時我們都必須非常小心——特别是在 Vue 中更是如此,隻有在必要時才使用某條指令,而且用法一定不能出錯,因為它們絕對有自己的代價。

這還讓我開始思考自己做過的其他工作,其中應用程式可能會因為架構而出現不必要的性能問題。大多數現代的前端架構都有很多抽象,使我們能更輕松地為 Web 制作應用程式。但我們應該牢記一點,那就是使用這些東西可能會引發潛在的性能問題。

我經常使用 Vue.js,是以決定探索一些我以前用過的指令,以前我用這些指令的時候完全沒考慮過它們可能對應用程式帶來的性能影響。其中有三條非常流行的指令進入了我的視線。

  • v-if 和 v-show

這兩條指令都是用來有條件地渲染元素的,但是它們背後的工作機制卻大不相同,是以用法也大相徑庭。v-if 一開始不會渲染元件,而隻在條件為真時才渲染元件。這意味着當你多次切換元件的可見性時,就會不斷重新渲染。如果你要多次更改元件的可見性,那就不要使用這個功能。這會影響你的性能。

v-show 是一個很好的替代品。不管你是否啟用 CSS 都會渲染你的元件,但是隻會根據條件是 true 還是 false 來決定元件是否可見。這種方法确實有其缺點,因為它不會将非必要元件的渲染推遲到你需要它們在螢幕上實際出現的時候。如果你的初始渲染沒那麼複雜,那麼它就很合适。

  • v-for

這條指令通常用來從數組中渲染清單。它有一個特殊的文法,形式為 item in list,其中 list 是源資料數組,而 item 是要疊代的數組元素的别名。預設情況下,Vue 在源資料數組上添加 watchers,每當發生更改時它就會觸發重新渲染。這種持續的重新渲染可能會對應用程式性能産生不利影響。如果你隻想可視化對象,那麼 Object.freeze() 是一個很好的解決方案,可以大大提高性能。但是請務必記住,你将無法更新元件或編輯對象資料。

在這個研究過程中我還意識到,Lighthouse 可能檢查的是以更直接的方式影響使用者體驗的應用性能名額,是以接下來我的疑問就是如何跟蹤伺服器上的應用程式性能。

我們是不是太依賴直覺,是不是在假設開發人員知道自己在做什麼,假設他們遵循的是最佳實踐?不管怎樣,這次經曆讓我對單頁應用程式的性能産生了不同的看法。大家可以在 GitHub 上檢視上述項目的存儲庫,也歡迎大家在 Twitter 上和我打招呼。

【雲栖号線上課堂】每天都有産品技術專家分享!

課程位址:

https://yqh.aliyun.com/zhibo

立即加入社群,與專家面對面,及時了解課程最新動态!

【雲栖号線上課堂 社群】

https://c.tb.cn/F3.Z8gvnK

原文釋出時間:2020-04-01

本文作者:Daniel Madalitso Phiri

本文來自:“

InfoQ

”,了解相關資訊可以關注“

繼續閱讀