vueTable大數據展現優化

背景

clipboard.png
大數據項目根據用戶輸入代碼查詢數據,用戶的代碼不可控(好比select from db limit 5000),有可能一頁需求要求展現100行5000列數據。因爲是用戶代碼實時查詢的數據,後端不可能將全部查詢結果都存儲。所以,查詢的結果是實時的、全量的,分頁和排序都須要前端去實現前端

現狀

剛開始的接手項目的時候徹底不能展現十萬級的數量,chrome標籤頁直接崩潰。這個在分析了需求,展現數據不須要響應式後,用了Object.freeze()後就能夠勉強展現十萬級數據。雖然仍是卡頓,可是需求已經實現。(值得注意的是Object.freeze()並非深度凍結,實際應用中對象要進行遞歸操做。)vue

下面圖展現的是100行乘以一千列,在左右拖拽0-150列。目前也對超過兩百列的數據進行橫向的懶加載操做,實現原理時監聽scroll事件滾動到末尾時截取對應下一組數據,而後將滾動條恢復到頭部。能夠從gif中明顯感受到這個過程是滾動條恢復到原狀之間耗時比較長。並且當用戶想要看前一組數據的最後一項和後一組數據的前一項時,js就要不停地作截取數據的操做從新渲染,開銷很是大。git

clipboard.png

利用chrome devtool performance進行性能分析。(進行性能分析時使用隱身模式避免chrome插件對結果分析形成誤差)github

  • 觀察FPS圖表,有幾段紅幀證實這過程當中頁面超負荷,會出現卡頓響應緩慢等。

    clipboard.png

  • 選中紅幀區域,Main區域發生變化,變爲當前選擇時段的函數調用棧詳情。點擊會在下面的Summary裏發現對應的信息以及警告提示迴流可能爲性能的瓶頸Forced reflow is a likely performance bottleneck.。對應的問題出在監聽scroll事件後出現的js代碼中,執行的次數很是多。不只須要去讀取scrollLeft值,還由於從新渲染數據時使組件縱向高度發生了改變,進而屢次觸發了element-ui組件的updateScrollY方法。從Screenshots能夠看到這時恰好是數據移動到最後要對數據進行截取從新渲染。
    clipboard.png
  • 從上圖summary能夠看到js的運行壓力很大,勾選memory項紀錄js heap佔用狀況,查看到佔用高達161mb-325mb
    clipboard.png

優化要點

  • table的數據並不會有修改的須要,僅僅是展現,並不須要響應式。Object.freeze()能夠阻止vue追蹤屬性的變化,減小性能的開銷
  • 因爲數據展現的table不只大量並且常常變換數據集。爲了減小回流和重繪,table作絕對定位脫離文檔流,避免佈局抖動。
  • 因爲後端返回的數據一組表頭和內容分開的數組,而開源element-ui的vue組件都是以key:value的形式,大量數據狀況下僅僅是將數組轉化爲key:value的形式就花費掉幾百毫秒的時間。開源組件能解決的是通用狀況,這種狀況下爲了儘可能減小開銷重寫適用於業務的table組件仍是頗有必要的。chrome

    //後端返回的格式
    data = [
        columnName: ['col1', 'col2', ……],
        columns: [
            ['1', '2', ……],
            ['1', '2', ……],
            ……
        ]
    ]
    // 開源組件須要的格式
    data = [
        {
            col1: '1',
            col2: '2',
            ……
        },
        {
            col1: '1',
            col2: '2',
            ……
        }
    ]
  • 後端返回的數據量有可能高達百萬級,儘管前端進行分頁仍是有可能要展現到數量達十萬。其中行最多每頁只展現100條,可是列由ide用戶執行的代碼決定,這裏主要影響性能的是列數。列數有可能爲1000條,模擬橫向懶加載,將拿回來的數組截取部分展現,減小頁面上的dom節點。可是目前模擬懶加載的方式用戶體驗很差。element-ui

    • 爲了解決橫向滾動時相鄰列的數據可以展現在同一屏上,而不需屢次來回切換,首先作的工做是在截取數據時保留前一屏的數據,拖動後滾動條回到中間位置,在必定範圍內不須要屢次滾動才能查看。(以下圖)

      clipboard.png

- 但這種方式也是很是不友好,每次滾動到最後要去檢測用戶是否按着鼠標有沒有擡起,防止觸發屢次數據從新渲染。由於這種狀況下,用戶拖一次只能加載一組新數據,滾動條便回到了中間位置,若是用戶須要看到最後一組數據就要屢次操做。正常的懶加載應該是有一條適應高度的滾動條拖拽,無縫鏈接。

- 懶加載方式常見的有:
    1. 淘寶一屏用元素佔據必定的高度,而後再去拉圖片數據。滾動條便適應高度的拖動距離。但這種方式仍是須要元素佔位,淘寶一頁的數據量其實不算大,由於它結合了分頁。
    2. 掘金沸點的無限加載:掘金的方式是監聽到底部時,再去拉響應的數據追加,滾動條會自適應滾到相對應的地方。可是掘金這種懶加載一直加載數據沒有截取掉舊數據,因此滾動條距離也是一直適應數據的。嘗試將掘金沸點一直拖動到2000條,網頁已經開始有點卡頓。而在ide項目中,兩千條數據算是少許數據。
- 啓發於[https://github.com/tangbc/vue-virtual-scroll-list](vue-virtual-scroll-list),利用了padding值模擬了淘寶固定高度,不須要元素佔位,模擬出所有數據量的滾動條縱向滾動距離,拖動時徹底無感知數據的從新渲染。目前vue-virtual-scroll-list只支持縱向,但稍微改造下就能用在ide項目的橫向懶加載。(改造後以下圖,gif軟件錄製時稍微有點卡頓感)

clipboard.png

  • scroll長時間運行的從新計算樣式事件,其時間若是超過 16.7 毫秒,而且剛好發生在滾動期間,致使用戶體驗到明顯的抖動。爲了在拖動過程當中數據變化以連貫、平滑進行過渡,函數節流改setTimeout爲requestAnimationFrame(rAF),由系統來決定回調函數的執行時機;它能保證回調函數在屏幕每一次的繪製間隔中只被執行一次,這樣就不會引發丟幀現象,也不會致使渲染數據出現卡頓的問題,而且rAF能兼容到ie9以上了。

優化後結果分析

拖動幾百條數據截取的performance在FPS圖表中已經沒有最初的紅標,沒有Forced reflow,每幀的rendering也由rAF控制在16.7ms之內,js內存佔用也從161mb-325mb,下降到157mb-196mb。後端

clipboard.png

組件接口設計原則

複用性:配置參數的方式去差別化體現,參數的可配置性提升了組件的複用率和靈活性。
可維護性:組件化後,組件內部的邏輯只對組件負責,外部的邏輯只經過配置參數適配,提升了代碼的邏輯清晰度,能夠快速定位代碼出現問題的地方。數組

這個組件設計時對外提供toLefttoRightonScroll事件,分別是滑動過程當中到了頭、尾,及滑動過程的回調。提供了offsetremainbench參數表示剛渲染時的誤差,顯示的列數,及保留多少列在實際dom中。dom

小結

之前沒有想過js也會承受那麼大的壓力,一點點優化都能顯著減輕內存。在寫代碼時要特別關注高頻事件的觸發,一切的優化方向就是在實現功能的前提下減小從新渲染的發生。ide

相關文章
相關標籤/搜索