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

相关文章
相关标签/搜索