一次網站性能優化經歷

六月份開始搭建的自己的第一個網站:書蟲小說

在經歷前端頁面重寫後,網站首屏加載速度有了一定提升,之後發現小說目錄詳情頁白屏時間竟然高達1-2s,拖了一段時間,打算解決這個問題,我用Chrome調試抓包發現json數據包大小竟然有200多KB,測試了一下加載時間大概在700ms-1500ms,由於章節列表的生成依賴於這個數據,所以自然會產生很長一段時間的白屏,我打算先解決這個問題

server傳數據是支持gzip壓縮的,例如nginx發送html文檔給客戶端,會自動開啓gzip壓縮。至於一些其他格式的數據比如json需要手動開啓,修改api網站nginx配置文件如下:

gzip             on;

# 設置最小壓縮大小,防止出現越壓越大的情況
gzip_min_length  1k; 

# 代表壓縮級別,1-9可選,9最高同時也最耗cpu(但是好像沒關係目測了下我CPU很佛系,平時佔用超低)
gzip_comp_level  9; 

gzip_proxied     expired no-cache no-store private auth;

# 代表要壓縮的數據類型
gzip_types       text/plain application/x-javascript text/css

application/xml application/javascript application/json; 

經過這個設置後,json數據從200+KB降到了30+KB,加載時間從原來的700+ms
降到了200+ms, 白屏時間至少減少了1s,可是依然存在。

這時我想到我要加載的是一個長列表,很多小說的章節數高達1000+,有一部《校花的貼身高手》更是高達8000+章,要一次渲染一個這麼長的列表是相當耗費性能的,所以決定採用懶加載方案。

方案詳情:

1. 數據還是一次性請求,因爲數據延遲時間已經可以接受了,分多次請求的反而還增加了服務器壓力(重新請求又要建立TCP連接,又要進行三次握手,blablabla...)。
2. 初次載入時只加載部分章節,直到用戶滑到網頁文檔底部時開始插入數據,由於是加載已經緩存在內存中的數據,所以幾乎不會有延遲,只是會因重新插入節點頁面迴流。

如何判斷一個元素是否滾動到底?

var element = document.documentElement //這裏拿document來判斷
element.scrollHeight - element.scrollTop === element.clientHeight

幾個屬性及其含義:

  • scrollHeight

表示的是元素的內容高度,包括了視區不可見的內容

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-07mEcw4a-1572932371223)(https://developer.mozilla.org/@api/deki/files/840/=ScrollHeight.png)]

  • scrollTop

一個元素的 scrollTop 值是這個元素的頂部到視口可見內容(的頂部)的距離的度量。當一個元素的內容沒有產生垂直方向的滾動條,那麼它的 scrollTop 值爲0。

如圖:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-zEYuzPCe-1572932371227)(https://developer.mozilla.org/@api/deki/files/842/=ScrollTop.png)]

  • clientHeight

元素在出現在視區內的高度

有了這些概念,所以當一個滾動元素滑倒底部時,那麼element.scrollHeight - element.scrollTop 應該等於 element.clientHeight

我的做法是給documentElement添加了scroll事件處理器,監聽滾動事件,由於頻繁獲取以上這幾個屬性又挺消耗性能,我給回調函數設置了節流

scrollWatcher() { // method
  throttle(() => {
    let el = document.documentElement;
    //這裏再減300是爲了預先加載,如果等到真的滑倒底部再加載時機有點晚
    let flag = el.scrollHeight - el.scrollTop - 300 <= el.clientHeight;
    if (flag) {
      // console.log(flag)
      this.offset += 1;
    }
  }, 400)();
}

chapters() { // computed

  //this.getCatalog.chapters存放的是全部的數據,
  //每一次快要滑倒底部事件的觸發,就讓offset + 1,
  //利用數組的切片方法不斷更新這個區間,直到全部拿完,
  //也就是所有章節在頁面加載完畢,移除這個事件監聽器
  
  const chapters = this.getCatalog.chapters.slice(0, this.offset * 60 + 60);
  if (this.chaptersLen === chapters.length) {
    document.removeEventListener('scroll', this.scrollWatcher);
  }
  return chapters;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章