HTML图片懒加载技术
说到网页优化,图片懒加载绝对是个"狠角色"。啥叫懒加载?就是图片不着急全都加载,等你快要看到它的时候,它才姗姗来迟地冒出来。这样一来,页面加载速度嗖嗖的,用户体验直接起飞,流量也省了不少,简直就是前端界的"省钱小能手"。
一、原理简介
懒加载的核心思想就是"按需分配,量力而行"。你想啊,页面那么长,图片那么多,一股脑全加载,服务器都要"累瘫"了。我们聪明点,等用户快滚到图片那儿了再加载,这不就皆大欢喜了?
二、常用实现方式
1. 原生loading属性
HTML5给<img>标签塞了个新属性loading,用起来不要太简单:
<img src="example.jpg" loading="lazy" alt="示例图片">
loading="lazy":懒加载,图片"摸鱼"到快上班才来loading="eager":默认,图片"卷王"一上来就干活
loading="lazy" 的工作机制详解
-
触发时机
- 当图片距离视口底部还有一段距离(具体多少,得看浏览器心情),浏览器就会悄悄把图片加载上。
- 这样用户一滚到图片,图片就"闪亮登场",不会让你等到花儿都谢了。
-
浏览器实现原理
- 浏览器解析HTML时,发现
loading="lazy"的图片,就把它们先放一边,等到快进视口了再加载。 - 这样一来,首屏加载速度直接"起飞",服务器压力也小了不少。
- 浏览器解析HTML时,发现
-
兼容性
- 主流浏览器都支持,除了Safari有点"特立独行",有些版本还不认这个属性。
- 具体支持情况可以去Can I use查查,别到时候"翻车"了。
-
注意事项
- 只对
<img>和<iframe>生效,别想着给div加。 - 懒加载图片可能被爬虫无视,SEO要注意,重要图片别懒。
- 首屏大图、Logo啥的,建议别用懒加载,别让用户一进来啥都看不到。
- 图片在
display: none的容器里,懒加载可能"罢工"。
- 只对
优点:简单粗暴,原生支持,写起来不要太爽。 缺点:兼容性有坑,老浏览器不买账。
2. Intersection Observer API
用JavaScript的Intersection Observer API,懒加载就像开了"外挂",灵活得很:
Intersection Observer API 详解
-
基本原理
- 这个API就是个"监控器",专门盯着元素啥时候进视口。
- 图片快进视口了,咱就让它加载,效率杠杠的。
-
核心API与参数说明
IntersectionObserver构造函数:const observer = new IntersectionObserver(callback, options);callback:元素可见性变化时的回调,图片"露脸"就通知你。options:配置项,能指定"监控"范围、触发时机等。root:谁当视口,不写就是浏览器窗口。rootMargin:给视口加点"缓冲区",提前加载。threshold:多少比例才算"露脸",0就是刚露头就算。
observer.observe(target):开始盯着目标元素。observer.unobserve(target):不盯了。observer.disconnect():全体解散。
-
懒加载代码示例
<img data-src="example.jpg" class="lazy" alt="示例图片">
<script>
const imgs = document.querySelectorAll('img.lazy');
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
obs.unobserve(img);
}
});
});
imgs.forEach(img => observer.observe(img));
</script>
优点:灵活自如,兼容性还行,能自定义各种骚操作。 缺点:得写JS,IE直接"摆烂",要兼容得上polyfill。
3. 常用懒加载库
除了前面提到的 lazysizes 外,市面上还有一堆"卷王"库,个个身怀绝技:
- lazysizes:功能全,插件多,社区活跃,懒加载界的"扛把子"。
- lozad.js:lozad.js 体积小,无依赖,简单粗暴,支持多种元素。
- vanilla-lazyload:vanilla-lazyload 配置灵活,兼容性好,适合各种奇葩场景。
- blazy:blazy 轻量级,支持图片、iframe,兼容性也不错。
- react-lazyload:react-lazyload 专为React打造,API友好,集成无压力。
这些库大多用Intersection Observer当"底座",有的还会回退到滚动事件监听,开发者可以按需"拿来主义"。
五、WHY
为什么要用图片懒加载?
-
页面加载速度快到飞起,用户体验直接拉满
- 图片是网页里的"重量级选手",全加载页面直接"龟速"。
- 懒加载只让眼前的图片先上,首屏内容"秒开",用户再也不用"望穿秋水"。
-
省流量省资源,老板看了都说好
- 用户没看到的图片就不加载,流量省一大截,移动端用户直呼"真香"。
- 服务器压力小了,网站抗压能力up up。
-
SEO也能兼顾,鱼和熊掌都能有
- 首屏图片别懒,SEO和性能两手抓。
什么时候该用懒加载?
-
图片多、页面长,滚动到底都不带喘气的那种
- 比如:电商商品列表、图片瀑布流、新闻资讯、博客文章列表。
- 这些页面图片多得像下饺子,一次全加载,服务器都要"原地去世"。
-
移动端页面,流量就是命
- 移动端用户流量有限,网络环境"玄学",懒加载能让体验"起飞"。
-
首屏外的图片,别让用户等到天荒地老
- 首屏图片建议直接加载,别让用户一进来啥都看不到。
- 首屏以下的图片,懒加载安排上。
具体例子与原因分析
- 电商网站商品列表:用户一般只看前几屏,懒加载避免一次性加载全部商品图片,页面"嗖嗖"地就出来了。
- 社交平台/图片社区(比如微博、知乎、Pinterest):图片流页面内容多,懒加载让你边刷边看,体验"丝滑"。
- 新闻资讯类网站:文章列表页图片多,懒加载减少无效加载,首屏速度"起飞"。
- 移动端App内嵌H5页面:流量宝贵,懒加载帮你省到极致,加载体验"嘎嘎好"。
一句话总结: 图片懒加载适合图片多、页面长、用户滚动浏览的场景。用得好,性能体验双赢;但首屏核心图片别懒,SEO和首屏体验都要顾全。
六、扩展应用
懒加载Vue3组件封装示例
如下是一个基于TypeScript和
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue';
interface Props {
src: string;
alt?: string;
placeholder?: string;
}
defineProps<Props>();
const imgRef = ref<HTMLImageElement | null>(null);
const loaded = ref(false);
let observer: IntersectionObserver | null = null;
onMounted(() => {
if (imgRef.value) {
observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loaded.value = true;
observer && observer.unobserve(entry.target);
}
});
});
observer.observe(imgRef.value);
}
});
onBeforeUnmount(() => {
if (observer && imgRef.value) {
observer.unobserve(imgRef.value);
observer.disconnect();
}
});
</script>
<template>
<img
ref="imgRef"
:src="loaded ? src : placeholder || ''"
:alt="alt"
style="width: 100%; display: block;"
/>
</template>
使用说明:
src:图片真实地址。alt:图片描述(可选)。placeholder:占位图地址(可选)。- 组件会在图片进入视口时自动加载真实图片。
用法示例:
<LazyImage src="https://example.com/real.jpg" placeholder="https://example.com/placeholder.jpg" alt="示例图片" />
react组件
如下是一个基于TypeScript的React图片懒加载组件示例,使用函数组件和useRef、useEffect实现:
import React, { useRef, useState, useEffect } from 'react';
interface LazyImageProps {
src: string;
alt?: string;
placeholder?: string;
style?: React.CSSProperties;
className?: string;
}
const LazyImage: React.FC<LazyImageProps> = ({ src, alt = '', placeholder = '', style, className }) => {
const imgRef = useRef<HTMLImageElement | null>(null);
const [loaded, setLoaded] = useState(false);
useEffect(() => {
const img = imgRef.current;
if (!img) return;
let observer: IntersectionObserver | null = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
setLoaded(true);
observer && observer.unobserve(entry.target);
}
});
});
observer.observe(img);
return () => {
if (observer && img) {
observer.unobserve(img);
observer.disconnect();
}
};
}, []);
return (
<img
ref={imgRef}
src={loaded ? src : placeholder}
alt={alt}
style={style}
className={className}
/>
);
};
export default LazyImage;
使用说明:
src:图片真实地址。alt:图片描述(可选)。placeholder:占位图地址(可选)。style、className:可自定义样式。- 组件会在图片进入视口时自动加载真实图片。
用法示例:
<LazyImage src="https://example.com/real.jpg" placeholder="https://example.com/placeholder.jpg" alt="示例图片" style={{ width: '100%' }} />
微信原生小程序组件封装
如下是一个微信原生小程序图片懒加载组件的封装示例,利用小程序的IntersectionObserver实现:
1. 组件结构
lazy-image.wxml
<image
wx:if="{{show}}"
src="{{src}}"
alt="{{alt}}"
class="lazy-image"
mode="aspectFill"
/>
<image
wx:else
src="{{placeholder}}"
alt="{{alt}}"
class="lazy-image"
mode="aspectFill"
/>
lazy-image.js
Component({
properties: {
src: String,
alt: String,
placeholder: {
type: String,
value: ''
}
},
data: {
show: false
},
lifetimes: {
attached() {
this.createIntersectionObserver()
.relativeToViewport({ bottom: 100 })
.observe('.lazy-image', (res) => {
if (res.intersectionRatio > 0) {
this.setData({ show: true });
this.observer && this.observer.disconnect();
}
});
},
detached() {
this.observer && this.observer.disconnect();
}
},
methods: {
createIntersectionObserver() {
this.observer = this.createIntersectionObserver();
return this.observer;
}
}
});
lazy-image.json
{
"component": true
}
lazy-image.wxss
.lazy-image {
width: 100%;
display: block;
}