IntersectionObserver 预取需融合滚动位置、悬停行为与模块语义判断用户意图,通过 rootMargin 扩展触发窗口、多级 threshold 分阶段加载、data-prefetch-type 语义分级、悬停增强与滚动趋势动态调参,并用 WeakMap/Map 去重、并发限制及失败降级保障性能。
直接用 IntersectionObserver 做跳转预取,关键不是“等用户点链接”,而是让浏览器在用户大概率要点击前,就悄悄把目标页面的 HTML、关键 JS 和 CSS 提前拉下来——这需要把“滚动位置+悬停行为+模块语义”三者结合起来判断意图,而不是只盯一个
。
用 rootMargin + threshold 构建“可预测”的触发窗口
单纯设
或
不可靠:它只反映“此刻是否露边”,但用户从看到链接到点击,中间有 200–600ms 的决策延迟。网络请求、HTML 解析、JS 执行都要抢在这段时间内完成。
rootMargin 是核心杠杆
:设为
(桌面)或
(移动端),相当于把视口逻辑下移一段距离,让链接在真正进入可视区前 300ms 左右就触发预取
threshold 推荐用数组
:
。10% 可见时做 DNS 预解析 + TCP 预连接;30% 时 fetch HTML header(
)确认状态码和缓存策略;60% 时才真正 fetch 完整 HTML 并存入
避免
或
:前者等于放弃“预”字,后者在快速滚动时会大量误触发
按 data-prefetch-type 分级调度资源获取行为
不是所有带
的链接都值得预取。必须结合语义标记区分优先级,防止带宽被低价值跳转挤占。
主流程跳转
(如商品详情页、下单页):标记
,满足
即启动完整 fetch +
关键模块
辅助路径
(如“帮助中心”“退换货政策”):标记
,仅在
且用户已悬停 300ms 后才 fetch
外部链接/下载类
:默认不预取,除非显式加
属性
引入轻量行为信号,让预取“懂用户意图”
纯像素级观察在真实场景中容易失效。比如轮播图切换时链接频繁进出视口,或用户快速扫过导航栏——这些都不该触发预取。
悬停增强
:监听
,对目标链接所在模块(如
或
)内所有
链接立即发起预取,无视当前 intersection 状态
滚动趋势动态调参
:节流采样 scroll 事件(60ms 间隔),若连续两次
(向下滚动),临时将 rootMargin 加大到
;向上滚动则收窄至
,减少误加载
三折叠设备适配
:检测
或屏幕宽高比,对 Mate XT 等 G 态(2200×2000)自动切为
防重复、控并发、保可用的执行层约束
一个电商首页可能有 20 个“去看看”按钮共用同一商品详情 URL,不做控制会导致 20 个并发 fetch,浪费带宽还可能触发 CDN 限流。
WeakMap 缓存单元素状态
:以 DOM 元素为 key,记录
,避免同一链接被多次 observe 触发
Map 全局去重 URL
:用
,相同
只保留一个 fetch Promise,后续直接复用
并发数硬限制
:内部队列最多同时执行 2 个预取任务(
),关键首屏资源永远优先
失败降级
:fetch 失败后写入
,30 秒内不再尝试;节流网络(
)下禁用 HTML 预取,只保留 DNS 预解析
isIntersectingthreshold: 0isIntersecting === true"250px 0px"${window.innerHeight * 0.25}px[0.1, 0.3, 0.6]method: 'HEAD'cacheStoragerootMargin: "0px"threshold: [0, 1]hrefdata-prefetch-type="critical"intersectionRatio >= 0.3import()data-prefetch-type="secondary"intersectionRatio >= 0.6data-prefetch="true"mouseenter.nav-menu.product-carddata-prefetchdeltaY > 0"400px""100px"window.matchMedia('(display-mode: standalone)')${window.innerHeight * 0.3}px{ url, promise, timestamp }urlToPromise = new Map()hrefqueue.slice(0, 2)failedUrls.add(url)navigator.connection.effectiveType === '2g'