如何有条件的实现网页预取?

仅在良好的网络条件下和可用的设备上进行预取。

预取策略
预取已经存在一段时间了,但谨慎使用它很重要,因为它会为并非立即需要的资源消耗额外的带宽。应谨慎应用此技术,以避免不必要的数据使用。

如果满足以下条件,则会预取文章:

  • 预取文章链接的可见性: 使用Intersection Observer API来检测包含要预取的文章的部分的可见性。
  • 增加数据使用的有利条件:如前所述,预取是一种推测性性能改进,会消耗额外的数据,并且在每种情况下都可能不是理想的结果。为了减少浪费带宽的可能性,使用网络信息 API和设备内存 API来确定是否获取下一篇文章。 仅在以下情况下获取下一篇文章:
    • 连接速度至少为3G,设备内存至少为4GB,
    • 或者如果设备运行的是 iOS。
  • CPU 空闲:最后,Terra 使用 来检查 CPU 是否空闲并且能够执行额外的工作requestIdleCallback,这会在主线程空闲时或在特定(可选)截止日期之前处理回调(以先到者为准)。

遵守这些条件可确保 仅在必要时获取数据,从而节省带宽和电池寿命,并最大限度地减少最终未使用的预取的影响。

JavaScript实现

function prefetch(nodeLists) {
  // Exclude slow ECTs < 3g
  if (navigator.connection &&
    (navigator.connection.effectiveType === 'slow-2g'
      || navigator.connection.effectiveType === '2g')
  ) {
    return;
  }

 
// Exclude low end device which is device with memory <= 2GB
  if (navigator.deviceMemory && navigator.deviceMemory <= 2) {
    return;
  }

  const fetchLinkList = {};

  const observer = new IntersectionObserver(function (entries) {
    entries.forEach(function (entry) {
      if (entry.isIntersecting) {
        if (!fetchLinkList[entry.target.href]) {
          fetchLinkList[entry.target.href] = true;

          fetch(entry.target, {
            priority: 'low'
          });
        }

        observer.unobserve(entry = entry.target);
      }
    });
  });
}

const idleCallback = window.requestIdleCallback || function (cb) {
  let start = Date.now();

  return setTimeout(function () {
    cb({
      didTimeout: false,
      timeRemaining: function () {
        return Math.max(0, 50 - (Date.now() - start));
      }
    });
  }, 1);
}

idleCallback(function () {
  prefetch(nodeLists)
})

  • 在启动预取之前,预取函数首先会检查最低连接质量和设备内存。
  • 然后,它使用 IntersectionObserver 监视视口中的元素何时可见,随后将 URL 添加到列表中进行预取。
  • 预取过程通过 requestIdleCallback 进行调度,目的是在主线程空闲时执行预取函数。