從這個博客皮膚邁入前端性能優化一小步

前置

正如你所見,我如今用的這個博客皮膚,在沒優化以前幀率會降到個位數. 如今與之相比,是否是好不少呀? 下面將從滾動 scroll 優化這一方面展開,主要說一下思路.css

只在極少狀況下會降到 30fps,通常穩定在 55-60fpx.css3

頭部導航條

頭部導航條會監聽滾動條上下滾動的方向隨之展開或隱藏.當隱藏時,文章目錄會上移一小段距離並固定;反之,會回到原來的位置.這裏的頭部導航條使用了 css3 transform 屬性, 有時會調動 GPU 來加速, 因此導航條優化主要在於對監聽滾動條事件的處理.canvas

很差的作法性能

頭部導航條和文章目錄各監聽一個事件,分別寫在一個 func 中. 這樣會增長一個事件消耗,且代碼會有冗餘.學習

優化優化

仔細分析, 文章目錄是隨頭部導航條的變化而變化的.徹底能夠作一次事件監聽完成這兩件事,例以下面這樣:動畫

$(window).scroll(function() {
  if (condition) {
    // 顯示導航條
    // 目錄下移
  } else {
    // 隱藏導航條
    // 目錄上移
  }
})

由於代碼量比較大, 當時寫的時候就沒考慮合併, 主要是先把功能作出來.
有了經驗,之後再遇到相似的狀況,就會考慮他們之間的聯繫了, 這裏的重點就是找到這二者之間的聯繫.
可是這樣就完了嗎 ? 有些經驗的, 可能想到防抖(debounce), 但仔細想一想防抖真的夠好嗎?
若是加上防抖, 只會下降在一段時間內事件觸發的頻率.這樣能減小很大的性能開銷,通常遇到監聽 scroll 或者 resize 等事件首先都會這樣想吧.
仔細觀察,就能夠發現: 若是一直向上或向下滾動,導航條和文章目錄都會保持一個狀態, 只有逆向滾動時,它們纔會一塊兒發生一次狀態改變.
這裏有更好的作法:firefox

function scrollFunc() {
  let scrollDirection
  if (!scrollAction) {
    scrollAction = window.pageYOffset
  }
  let diff = scrollAction - window.pageYOffset
  if (diff < 0) {
    scrollDirection = "down"
  } else if (diff > 0) {
    scrollDirection = "up"
  }
  scrollAction = window.pageYOffset
  return scrollDirection
}

let scrollAction, originalDir

$(window).scroll(function() {
  let direction = scrollFunc()
  if (direction && originalDir != direction) {
    if (direction == "down") {
      // 顯示導航條
      // 目錄下移
    } else {
      // 隱藏導航條
      // 目錄上移
    }
    originalDir = direction
  }
})

上面這段代碼,很容易明白.歸納一下: 至關於增長一個作更少計算的中間層,只有符合條件時纔會觸發真正須要執行的操做.
甚至想讓給這個所謂的中間層加上防抖也是能夠的.這樣和原來的對比更加明顯了.code

文章目錄活躍標題樣式

監聽滾動條滾動,若是這個文章標題超出了頂部, 即認爲當前標題下的內容活躍(你正在瀏覽這部分),就會給當前目錄中的標題添加活躍樣式.
很顯然,這裏涉及遍歷操做,因此計算量較大.這裏可使用防抖來優化,可是我使用了節流.使用節流能保證你較快速滾動頁面時,依然能觸發指定頻次的 func,以顯示目錄活躍狀態.
這樣看起來就更平滑.使用防抖則不能,會等你中止滑動瞬間移動給最後一個活躍的標題添加樣式,這樣有明顯的頓感.固然使用防抖也是能夠的.很顯然,這裏使用節流仍然會增長部分開銷.
另外,有更好的方法實現文章目錄,好比使用 canvas 來繪製.我寫的代碼有些糟,能夠另作一番優化.orm

另外補充一個小知識, 如何判斷文章標題是否超出了頂部呢 ? 我是這樣實現的:

getClientRect(element) {
    const {top, bottom, left, right, height, width} = element.getBoundingClientRect()
    return {
        top,
        bottom,
        left,
        right,
        height: height || bottom - top,
        width: width || right - left
    }
}

若是你對 Element.getBoundingClientRect() 沒有了解, 若是有興趣, 我在這裏放了一個連接-MDN, 你能夠跳轉以學習它.

百分比的指示器

例如右下角帶百分比的指示器,經過監聽滾動條位置轉化成百分比,同時改變元素高度, 以控制動畫的高度. 這裏就犯了大忌了, 不斷改變元素高度, 會致使不斷重繪. 這部分使用 requestAnimationFrame 來優化, 雖然幀數提高明顯, 這樣仍然是極很差的作法, 不要在監聽滾動的事件中修改樣式! 找了好久沒找到可以不改變高度就能實現這個效果的方式, 就給這個皮膚添加了一個能夠選擇簡易指示器的選項.

back2top: {
    enable: true,
    type: "complex", // 可選 'simple' 不使用動畫效果
    right: ""
},

這個指示器的配置爲何叫 back2top ? 這是由於若是你將鼠標放上去,它會顯示一個箭頭,點擊能夠回到頂部,它實際上是一個返回頂部的按鈕.
若是你對 window.requestAnimationFrame 沒有了解, 若是有興趣, 我在這裏放了一個連接-MDN, 你能夠跳轉以學習它.

最後

另外, 沒優化以前在firefox上就很流暢 <( ̄▽ ̄)/

博客還有其餘地方的滾動監聽, 思路重複就不一一列舉了.

其實徹底能夠刪去不少滾動監聽事件, 這樣也好, 可以稍微鍛鍊一下本身, 稍微增長這方面經驗.

文章有錯誤不足,敬請指出,謝謝!

相關文章
相關標籤/搜索