摘要:不要小看這個優化,由于內部監聽器中的比例很高超過所以在的情況下,可以帶來比較大的提升。但是它的時間復雜度是,在大列表下會帶來顯著的性能開銷甚至完全超過更新的開銷。更多網易技術產品運營經驗分享請訪問網易云社區。文章來源網易云社區
本文由作者鄭海波授權網易云社區發布。
本文旨在用 20% 的精力解決使用Regular過程中 80% 的性能問題.
這里大部分是關于臟檢查的性能優化,不了解的可以先看下《Regular臟檢查介紹》
首先,我們可以用一個簡化后的公式來描述Regular的單次臟檢查的復雜度
N·logN · M · T
其中
N : 代表組件深度
M : 代表組件平均監聽器數量
T : 代表單個Watcher的檢查時間
這樣問題就落在了如何降低這三個因子了
降低N —— 組件層級
這層是收益最高的方案,因為影響因子是 N·logN.
以上圖為例,葉子節點進行$update()時,會首先找到DigestRoot (默認情況下,即頂層使用 new 創建的組件),再層層向下進行組件的$digest()檢查,在目前組件抽象較細致的開發習慣下,很容易產生10多層的組件深度,適當控制下digest深度可以得到可觀的性能提升。
注 : 這個digest flow設計是為了避免產生網狀更新鏈
方案1. 使用isolate 控制digest深度
第一個方式即使用isolate屬性控制組件的數據流向,如 這樣,在第一次初始化后,b組件就不再與a組件有任何數據綁定關系
如圖所示,b組件此時就會成為g組件的實際DigestRoot。b組件內部的$update不會再會冒泡到外層
但這種方式同時讓a的數據變更無法傳達到b組件極其內部,如下圖所示
如果需要實現a->b的單向傳導,可以設置isolate=1
isolate = 1 實際就形成了組件的單向數據流
方案2. 合理抽象組件
除了通過isolate手動控制更新樹的深度之外,我們直接減小組件深度當然也可以。 但這似乎與React等框架推崇的方式相悖,其實不然。
過度抽象的組件,除了引入使用負擔和增加組件層級外,無法帶來直觀的收益。 抽象記得要基于復用的前提,沒有復用前提的組件抽象,除了讓你的文件夾變得更復雜外,毫無益處。 當然它可以給你帶來好看的組件結構圖 :)
降低M: 平均監聽者數量
在Dirty-Check Loop中,在每個組件節點上都會經歷$digest階段: 遍歷監聽者數組,檢查數據是否發生變更。
方案1. 升級到v0.5.2版本以上
首先將上面的公式再簡化,并拓展到 一輪完整的臟檢查Dirty-Check Loop ,可以用下面的公式來表示
K·P·T
其中
K: 臟檢查穩定性檢測輪數 (1~30次不等,30次仍不穩定將拋出錯誤)
P:?digest影響到的所有監聽器
T: 單個監聽器的消耗時間
在Github: 0.5.2版本,有一個優化就是講監聽器分為了 穩定監聽器(stable) 和 不穩定監聽器(unstable)
不穩定的監聽器即具有Side Effect,比如
this.$watch("firstName", (firstName)=>{ this.data.nickname = firstName + "先生"})
當firstName改變時,nickname也會隨之改變,所以為了確保不出錯,框架會檢測多輪直到這類監聽表達式不再變化
穩定的監聽器就是一些沒有Side Effect的監聽比如大部分內置的監聽(文本插值、r-html、屬性插值等), 這類監聽處理邏輯只有讀操作,而沒有寫操作。其實只需要檢測一次即可
這樣公式就修改為了
K·P1·T + P2·T
其中 P1+P2 = P , P2 為Stable監聽器, P1為非穩定。不要小看這個優化,由于內部監聽器中, P2的比例很高(超過80%)所以在K>1的情況下,可以帶來比較大的提升。
除此之外,你同時也可以自己主動來標記哪個監聽器是屬于stable
this.$watch("title", (title)=>{ this.$refs.top.innerText = title
}, {stable: true})
使用一次綁定表達式@(expression)
除非明確了不再對某個監聽感興趣,通過 一次綁定表達式 來提升性能其實并不是特別關鍵,但如果這個表達式正好在一個list循環中,那控制的收益會比較大,比如
{#list list as item by item_index}
如果這個列表有100項,那可以直接減少100個對item.list綁定(何況大部分情況都不止一個屬性傳入), 屬于操作少收益大。
降低T: 單個監聽器的平均消耗時間
其實每個表達式比如user.firstName + "-" + user.lastName 需要判斷變化的開銷各不相同,我們只需要針對高開銷的監聽器進行控制即可達到效果。
盡可能帶上list語句的by描述
list是最容易產生性能瓶頸的部分,下面做下簡單說明
默認情況下,Regular使用的萊文斯坦編輯距離(Levenshtein Distance), 別被嚇到了,實際上wiki百科等資源上都有完成的偽代碼描述, 是個簡單的常用算法。
它的優點是,不需額外標記,就可以找到盡可能少的步驟從一個字符串過渡到另一個(但并不保證相同值一定被保留), 數組同理. 這樣映射到框架內部,就可以以盡可能少的步驟來變更DOM了,相信大家都知道DOM開銷很大了。
但是它的時間復雜度是O(n^2) ,在大列表下會帶來顯著的性能開銷, 甚至完全超過DOM更新的開銷。
所以在Regular v0.3的某個版本引入了by的用法, 例如
{#list items as item by item_index}
{/list}
顧名思義,新舊列表按順序其item_index是不會變化的,即0,1,2... . 所以列表更新時,不會嘗試去銷毀重建,而是直接更新內部的值. 這種更新方式,內部的diff復雜度是 O(n), 屬于極大的優化了性能.而且在DOM更新上比LS算法模式更輕量
這樣用by item_index其實也帶來一個問題,就是雖然循環對應的值改變了,但內部組件是不會重建的,即config、init不會被觸發。
理論上 by 關鍵詞之后可以接任意表達式,但是在之前版本是不生效的 (詳情看#90 regularJS的track by沒起作用) .
這個問題在最新版本已經被修復, 即你可以更精確的控制,是否要復用某一個項對應結構(內部組件是不會重建的,即config、init不會被觸發)
{#list items as item by item.id}
升級到v0.5.2減少銷毀時間
在之前的版本, Regular的模板內容在銷毀時,內部會進行大量的splice操作導致了性能問題,在0.5.2版本進行優化,整體銷毀時間有了 數倍的提升
總結
從操作難易度和關鍵度上,主要是以下建議
升級到Regular最新版本(也方便你使用最新的SSR、跨組件通信等特性),至少也是v0.5.2來整體提高性能(這個版本還做了不少別的性能優化)
list記得使用by語句,特別是by item_index (item_index取決于你的命名)
組件通過isolate來控制digest深度
本文來自于那個2年只更新了0.2個版本的Regular作者之手,請輕噴。
下一篇應該是關于跨組件通信的文章。
更多網易技術、產品、運營經驗分享請訪問網易云社區。
文章來源: 網易云社區
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/25332.html
摘要:以上面的例子為代表完整生命周期如下下一篇,應該會以為例,介紹一種基于來解決跨組件的數據通信的方式免費領取驗證碼內容安全短信發送直播點播體驗包及云服務器等套餐更多網易技術產品運營經驗分享請訪問網易云社區。文章來源網易云社區 本文由作者鄭海波授權網易云社區發布。 背景在組件化不斷深入的大環境下,無論使用哪種 MDV 框架都最終會遇到一個頭疼的問題,就是「跨組件通信」。 下圖是個簡單的例子 ...
摘要:作為開發年的開發者該如何系統的提升自己如今,國內移動互聯網的紅利期已過,開發工程師也從最初的一人難求,到現在的一個崗位百人競爭,僧多粥少的情況直接導致整個行業對求職者的要求越來越高。另外,開發越來越規范,間接導致項目對質量要求的提升。 ...
摘要:順便一說,這首歌的原唱是秋田,中島當年嗓子壞了,才有這歌。中文是直接翻譯來的,作曲是秋田。一部電影春夏秋冬又一春春夏秋冬又一春是由金基德執導,金英民吳英秀金基德主演的一部韓國電影。年月日于韓國上映。 原鏈接: http://bluezhan.me/weekly/#/9-2 1、web前端 Angular vs. React vs. Vue: A 2017 comparison 9 S...
閱讀 2752·2021-10-26 09:50
閱讀 2396·2021-10-11 11:08
閱讀 2135·2019-08-30 15:53
閱讀 1913·2019-08-30 15:44
閱讀 2389·2019-08-28 18:12
閱讀 2528·2019-08-26 13:59
閱讀 2860·2019-08-26 12:19
閱讀 2759·2019-08-26 12:09