動態

詳情 返回 返回

使用惰性函數優化頁面滾動事件的性能 - 動態 詳情

我們在做前端開發的時候,曾遇到一些非常炫酷的宣傳頁。例如每一個蘋果產品的主頁面。我們會發現,這樣炫酷的頁面,總是跟隨我們鼠標滾輪的操作,在頁面中響應不同的事件。


一、瀏覽器的不同

我們都知道,需要在前端頁面中監聽到鼠標滾輪的事件,不同瀏覽器內核提供的方法是不同的。所以,每當我們需要監聽鼠標滾輪事件,就需要先判斷使用終端是用的什麼瀏覽器。

在前端,我們可以通過 window.navigator.userAgent 這個瀏覽器自帶的 API 來判斷瀏覽器類型。其中,ChromeFireFoxEdge 這三個如今最主流的瀏覽器,返回的格式如下:

瀏覽器 userAgent 返回值
Chrome Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
FireFox Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0
Edge Mozilla/5.0 (Windows NT 10.0; Win64; x64; ServiceUI 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134

由於鼠標滾輪事件,只有 FireFox 瀏覽器比較特殊,其它瀏覽器都是一樣的。所以我們只需要判斷是否是 FireFox 瀏覽器即可。

const browser = window.navigator.userAgent; // 獲取瀏覽器信息
const isFirefox = browser.toLowerCase().includes('firefox'); // 判斷是不是 FireFox 瀏覽器

二、監聽滾動事件

在 Chrome 等主流瀏覽器中,我們可以直接通過監聽 mousewheel 事件。其對應事件處理函數中的事件對象 event 包含一個 wheelDelta 屬性。當鼠標滾輪向上滾動的時候,wheelDelta 的值為 120;反之,當鼠標滾輪向下滾動的時候,wheelDelta 的值為 -120

因此,我們可以直接通過判斷 wheelDelta 的值,獲取對應的操作邏輯:

document.addEventListener('mousewheel', function(e) { // 其它瀏覽器使用 mousewheel 監聽
  const ev = e || event;
  ev.wheelDelta > 0 ? console.log('向上滾動') : console.log('向下滾動'); // wheelDelta 為 -120,向下滾動;wheelDelta 為 120,向上滾動
});

在 FireFox 瀏覽器中,我們需要通過監聽 DOMMouseScroll 事件。其對應事件處理函數中的事件對象 event 並不包含 wheelDelta 屬性,而是使用 detail 代替。並且,detail 屬性的值也是和事件反過來的。當鼠標滾輪向上滾動的時候,detail 的值為 -3;反之,當鼠標滾輪向下滾動的時候,detail 的值為 3

因此,在 FireFox 瀏覽器中,我們需要通過判斷 detail 的值來獲取對應的操作邏輯:

document.addEventListener('DOMMouseScroll', function(e) { // FireFox 瀏覽器使用 DOMMouseScroll 監聽
  const ev = e || event;
  ev.detail < 0 ? console.log('向上滾動') : console.log('向下滾動'); // detail 為 3,向下滾動;detail 為 -3,向上滾動
});

三、完整的監聽滾動函數

通過以上兩步,我們可以簡單的將其構建成一個完整的監聽鼠標滾動的通用函數:

function scroll() {
  const browser = window.navigator.userAgent; // 獲取瀏覽器信息
  const isFirefox = browser.toLowerCase().includes('firefox'); // 判斷是不是 FireFox 瀏覽器
  if (isFirefox) {
    document.addEventListener('DOMMouseScroll', function(e) { // FireFox 瀏覽器使用 DOMMouseScroll 監聽
      const ev = e || event;
      ev.detail < 0 ? console.log('向上滾動') : console.log('向下滾動'); // detail 為 3,向下滾動;detail 為 -3,向上滾動
    });
  } else {
    document.addEventListener('mousewheel', function(e) { // 其它瀏覽器使用 mousewheel 監聽
      const ev = e || event;
      ev.wheelDelta > 0 ? console.log('向上滾動') : console.log('向下滾動'); // wheelDelta 為 -120,向下滾動;wheelDelta 為 120,向上滾動
    });
  }
}

一般來説,函數寫成,大功告成。但是咱們不妨思考這樣一個問題:

對於同一個瀏覽器來説,每次進來都要判斷瀏覽器類型,並做出判斷,這樣做是必須的嗎?

比如一個 Chrome 瀏覽器,在第一次訪問的時候,程序需要判斷瀏覽器類型,並根據判斷執行以上程序中 else 的部分。那麼,從第二次開始同一個 Chrome 瀏覽器進行訪問的時候,可以直接執行 else 部分中的代碼即可,何樂而不為呢?

這樣,掌聲有請我們今天的主角 —— 惰性函數


四、惰性函數

通過打印,我們可以看到。以上函數在每一次執行的時候,均會完整執行:

於是,我們巧妙的藉助 JavaScript 中函數聲明的特點,對函數名變量進行重新賦值,就可以巧妙地減少執行函數的大小:

function scroll() {
  const browser = window.navigator.userAgent; // 獲取瀏覽器信息
  const isFirefox = browser.toLowerCase().includes('firefox'); // 判斷是不是 FireFox 瀏覽器
  if (isFirefox) {
    scroll = function () {
      document.addEventListener('DOMMouseScroll', function(e) { // FireFox 瀏覽器使用 DOMMouseScroll 監聽
      const ev = e || event;
      ev.detail < 0 ? console.log('向上滾動') : console.log('向下滾動'); // detail 為 3,向下滾動;detail 為 -3,向上滾動
    });
    }
  } else {
    scroll = function () {
      document.addEventListener('mousewheel', function(e) { // 其它瀏覽器使用 mousewheel 監聽
      const ev = e || event;
      ev.wheelDelta > 0 ? console.log('向上滾動') : console.log('向下滾動'); // wheelDelta 為 -120,向下滾動;wheelDelta 為 120,向上滾動
    });
    }
  }
}

在這裏,我們分別把 if 和 else 中的代碼都放到一個匿名函數中,並且把匿名函數賦值給一個變量。需要注意的是,這個變量並不是我們自己聲明的,而是直接寫了父函數的函數名 scroll

這裏牽扯到 JavaScript 代碼在瀏覽器加載的底層原理,不再過多展開。感興趣的小夥伴們可以關注我以後的文章,我會在不久的將來做更加詳細的講解。

通過打印,我們發現從第二次開始,執行函數 scroll 不再是整個函數,而僅僅是我們需要執行的匿名函數

這樣處理之後,程序的執行部分得到了大大的簡化,也可以提升程序的執行效率。

這樣,在函數體中巧用覆蓋賦值的方式寫的匿名函數方法,我們就叫做惰性函數

關注我,給您更多精彩內容!

Add a new 評論

Some HTML is okay.