IntersectionObserver應用之懶加載和佈局渲染

  最近在項目中,遇到要對大量圖表數據渲染的狀況。因此當時想着能不能只渲染視圖中可見的圖表呢?而後在網上搜索了一番,查到了這個有趣的知識。css

一、IntersectionObserver

  爲開發者提供了一種能夠異步監聽目標元素與其祖先或視窗(viewport)交叉狀態的手段。祖先元素與視窗(viewport)被稱爲根(root)。html

let io = new IntersectionObserver(callback, options)
`callback` 是當元素的可見性變化時候的回調函數,`options`是一些配置項(可選)。
複製代碼

options 主要有如下一個配置項。vue

const options = {
    root: null,//表示默認爲窗口
    threshold: [0, 0.5, 1],//表示當觀察元素出現0%、50%、100%時候就會觸發回調函數
    rootMargin: '30px 100px 20px'//
}
var io = new IntersectionObserver(callback, options)
複製代碼
  • root
    用於觀察的根元素,默認是瀏覽器的視口,也能夠指定具體元素,指定元素的時候用於觀察的元素必須是指定元素的子元素
  • threshold
    用來指定交叉比例,決定何時觸發回調函數,是一個數組,默認是 [0]
threshold: [0,0.5,1],//表示當觀察元素出現0%、50%、100%時候就會觸發回調函數
複製代碼
  • rootMargin
    用來擴大或者縮小視窗的的大小,使用 css 的定義方法。
rootMargin: '10px 130 100px 50px'
    //表示`top、right、bottom` 和 `left` 的值。
複製代碼

callback在元素的可見性變化時,纔會觸發。
  callback中有一個參數 entries。它是一個IntersectionObserverEntry對象數組。以下圖所示。主要有如下屬性。咱們經常關注的有 isIntersecting(表示當前是否可見)和 target(被觀察的目標元素)以及intersectionRatio(表示元素的可見程度,0表示剛剛可見,1表示徹底可見)。git

二、IntersectionObserver的實例方法

  • disconnect()
    使IntersectionObserver對象中止監聽工做。
  • unobserve()
    使IntersectionObserver中止監聽特定目標元素。
  • observe()
    使IntersectionObserver開始監聽一個目標元素。
  • takeRecords()
    返回全部觀察目標的IntersectionObserverEntry對象數組。

如下是項目中的一部分html代碼,爲了接下來更好的展現說明。github

<grid-layout :layout.sync="layout" :row-height="rowHeight" :is-draggable="draggable" :is-resizable="resizable" :is-mirrored="mirrored" :prevent-collision="preventCollision" :vertical-compact="true" :use-css-transforms="true" :responsive="responsive" @layout-mounted="layoutMountedEvent" @layout-updated="layoutUpdatedEvent">
	<grid-item v-for="item in layout" :key="item.i" :static="item.static" :x="item.x" :y="item.y" :w="item.w" :h="item.h" :i="item.i" class="grid-item" :index="item.i" @resized="resized">
			<Chart :optionData="item.data" :index='item.i' :type="item.displayType" @gridItemAllMounted="gridItemAllMountedEvent" />

	</grid-item>
</grid-layout>
複製代碼

接下來是在項目中用於條件渲染的核心代碼。數組

//表示全部的Chart組件都被渲染好了,此時開始監聽元素
function Observer(){
    const callback = (entries) => {
	entries.forEach(item => {
	    //index是我在目標元素上的一個自定義屬性
		const index = item.target.getAttribute('index');
		if (item.isIntersecting) {
		    //將當前視圖中可見的echart圖進行渲染
			this.$store.commit('initChart', index)
			// this.io.unobserve(item.target); // 中止觀察當前元素 避免不可見時候再次調用callback函數
		} else {
			// 清除不可見的,目的是爲了避免渲染不在視圖中的圖片
			this.$store.commit('clearInvisibleChartInstance', index)
		}
	});
    }
    this.io = new IntersectionObserver(callback);
    const gridItems = document.querySelectorAll('.grid-item');
    gridItems.forEach(item => {
    	this.io.observe(item);
    });
}
複製代碼

在這個項目中主要有如下幾點須要注意。瀏覽器

  • 一、由於這個項目須要根據 grid-layout組件的寬度來均分 gridItem,因此須要等到計算出 grid-layout 時才能對它的子組件進行平均分配。因此有如下的代碼。
//gridLayout組件編譯好後(Mounted()),在上級組件中觸發的監聽函數,此時纔開始根據gridLayout組件均配gridItem的寬度。
    layoutMountedEvent (colNum) {
    	this.colNum = colNum
    	this.createLayoutData(this.colNum)
    }
複製代碼
  • 二、須要等到全部的圖表組件都渲染好後再去對圖表組件進行監聽。因此有如下的代碼。具體如何實現能夠個人github中看詳細的代碼實現。
//全部的`echart組價渲染完畢後觸發`
    gridItemAllMountedEvent () {
    	this.startObserver()
    },
複製代碼
  • 三、在元素可見時要對該元素實時監聽,以便在窗口變化時作到局部渲染(條件渲染)。具體代碼以下如示。主要是用到了 isIntersecting屬性。
    注意 初次渲染時 item.target是全部的目標元素。而在後面利用滾動條來操做時的 item.target就是要顯示和要隱藏的元素。因此能夠利用這個性質。能夠用一個對象來實時保存當前可見的元素。當窗口變化時,就能夠額作到真正的條件渲染了。
entries.forEach(item => {
	    //index是我在目標元素上的一個自定義屬性
		const index = item.target.getAttribute('index');
		if (item.isIntersecting) {
		    //將當前視圖中可見的echart圖進行渲染
			this.$store.commit('initChart', index)
			// this.io.unobserve(item.target); // 中止觀察當前元素 避免不可見時候再次調用callback函數
		} else {
			// 清除不可見的,目的是爲了避免渲染不在視圖中的圖片
			this.$store.commit('clearInvisibleChartInstance', index)
		}
	});
複製代碼

項目地址bash

相關文章
相關標籤/搜索