原理: img标签统一自定义属性data-src='default.png',当检测到图片出现在窗口之后再补充src属性,此时才会进行图片资源加载
原生写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.box {
display: flex;
flex-wrap: wrap;
}
img {
width: 400px;
height: 400px;
}
</style>
</head>
<body>
<!-- 可以给img标签统一自定义属性data-src='default.png',当检测到图片出现在窗口之后再补充src属性,此时才会进行图片资源加载。 -->
<div class="box">
<img data-src="http://pic.616pic.com/ys_img/00/79/81/FTVKUmzD8E.jpg">
<img data-src="http://pic.616pic.com/ys_img/00/07/10/dKr1dCXshx.jpg">
<img data-src="http://pic.616pic.com/ys_img/00/08/05/88rYhZkGBm.jpg">
<img data-src="http://pic.616pic.com/ys_img/00/11/26/tQ9RAXG5SE.jpg">
<img data-src="http://pic.616pic.com/ys_img/00/12/26/WPsNV0b8Po.jpg">
<img data-src="http://pic.616pic.com/ys_img/00/28/74/vXD17fgSzV.jpg">
<img data-src="http://pic.616pic.com/ys_img/00/03/99/c5bqRIQ2mY.jpg">
<img data-src="http://pic.616pic.com/ys_img/00/03/62/z8rGeMvQwJ.jpg">
<img data-src="http://pic.616pic.com/ys_img/00/14/83/vdzed0sUfo.jpg">
<img data-src="http://pic.616pic.com/ys_img/00/75/27/mwlBaFXGSV.jpg">
<img data-src="http://pic.616pic.com/ys_img/00/76/75/dfjKrC9Sx5.jpg">
<img data-src="http://pic.616pic.com/ys_img/00/84/96/xBW5RPyOoq1.jpg">
<img data-src="http://pic.616pic.com/ys_img/00/85/08/thbRwvoP9z.jpg">
</div>
<script>
function lazyload() {
const imgs = document.getElementsByTagName('img')
const len = imgs.length
// 视口的高度
const viewHeight = document.documentElement.clientHeight
// 滚动条高度
const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop
for (let i = 0; i < len; i++) {
const offsetHeight = imgs[i].offsetTop
if (offsetHeight < viewHeight + scrollHeight) {
const src = imgs[i].dataset.src
imgs[i].src = src
}
}
}
lazyload()
// 可以使用节流优化一下
window.addEventListener('scroll', lazyload)
</script>
</body>
</html>
vue中的使用
main.js中全局动态注册自定义指令
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
+ import * as directives from '@/directives'
const app = createApp(App)
+ Object.keys(directives).forEach(key => {
+ app.directive(key, directives[key])
+ })
app.use(store).use(router).mount('#app')
- 新建文件
src/directives/index.js
export const lazy = {
mounted(el, { value }) {
// 图片的懒加载逻辑
// 参数1:回调函数
// 参数2:可选的配置
const observer = new IntersectionObserver(
([{ isIntersecting }], observer) => {
// isIntersecting为true代表进入可视区
if (isIntersecting) {
// 停止监听
observer.unobserve(el)
// 给el元素设置src属性
// value = '123.jpg'
el.src = value
// 如果图片加载失败,显示默认的图片
el.onerror = function() {
el.src = require('@/assets/error.jpg')
}
}
},
{
threshold: 0
}
)
observer.observe(el)
}
}
- 在组件中使用
<template>
<h1>图片懒加载</h1>
<div class="box">
<img v-for="(img, index) in imgList" :key="index" v-lazy="img">
</div>
</template>
<script>
import { reactive, toRefs } from 'vue'
import { reqImgList } from '@/api/img'
export default {
name: 'Home',
setup () {
const state = reactive({
imgList: []
})
reqImgList(4).then(res => {
state.imgList = res.pic
})
return {
...toRefs(state)
}
}
}
</script>
<style lang="less" scoped>
.box {
display: flex;
flex-wrap: wrap;
img {
width: 400px;
height: 400px;
}
}
</style>