预加载图片必须用或javascript创建image对象,其他方式不可靠;前者需置于且href为静态路径,后者需先绑定事件再赋值src并控制并发。

预加载图片不是靠 <img> 标签加个属性就能自动生效的,真要提前下载进缓存,必须用 <link rel="preload"> 声明,或用 JavaScript 主动创建 Image 对象。其他方式(比如 loading="lazy"、display: none、CSS 背景负偏移)要么是延迟加载,要么不可靠、无法监听状态。
<link rel="preload"> 是首屏关键图最稳妥的写法
它不渲染、不占布局、只下载,浏览器能识别为高优先级资源,Lighthouse 也能打分。但必须写对位置和参数:
- 必须放在
里,越早越好;as="image"缺一不可,否则浏览器当普通 fetch 处理,可能跳过图片特有的缓存策略 -
href必须是静态路径,不能含 JS 变量或模板语法(如/assets/${name}.webp),否则直接 404 - 相对路径以 HTML 文件所在位置为基准,比如 HTML 在
/user/profile.html,就别写./images/hero.jpg,改用/images/hero.jpg - Chrome 101+ 支持
fetchpriority="high",可显式抬高请求优先级;旧版忽略无副作用,建议加上
示例:
<link rel="preload" as="image" href="/assets/logo.webp" fetchpriority="high">
用 Image 对象做运行时条件预加载
当你需要根据用户行为(hover、点击)、设备像素比(window.devicePixelRatio)、模块加载时机等动态决定是否加载某张图时,Image 是唯一可控的选择。但顺序和容错必须到位:
- 先绑定
onload和onerror,再赋值src;否则缓存命中时事件同步触发,回调直接被跳过 - 赋值后立刻检查
img.complete:若为true,说明已缓存,需手动执行成功逻辑 - 避免在 for 循环里密集
new Image()——浏览器并发请求数通常只有 6~8 个,建议用 Promise 队列控制并发(例如每次最多 4 个) - 不要写
new Image().src = url这种简写,失败无法捕获,也无法 await
基础封装参考:
const preloadImage = (url) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error(`Failed to load ${url}`));
img.src = url;
});
};
别把 loading="lazy" 或 display: none 当预加载用
这是最常踩的坑:loading="lazy" 是推迟加载,Chrome 默认会等到滚动到视口前 500px 才发请求;loading="eager" 只是“不懒”,不等于“提前”。display: none 的 <img> 仍按正常时机加载,且会占 DOM、触发布局计算。
- CSS 负偏移背景图(
background-position: -9999px -9999px)虽能触发下载,但无法监听完成、无法 fallback、不支持 WebP 自动降级,还可能被某些 adblock 工具拦截 -
fetch()+Blob+URL.createObjectURL()能做到更精细控制,但要用完立刻URL.revokeObjectURL(),否则内存泄漏 - WebP 预加载必须配 fallback:如果目标浏览器不支持 WebP,
<link rel="preload">不会自动切 JPG,得靠 JS 检测后手动加载备用地址
真正难的不是写几行代码,而是判断哪张图值得预加载、什么时候该停手——首屏 banner 和 logo 值得,但用户还没点开的相册第 20 张图不值得。预加载太多反而挤占带宽,拖慢真实首屏渲染。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











