最近在項目中,遇到要對大量圖表數據渲染的狀況。因此當時想着能不能只渲染視圖中可見的圖表呢?而後在網上搜索了一番,查到了這個有趣的知識。css
爲開發者提供了一種能夠異步監聽目標元素與其祖先或視窗(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
disconnect()
unobserve()
observe()
takeRecords()
如下是項目中的一部分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)
}
複製代碼
//全部的`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