IntersectionObserver 预取需融合滚动位置、悬停行为与模块语义判断用户意图,通过 rootMargin 扩展触发窗口、多级 threshold 分阶段加载、data-prefetch-type 语义分级、悬停增强与滚动趋势动态调参,并用 WeakMap/Map 去重、并发限制及失败降级保障性能。

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










