预取需提前200–400ms触发,依赖rootmargin实现时间维度调度;用weakmap+map两级去重防并发;结合滚动趋势动态调整rootmargin;fetch后必须decode完成再赋值src。

不是等元素“刚露头”才加载,而是让浏览器提前感知它即将进入视口,在用户真正看到前 200–400ms 就发起资源请求——这需要把 IntersectionObserver 从被动监听工具,升级为主动调度系统。
用 rootMargin 实现时间维度的提前触发
threshold 只能表达“当前可见比例”,无法表达“即将进来”。真正可控的提前量来自 rootMargin:它把视口逻辑边界向外扩展,让回调在像素交集发生前就触发。
- 桌面端滚动较慢,常用
"300px";中速手机建议"150px" - 三折叠设备(如 Mate XT 的 G 态)需动态适配:
`${window.innerHeight * 0.3}px` - 避免写死值,可结合设备 DPR 和历史滚动速度反推:例如
Math.max(100, window.devicePixelRatio * 120)px
用 WeakMap + Map 实现两级去重防竞争
一个模块可能被多次 observe,多模块也可能共用同一张图 URL。不加控制会引发并发激增、带宽浪费,甚至触发 CDN 限流。
- 用 WeakMap 缓存每个 DOM 元素的状态:
key = element,value = { url, promise, timestamp } - 用 Map 对全局 URL 去重:
url → Promise,相同资源只 fetch 一次 - 内部限制并发数,例如
Promise.allSettled(queue.slice(0, 3)),最多同时预取 3 个资源
结合滚动趋势做轻量预测调度
“感知式”的核心是把预取时机和用户行为意图挂钩,而不是机械响应像素变化。
- 对 scroll 事件节流采样(仅用于趋势判断),计算最近 300ms 的
deltaY符号与幅度 - 若连续两次向下滚动,则临时加大
rootMargin到"400px";向上滚动时收窄为"100px" - 对轮播图、放大镜等交互密集模块,监听
mouseenter,立即触发其下所有data-prefetch图片预取,无视 observer 状态
确保资源可用性:解码完成再赋值 src
预取 ≠ 可用。网络下载完成不等于图片能立刻渲染,未解码的 blob 或 JPEG 仍会导致主线程卡顿。
- 拿到
response.arrayBuffer()后,创建Blob并用URL.createObjectURL() - 创建
Image实例,调用img.decode(),等待 Promise resolve 后再赋值img.src - 失败时回退到传统
src加载,避免白屏或中断体验










