加快你的網站響應速度-Vue懶載入

大雄沒了哆啦A夢發表於2019-03-18

關於懶載入

眾所周知,對於頁面內容比較豐富的網站,dom的解析會很複雜,這樣就會導致首屏載入過慢,對於圖片很豐富的網站,我們知道可以使用圖片懶載入來提高網站的響應速度,我在我的另外一篇文章中寫過,有興趣的點選這裡。像淘寶、京東等等的首頁都是經過懶載入處理的,他們會先渲染出骨架,然後懶載入的區域出現在了可視範圍的時候再把骨架替換為真實的內容。

骨架: image

真實內容:
image

這樣就避免首頁一次性載入過多的內容瀏覽器需要渲染過多的dom導致的卡慢, 同時圖片也懶載入了,避免下載圖片導致的dom渲染阻塞,可謂一舉而兩得,那麼在vue中,我們怎麼做懶載入呢?這裡分享下我做的專案當中,利用vue的mixin做的懶載入。

vue mixin

關於vue mixin,我想大家都知道vue mixin的作用,個人覺得它最大的用處就是共享一些資料data和方法methods,從而實現程式碼重用。居於它的作用,我選擇mixin作為實現懶載入的手段,所有需要懶載入的模組,都匯入懶載入所需要的mixin。

如何實現懶載入

我們知道vue的v-if可以決定是否要渲染該元件,所以我們利用v-if來控制懶載入的元件,如果這個元件不在可視區域,則將其設為false,或者讓它渲染骨架,當元件出現在可視區域的時候再去渲染真實的dom。

如何判斷元件出現在可視區域

判斷元件是否出現在可視區域有兩種手段:
一、是利用元素的getBoundingClientRect方法獲取元素所處的位置,然後判斷top屬性是否大於0到window.innerHeight之間,並且需要監聽頁面的scroll事件,不斷進行上述判斷,想要了解更多的,請閱讀我開頭說的圖片懶載入實現方式。
二、利用新特性IntersectionObserver,這個特性不需要我們去監聽滾動事件,只要元素出現在視線,就會觸發可見的事件

if ("IntersectionObserver" in window) {
  let lazyCompObserver = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
      // 元素可見 
      if (entry.intersectionRatio > 0) {
        // TODO 進行一些元素可見後的一些操作,同時該元素不需要被observe了
        lazyCompObserver.unobserve(entry.target)
        
      }
    })
  })
  if(this.$el.nodeType === 1) {
    // 需要observe的元素
    lazyCompObserver.observe(this.$el)
  }
}
複製程式碼

實現

綜上所述,我們實現懶載入的mixin是這樣寫的

// lazy-load mixin
import {throttle, getClientHeight} from 'tool/tool'
export default {
    data() {
        return {
          isShow: false,
          _throttleFn: null
        }
      },
      methods: {
        inViewShow() {
          if(this.isShow) {
            document.removeEventListener('scroll', this._throttleFn, false)
            return
          }
          // 不支援IntersectionObserver api的情況下判斷圖片是否出現在可視區域內
          let { $el } = this
          const rect = $el.getBoundingClientRect()
          // 出現在視野的時候載入元素
          if(0 < rect.top && rect.top < getClientHeight()) {
            this.isShow = true
          }
        }
      },
      mounted() {
        // 支援IntersectionObserver的使用這個api 
        if ("IntersectionObserver" in window) {
          let lazyCompObserver = new IntersectionObserver((entries) => {
            entries.forEach((entry) => {
              if (entry.intersectionRatio > 0) {
                this.isShow = true
                lazyCompObserver.unobserve(entry.target)
                // 當元素可見時,如果需要回撥,則執行
                this.onVisible && this.onVisible()
              }
            })
          })
          if(this.$el.nodeType === 1) {
            lazyCompObserver.observe(this.$el)
          }
        } else {
          // 不支援的使用getBoundingClientRect和scroll來判斷
          this.inViewShow()
          this._throttleFn = throttle(this.inViewShow)
          document.addEventListener('scroll', this._throttleFn, false)
        }
    }
}

// 呼叫
<template>
    <li v-if="isShow">
    .....
    </li>
</template>
<script>
import lazyLoad from 'mixins/lazy-load'
export default {
    mixins: [lazyLoad],
    methods: {
        onVisible() {
            console.log('元素可見了')
        }
    }
}
</script>
複製程式碼

效果就是這樣 image第一個li是可見的,第二個li不可見,所以不渲染。

注意:這裡因為要判斷元素的位置,所以需要用樣式設定li的高度,如果你懶載入的是其他元素,也需要設定高度

有不對或者改正的地方,忘各位留言指正。


相關文章