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);
    }
  });
});
複製代碼

擴展閱讀

參考

最後

推薦一下本身的我的公衆號:前端精讀(每日定時推送一篇前端好文)

前端每日精讀
相關文章
相關標籤/搜索