动态插入的无效,浏览器仅在HTML解析阶段处理;首屏关键资源须写死在HTML中;运行时预加载唯一可靠方式是new Image()或fetch()。

动态插入的 根本不生效
浏览器只在 HTML 解析阶段处理 rel="preload",JS 动态创建并 append 到 document.head 的 不会触发预加载逻辑——它只是个普通 DOM 节点。你看到的资源下载,其实是后续 img.src 或 fetch() 触发的,和 preload 无关。
实操建议:
- 首屏关键资源(如 banner 图、核心字体)必须写死在 HTML 的
最前面,且href是静态字符串("/assets/logo.webp"✅,"/assets/${name}.webp"❌) - 若资源路径由后端模板或构建时变量决定,确保它在服务端渲染阶段已展开为确定值
- 用 Chrome DevTools → Network 面板过滤
Preload类型,确认请求是否带Initiator: Parser—— 只有这个才是真 preload
new Image() 是唯一可控的运行时预加载方式
当预加载决策依赖用户行为(hover、滚动到某区域)、设备能力(window.devicePixelRatio)或模块状态(懒加载组件已挂载),new Image() 是绕过 HTML 解析限制的唯一手段。
但顺序和状态检查不能错:
- 必须先绑定
onload/onerror,再赋值src;否则缓存命中时事件同步触发,回调直接丢失 - 赋值后立刻检查
img.complete === true,为 true 表示已缓存,需立即执行成功逻辑(比如启用按钮) - 并发数要节制:同一域名下浏览器通常只允许 6~8 个并发图片请求,堆 20 个
new Image()会排队阻塞 - 基础封装可参考:
preloadImage(url)返回 Promise,内部处理 complete 检查与错误 fallback
preload="auto" 对 动态插入完全无效
动态创建的 元素,无论是否设置 preload="auto" 或 preload="metadata",浏览器都不会发起任何预加载请求——它没参与初始解析流程。
正确做法是监听 IntersectionObserver,等元素进入视口前调用 video.load():
-
load()后需监听canplay或loadeddata才能安全展示控件或自动播放 - 若需更激进加载(如课程主视频),可搭配
主动发起请求(仅同域有效,且不触发解码) - iOS Safari 无视所有
preload值是铁律,写了等于白写,别浪费代码行
别把 loading="lazy" 或 display: none 当预加载用
loading="lazy" 是延迟加载,不是预加载;display: none 只是隐藏,不触发下载。两者都和“提前获取资源”无关,反而可能让资源更晚出现。
容易踩的坑:
- 给首屏大图加
loading="lazy",导致 LCP 拖慢甚至失败 - 用
display: none包裹一个,以为“藏起来就能预加载”,实际浏览器根本不会发起请求
- 想靠 CSS 控制图片加载时机?不行。只有
src、srcset或 JS 显式赋值才会触发 fetch
真正需要运行时控制加载时机的场景,必须用 new Image() 或 fetch() + URL.createObjectURL(),没有捷径。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











