本文由作者鄭海波授權網易雲社群發布。
本文旨在用 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)=>)
當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)=>, )
使用一次繫結表示式@(expression)
除非明確了不再對某個監聽感興趣,通過 一次繫結表示式 來提公升效能其實並不是特別關鍵,但如果這個表示式正好在乙個list迴圈中,那控制的收益會比較大,比如
如果這個列表有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的用法, 例如
顧名思義,新舊列表按順序其item_index是不會變化的,即0,1,2... . 所以列表更新時,不會嘗試去銷毀重建,而是直接更新內部的值. 這種更新方式,內部的diff複雜度是 o(n), 屬於極大的優化了效能.而且在dom更新上比ls演算法模式更輕量
這樣用by item_index其實也帶來乙個問題,就是雖然迴圈對應的值改變了,但內部元件是不會重建的,即config、init不會被觸發。
這個問題在最新版本已經被修復, 即你可以更精確的控制,是否要復用某乙個項對應結構(內部元件是不會重建的,即config、init不會被觸發)
舉個例子,只要item.id ===0的項還在,那對應的dom結構就確保不會被**,只會進行更新操作. 這裡的時間複雜度也是o(n), 但實際開銷會比by item_index高不少。
公升級到v0.5.2減少銷毀時間
在之前的版本, regular的模板內容在銷毀時,內部會進行大量的splice操作導致了效能問題,在0.5.2版本進行優化,整體銷毀時間有了 數倍的提公升
總結從操作難易度和關鍵度上,主要是以下建議
公升級到regular最新版本(也方便你使用最新的ssr、跨元件通訊等特性),至少也是v0.5.2來整體提高效能(這個版本還做了不少別的效能優化)
list記得使用by語句,特別是by item_index (item_index取決於你的命名)
元件通過isolate來控制digest深度
Regular高階 幾點效能優化的建議
本文由作者鄭海波授權網易雲社群發布。本文旨在用 20 的精力解決使用regular過程中 80 的效能問題.這裡大部分是關於髒檢查的效能優化,不了解的可以先看下 regular髒檢查介紹 首先,我們可以用乙個簡化後的公式來描述regular的單次髒檢查的複雜度 n logn m t 其中這樣問題就落...
Regular高階 跨元件通訊
本文由作者鄭海波授權網易雲社群發布。背景在元件化不斷深入的大環境下,無論使用哪種 mdv 框架都最終會遇到乙個頭疼的問題,就是 跨元件通訊 下圖是個簡單的例子 這裡包含 事件通訊 和 資料通訊 兩個維度。事件傳遞 為了將事件 click 從 傳遞到最外層元件,需要依次通過 和 等可能本不關心這個事件...
mysql 效能優化的幾點建議
1 盡量取出自己想要的字段,不要這樣select from table 因為你取的越多,網路傳輸的資料就越多,從網路頻寬和網路緩衝區上來看都是浪費。特別是在order,效能更是下降。實現方式是先將需要排序的字段和可以直接定位到相關行資料的指標資訊取 出,然後在我們所設定的排序區 通過引數sort b...