quicklink源码浅析

前言

这些天估计你们都陆陆续续已经据说了 GoogleChromeLabs/quicklink 这个项目了,它由 Google 公司著名开发者 Addy Osmani 发起,实现了在空闲时间预获取页面可视区域内的连接,从而加快后续加载速度,从而来作到后续页面秒开都功能。前端

工做原理

Quicklink 经过如下方式加快后续页面的加载速度:git

  • 检测视区中的连接(使用 Intersection Observer
  • 等待浏览器空闲(使用 requestIdleCallback
  • 检查用户是否处于慢速链接(使用 navigator.connection.effectiveType)或启用了省流模式(使用 navigator.connection.saveData)
  • 预获取视区内的 URL(使用或 XHR)。 可根据请求优先级进行控制(若支持 fetch() 可进行切换)。

触发条件

若是用户的有效链接类型数据保护程序首选项代表它有用的时候, 若是存在urls,则预取一系列URL,或者查看document的视口内连接。 若是进入窗口,就开始预加载github

API

quicklink 接受带有如下参数的 option 对象(可选):segmentfault

  • el:指定须要预获取的 DOM 元素视区
  • urls:预获取的静态 URL 数组(若此配置非空,则不会检测视区中 document 或 DOM 元素的连接)
  • timeout:为 requestIdleCallback 设置的超时整数。 浏览器必须在此以前进行预获取(以毫秒为单位), 默认取 2 秒。
  • timeoutFn:指定超时处理函数。 默认为 requestIdleCallback。 也能够替换为 networkIdleCallback 等自定义函数(github.com/pastelsky/n… demo)
  • priority:布尔值,指定 fetch 的优先级。 默认为 false。 若配置为 true 将会尝试使用 fetch() API(而非 rel = prefetch)
  • origins:容许预取的URL主机名字符串的数组。默认为相同的域源,可防止任何跨源请求。
  • ignores:在origin检查后运行的自定义过滤器,默认没有

源码解读

  1. 合并参数,并设置常量
options = Object.assign({
    timeout: 2e3,
    priority: false,
    timeoutFn: requestIdleCallback,
    el: document,
  }, options);

  observer.priority = options.priority;

  const allowed = options.origins || [location.hostname];
  const ignores = options.ignores || [];
复制代码
  1. 设置requestIdleCallback的callback和浏览器调用callback的最后期限数组

    这里回调函数提供了两种策略:浏览器

    • 若是参数中有urls,则只将urls全部的连接进行预加载,不会对dom下的其余连接进行预加载
    • 若是参数中没有urls,根据options的el来遍历其下的全部a标签,经过Intersection Observer来监控

    若是符合options.origins规则,且不符合options.ignores规则,将其放入预加载的列表toPrefetch中, 能够经过不传options.origins来匹配全部dom

const toPrefetch = new Set();
options.timeoutFn(() => {
    if (options.urls) {
      options.urls.forEach(prefetcher);
    } else {
      Array.from(options.el.querySelectorAll('a'), link => {
        // 把每个a标签放入观察对象中,observer后面解释
        observer.observe(link);
        if (!allowed.length || allowed.includes(link.hostname)) {
          isIgnored(link, ignores) || toPrefetch.add(link.href);
        }
      });
    }
  }, {timeout: options.timeout})

复制代码
  1. 预加载函数

    那么预加载会作些什么呢?post

    首先它会从toPrefetch删除这个即将请求的url性能

    而后经过preFetched判断是否已经加载过了,来减小没必要要的请求。

    而后它会判断当前是否为2g或者省流量模式,若是是,则不作任何操做。

    接着会判断请求类型,默认是rel = prefetch,为true的时候,将会用fetch去请求数据,并对fetch作兼容。

    最后,更新preFetched这个对象

// index.mjs
function prefetcher(url) {
  toPrefetch.delete(url);
  prefetch(new URL(url, location.href).toString(), observer.priority);
}

// prefetch.mjs
const preFetched = {};

function prefetch(url, isPriority, conn) {
  if (preFetched[url]) {
    return;
  }
  if (conn = navigator.connection) {
    if ((conn.effectiveType || '').includes('2g') || conn.saveData) return;
  }
  return (isPriority ? highPriFetchStrategy : supportedPrefetchStrategy)(url).then(() => {
    preFetched[url] = true;
  });
};
复制代码
  1. Intersection Observer

    经过新建一个观察者,来观察放入观察的a标签,当a标签进入窗口的时候,则开始预加载这个连接

const observer = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const url = entry.target.href;
      if (toPrefetch.has(url)) prefetcher(url);
    }
  });
});
复制代码

扩展阅读

参考

最后

推荐一下本身的我的公众号:前端精读(每日定时推送一篇前端好文)

前端每日精读
相关文章
相关标签/搜索