on 2020-05-21

前言

在後台系統會常遇到的問題就是要呈現大量的資料(表單)

通常在這情況下會有兩種方式呈現資料

  1. 分頁

  2. 無限滾動

但是這都要在 model 額外做不少工

例如: 處理分頁, 處理排序, 處理搜尋

這些都是要再花時間處理的

這邊將會介紹一下如何在不太改動 model 的情況下提升前端效能

當然在真的是非常的大量的資料時還是真的要在 model 提供參數去 query DB

現況

因為新的需求需要添加到不少的資料

但是因為時程短加上再添加的資料量也不會大的誇張(約共 2000 左右的量

所以還是一次全部載入

問題點

首先會先遇到的問題就是

一次載入 2000 多筆資料頁面就開始變很慢(因為還有圖片)

打開 chrome developtool 就會發現 DOMContentLoaded 要到 13s 左右

慢到爆炸

這 13s 還不包含之後要執行的 JavaScript 與畫面渲染

因為還有圖片

所以 2000 多個圖片載完後

整個頁面花了 1 分鐘多…

screenshot image

解決方案

先把圖片改成 lazyload

從瀏覽器一次載入 2000 多筆資料瞬間變成只載入頁面會呈現的圖片

但是 DOMContentLoaded 還是要 12s 左右啊~~

screenshot image

解決 DOMContentLoaded 問題

因為後台的資料表單是 SSR(server side render) 從 PHP 噴的

所以一定會有個問題就是 2000 多筆的 table HTML DOM 很多

然後因為是從 server side 噴到瀏覽器上

所以會有一個卡的點就是 DB query 的效能(這部分已經優化過了, 在這次的案例是一次全撈出來是要優化什麼啊)

那只要 DB query 卡到的話

就會延遲頁面的呈現

因為 server side 是把所有的 response content 都好了才會噴回去瀏覽器

當然在 PHP 可以用 ob_flushflush 來處理

Refer - 強迫 PHP 將 Buffer 的資料提前輸出

Refer - PHP flush()與ob_flush()的區別詳解

但是目前沒有想要這樣做

因為目前的程式結構有點複雜, 所以就沒有用這方法實作

所以最後是決定把這一頁改成 CSR(client side render)

用 AJAX 拉資料下來

JavaScript 優化

因為是後台的操作會有 CRUD 的行為, 原本這些行為就都是 AJAX 處理

所以在 JavaScript 有很多操作行為的事件綁定(event binding)

所以就把這些行為改成事件委派(event delegation)

關於分頁, 排序和搜尋的處理

資料量變多後 PM 最喜歡說我希望有搜尋和排序這樣比較方便看資料誒

但是我們已經把所以資料經由 AJAX 取回到瀏覽器上了

這時就可以用一個 jQuery 超好用的 plugin DataTables

這 plugin 可以把 client 的 data 重新處理成具有分頁排序和搜尋的 table

也支援把已經在頁面上的 table 或 AJAX response 或是在 JavaScript 中的 data 重新處理

結論

當上述的東西處理完後

screenshot image

DOMContentLoaded 因為改成 AJAX 所以會少很多(12s -> 2s)

因為資料改成 AJAX 產生, 這速度比 SSR 快多了

再不看畫面 render 的情況下整個 loading 速度已經也提高很多

至於 render 時間不太好測試比較所以這邊就不比較了

基本上上述這些只會動用到前端的程式即可達到成效不錯的優化

也可以在 chrome 裝上 lighthouse 來檢測

因為光是這樣改在體感上就提升許多了, 也就沒有再用 lighthouse 測試

額外補充

測試 JavaScript 效能

可以用 runtime 來測試

var startTime = new Date().getTime();

var endTime = new Date().getTime();
console.log('testName: ' + (endTime - startTime) + 'ms');

DataTables AJAX 處理

Refer - ajax

Refer - columns.data

Refer - columns.render

Refer - [jQuery] jQuery DataTables 資料行加入checkbox、按鈕,關閉資料行排序、改變下拉選單的每頁顯示幾筆、變更DataTables樣式…等功能 Part5 final

套用 DataTables 後的 image lazyload 處理

因為現在使用的 image lazyload 會是觀察當前畫面的圖片後在確認這些圖片是否在可視範圍才會加載

所以當有分頁時

因為換分頁時會是新的 table DOM(table DOM 會重建) 所以換分頁後的圖片是之前沒有被觀察的

所以換分頁後得再重新觀察當前頁面的圖片

DataTables 有 page 的 event 可以監聽分頁切換

Refer - page

但是當使用 搜尋 , 排序等會造成畫面重新出現的情況的話

要有效地處理就是使用 datatable 的 draw event

Refer - page

只要表單重繪就會觸發

這邊圖片延遲載入是使用自己參照 Google 的開發文件寫的 code simpleImgLazyLoad

這邊 image lazy 只要圖片載完後 observer 物件就會回收

不過沒有載到的圖片還是會持續觀察

只是應該是不會有太大的影響

DataTables 版面寬度問題

因為 DataTables 處理完後有時會自動加上寬度

所以可以直接先使用 width: 100% 處理

Refer - DataTables example - Flexible table width

因為 datatable 預設的 table 是固定的

如果有 responsive 的需求

要額外用 responsive 的套件來協助

Refer - responsive