# 懒加载
# 什么是懒加载
在页面一打开时,有些还未被渲染的数据跟着一起加载,会降低渲染速度,影响页面性能。这时就需要将未被渲染的数据暂时不加载出来,等到需要渲染的时刻再加载出来,这便是懒加载。
# 应用场景
- 一个页面中如果有多张图片,而图片是在页面视图之外的,那么在页面初始渲染时,应暂时不加载图片资源。等图片进入视图区域,再进行加载。
- 当后端连续发送了上万条数据时,直接通过数组api迭代是十分损耗性能的一种做法,这时也可以采用懒加载。
# 解决方案
本文主要以图片懒加载为例。
- 使用getBoundingClientRect(),获取图片相对于整个屏幕视图的顶部高度,与容器的高度做对比,相等时则加载图片资源。
<script setup>
import { onMounted, reactive, ref } from "vue";
import aImg from "../assets/1.png";
import bImg from "../assets/2.png";
import cImg from "../assets/3.png";
import dImg from "../assets/4.png";
const dataList = reactive({
imgList: [aImg, bImg, cImg, dImg],
});
const imgRef = ref();
onMounted(() => {
const containerDom = document.getElementById("million_page");
const containerDomHeight = containerDom.clientHeight;
const imgDomArr = document.querySelectorAll("img");
containerDom.addEventListener("scroll", () => {
imgDomArr.forEach((image) => {
const imageTop = image.getBoundingClientRect().top;
if (imageTop < containerDomHeight) {
const data_src = image.getAttribute("data-src");
image.setAttribute("src", data_src);
}
});
});
});
</script>
<template>
<div id="million_page">
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<img
v-for="(item, index) in dataList.imgList"
:key="index"
:data-src="item"
ref="imgRef"
/>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
</div>
</template>
<style lang="less">
#million_page {
height: 50vh;
border: 1px solid black;
overflow-y: auto;
}
</style>
- 使用intersectionObserver(),交叉观察 api。将需要观察的Dom放入它的observe方法。在回调中做逻辑判断。
<script setup>
import { onMounted, reactive, ref } from "vue";
import aImg from "../assets/1.png";
import bImg from "../assets/2.png";
import cImg from "../assets/3.png";
import dImg from "../assets/4.png";
const dataList = reactive({
imgList: [aImg, bImg, cImg, dImg],
});
const imgRef = ref();
// 回调中的参数,为实体数组,存着每一个被观察到的dom。
const callback = (entries) => {
entries.forEach((entry) => {
// 如果他被观察到了(即进入了视图区域内)
if (entry.isIntersecting) {
let image = entry.target;
let data_src = image.getAttribute("data-src");
image.setAttribute("src", data_src);
}
});
};
// 创建交叉观察实例,传入回调。
const observer = new IntersectionObserver(callback);
onMounted(() => {
const containerDom = document.getElementById("million_page");
const imgDomArr = document.querySelectorAll("img");
containerDom.addEventListener("scroll", () => {
imgDomArr.forEach((image) => {
observer.observe(image);
});
});
});
</script>
<template>
<div id="million_page">
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<img
v-for="(item, index) in dataList.imgList"
:key="index"
:data-src="item"
ref="imgRef"
/>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
<p>我是p我是p我是p我是p我是p我是p我是p我是p我是p我是p</p>
</div>
</template>
<style lang="less">
#million_page {
height: 50vh;
border: 1px solid black;
overflow-y: auto;
}
</style>
# 小结
- 若是图片懒加载,推荐使用IntersectionObserver方法,它在性能上比getBoundingClientRect好用,不会引起回流。
- 若是数据表格懒加载,则依然需要使用到getBoundingClientRect,因为在观察api无法将未形成的数据行dom放入observe方法。