懒加载
延迟加载,对于一个很长的页面,优先加载可视区域的内容,其他部分等进入可视区域时再加载
懒加载作用
是一种网页性能优化的方式,它能极大的提升用户体验。比如一个页面中有很多图片,但是首屏只出现几张,这时如果一次性把图片全部加载出来会影响性能。这时可以使用懒加载,页面滚动到可视区再加载。优化首屏加载。
图片懒加载
监听滚动条滚动事件,当视口的高度+滚动高度,大于图片所在位置举例顶部的偏移量时(也就是距离),加载图片资源
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div style="height: 1200px;"></div>
<img src="https://ts2.cn.mm.bing.net/th?id=OIP-C.Fi0rA6s5NQ1VIlwp9IzhIgHaKe&w=210&h=297&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2"
data-src="https://ts3.cn.mm.bing.net/th?id=OIP-C.D-43aYLc7We_sO_5ZSMIXgHaFj&w=288&h=216&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2" />
<script>
let img = document.getElementsByTagName("img");
let num = img.length;
let count = 0;
lazyload();
window.addEventListener('scroll', lazyload);
function getTop(el) {
var T = el.offsetTop;
// 迭代地获取元素及其父元素相对于文档顶部的累积偏移量
while (el = el.offsetParent) {
T += el.offsetTop;
}
// 循环后 返回元素相对于文档顶部的总偏移量
return T;
}
function lazyload() {
//视口高度
let viewHeight = document.documentElement.clientHeight || document.body.clientHeight;
//滚动高度
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
for (let i = count; i < num; i++) {
// 元素现在已经出现在视口中
if (getTop(img[i]) < scrollTop + viewHeight) {
// 如何图片等于默认图 则加载新图
if (img[i].getAttribute("src") !== "https://ts2.cn.mm.bing.net/th?id=OIP-C.Fi0rA6s5NQ1VIlwp9IzhIgHaKe&w=210&h=297&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2") continue;
img[i].src = img[i].getAttribute("data-src");
count++;
} else {
break;
}
}
}
</script>
</body>
</html>
使用浏览器提供的getBoundingClientRect(),优化上述代码: lazyload修改如下:
getBoundingClientRect()
用于获取元素相对于视口的位置信息
function lazyload() {
let viewHeight = document.documentElement.clientHeight || document.body.clientHeight;
for (let i = count; i < num; i++) {
// getBoundingClientRect() 用于获取元素相对于视口的位置信息
// 当相对于视口位置 小于等于视口时说明图片已经可见了
if (img[i].getBoundingClientRect().top < viewHeight) {
if (img[i].getAttribute("src") !== "https://ts2.cn.mm.bing.net/th?id=OIP-C.Fi0rA6s5NQ1VIlwp9IzhIgHaKe&w=210&h=297&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2") continue;
img[i].src = img[i].getAttribute("data-src");
count++;
} else {
break;
}
}
}
使用IntersectionObserver()再次优化上述代码
IntersectionObserver
是浏览器原生提供的构造函数,用于查看某个元素是否进入了视口(viewport)
,用户能否看到该元素
修改上述代码:
<script>
let img = document.getElementsByTagName("img");
const observer = new IntersectionObserver(changes => {
console.log(changes)
//changes 是被观察的元素集合
for (let i = 0, len = changes.length; i < len; i++) {
let change = changes[i];
// 通过这个属性判断是否在视口中
if (change.isIntersecting) {
const imgElement = change.target;
imgElement.src = imgElement.getAttribute("data-src");
// IntersectionObserver 对象的一个方法,用于停止对指定元素的观察
observer.unobserve(imgElement);
}
}
})
Array.from(img).forEach(item => {
observer.observe(item)
});
</script>
内容懒加载:
一个页面有n条订单,每次可以给他显示10条,等用户拖动滚动条滚动到订单列表底部一段距离时再显示10条,直到加载完
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
span {
display: block;
}
</style>
</head>
<body>
<div style="height: 1200px;">1</div>
<div class="box">
<span>开始</span>
</div>
<script>
let div = document.getElementsByClassName("box")[0];
let lastSpanElement = div.querySelector('span:last-child');
const observer = new IntersectionObserver(changes => {
console.log(changes)
//changes 是被观察的元素集合
for (let i = 0, len = changes.length; i < len; i++) {
let change = changes[i];
// 通过这个属性判断是否在视口中
if (change.isIntersecting) {
for (let i = 0; i < 10; i++) {
const spanElement = document.createElement('span');
spanElement.textContent = `这是第 ${i + 1} 个<span>标签`;
div.appendChild(spanElement);
}
observer.unobserve(lastSpanElement);
lastSpanElement = div.querySelector('span:last-child');
observer.observe(lastSpanElement)
}
}
})
observer.observe(lastSpanElement)
</script>
</body>
</html>
详情可参考:
(IntersectionObserver API详解_
app.directive
自定义全局指令,它有两种写法
// 注册(对象形式的指令)
// 所有生命周期都可
app.directive('my-directive', {
/* 自定义指令钩子 */
})
// 注册(函数形式的指令)
// 固定只在2个生命周期上触发, mounted 和 updated
app.directive('my-directive', () => {
/* ... */
})
vue中install方法
可供我们开发新的自定义指令跟全局注册组件,第一个参数是vue
的构造器,第二个参数是可选的选项对象
然后再开始编写扩展指令之图片懒加载指令
export default {
install (app) {
app.component(XtxSkeleton.name, XtxSkeleton)
app.component(XtxCarousel.name, XtxCarousel)
+ defineDirective(app)
}
}
import defaultImg from '@/assets/images/200.png'
// 扩展指令
const defineDirective = (app) => {
// 图片懒加载
app.directive('lazyload', {
mounted(el, binding) {
const observer = new IntersectionObserver(([{ isIntersecting }]) => {
if (isIntersecting) {
// 停止观察
observer.unobserve(el)
el.onerror = () => {
// 图片加载失败 设置默认图
el.src = defaultImg
}
// binding 是对象,.value就是绑定指令的值
el.src = binding.value
}
}, {
//目标元素和根元素相交部分的比例达到该值的时候,
//callback 函数将会被执行
threshold: 0.01
})
// 开始观察
observer.observe(el)
}
})
}
使用扩展的图片懒加载指令:
<img alt="" v-lazyload="goods.picture" />
扩展:
vue还有许多工具库,其中也有类似的api可以实现这种功能,列如: