什么是quicklink
它是谷歌開源又一個(gè)精品庫,其官網(wǎng)是這么說的:
Faster subsequent page-loads by prefetching in-viewport links during idle time(通過空閑時(shí)間預(yù)加載視口中的links資源來讓之后的頁面加載更快)
聽起來是一個(gè)很神奇的功能氧苍,那么它是如何工作的呢:
- 檢測(cè)視口中的鏈接 (使用 Intersection Observer監(jiān)控)
- 等待瀏覽器空閑 (使用 requestIdleCallback監(jiān)控)
-
判斷用戶不是使用的慢網(wǎng)(如2G) (使用
navigator.connection.effectiveType
) 或者有啟用了數(shù)據(jù)緩存 (使用navigator.connection.saveData
) -
預(yù)加載對(duì)應(yīng)的URL (使用
<link rel=prefetch>
或者 XHR). 提供對(duì)請(qǐng)求優(yōu)先級(jí)的一些控制 (如果支持可切換成fetch()
).
怎么用quicklink
功能如此強(qiáng)大寞肖,那使用是不是會(huì)很麻煩呢拐格?不,你錯(cuò)了,讓我們來看看如何簡(jiǎn)單的來使用這個(gè)神奇的庫吧狂打。
安裝
可以采用npm的方式:
npm install --save quicklink
或者
我們可以直接進(jìn)行引用:https://unpkg.com/quicklink@1.0.0/dist/quicklink.umd.js
使用
quicklink 提供了幾種使用的方式:
快速使用:
<!--首先引用umd模式的js -->
<script src="dist/quicklink.umd.js"></script>
<!-- 在任何時(shí)候都可以進(jìn)行初始化 -->
<script>
quicklink();
</script>
你可以在load事件回調(diào)中來初始化书斜。
<script>
window.addEventListener('load', () =>{
quicklink();
});
</script>
ES Module 的引入:
import quicklink from "quicklink/dist/quicklink.mjs";
quicklink();
quicklink接口參數(shù):
接口 | 描述 | 類型 | 默認(rèn)值 |
---|---|---|---|
el | 在是口中需要被監(jiān)控的容器外殼 | dom節(jié)點(diǎn) | - |
urls | 進(jìn)行預(yù)加載的靜態(tài)URL數(shù)組(用來替換el屬性配置的監(jiān)控外殼) | Array | - |
timeout | 自定義的延遲時(shí)間诬辈,默認(rèn)為requestIdleCallback,也可以自定義 | Function | requestIdleCallback |
priority | 設(shè)置獲取資源的優(yōu)先級(jí) | boolean | false |
origin | 設(shè)置預(yù)加載資源的域菩佑,來保證任何域下都可以加載 | Array | 默認(rèn)相同域 |
ignores | 一個(gè)正則自晰,函數(shù)或者Array,用于進(jìn)一步的確定是否預(yù)取URL | RegExp稍坯、Function酬荞、Array | - |
使用方法
先來段html代碼:
<div id="demo1" class="screen">
<h1>Basic demo</h1>
<a href="../test/1.html">Link 1</a>
我是第一個(gè)容器
<a href="../test/2.html">Link 2</a>
</div>
<div class="screen">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eos, quos?
<a href="../test/3.html">Link 3</a>
<section id="stuff">
<a href="../test/main.css">CSS</a>
</section>
</div>
<div class="screen">
<a href="../test/4.html">Link 4</a>
</div>
- 我們可以設(shè)置預(yù)加載的位置,這樣可以視口中有很多資源時(shí)瞧哟,也不會(huì)加載混巧,只會(huì)當(dāng)demo1容器到視口時(shí)才會(huì)加載。
let elm = document.getElementById('demo1');
quicklink({
'el': elm
});
來看看我們的效果(不要在意錯(cuò)誤勤揩,這只是個(gè)demo):
- 我們?cè)趤砜纯磸?qiáng)制設(shè)置urls咧党,這樣就不會(huì)來判斷視口位置了。
quicklink({
urls: ['2.html','3.html', '4.js']
});
- 默認(rèn)為低優(yōu)先級(jí)(priority為false)系統(tǒng)會(huì)使用prefetch陨亡。對(duì)于高優(yōu)先級(jí)傍衡,會(huì)使用fetch()深员,或者XHR。
quicklink({ priority: false });
quicklink({ priority: true });
源碼解析
我們的主要目的是來研究一下這個(gè)庫是如何來實(shí)現(xiàn)這些神奇的功能的蛙埂。
整個(gè)項(xiàng)目中有比較重要API我們需要了解倦畅。
- 可以用來監(jiān)控視口的功能API IntersectionObserver
- 可以提前預(yù)加載 prefetch
首先我們先監(jiān)控整個(gè)瀏覽器的視口:
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const link = entry.target;
if (toPrefetch.has(link.href)) {
observer.unobserve(link);
prefetcher(link.href);
}
}
});
});
當(dāng)獲取到需要提前預(yù)加載的資源文件URL之后,判斷用戶當(dāng)前網(wǎng)絡(luò)绣的,如果是非2G網(wǎng)絡(luò)時(shí)叠赐,我們才會(huì)進(jìn)行資源預(yù)加載:
function prefetcher(url, isPriority, conn) {
if (preFetched[url]) {
return;
}
if (conn = navigator.connection) {
// Don't prefetch if the user is on 2G. or if Save-Data is enabled..
if ((conn.effectiveType || '').includes('2g') || conn.saveData) return;
}
// Wanna do something on catch()?
return (isPriority ? highPriFetchStrategy : supportedPrefetchStrategy)(url).then(() => {
preFetched[url] = true;
});
};
當(dāng)isPriority優(yōu)先級(jí)為高時(shí)調(diào)用highPriFetchStrategy,優(yōu)先級(jí)為低時(shí)調(diào)用supportedPrefetchStrategy屡江。
highPriFetchStrategy
如果優(yōu)先級(jí)為高時(shí)芭概,首先我們會(huì)采用Fetch,如果不支持惩嘉,則降級(jí)為XHR進(jìn)行加載:
function highPriFetchStrategy(url) {
return self.fetch == null
? xhrPrefetchStrategy(url)
: fetch(url, {credentials: `include`});
}
supportedPrefetchStrategy
如果優(yōu)先級(jí)為低時(shí)罢洲,我們會(huì)優(yōu)先使用prefetch,如果不支持宏怔,會(huì)降級(jí)使用XHR進(jìn)行加載:
const supportedPrefetchStrategy = support('prefetch')
? linkPrefetchStrategy
: xhrPrefetchStrategy;
我們?cè)趤砜纯搓P(guān)鍵的2個(gè)函數(shù) xhrPrefetchStrategy 和 linkPrefetchStrategy:
- xhrPrefetchStrategy:函數(shù)中會(huì)直接使用get請(qǐng)求進(jìn)行一起http請(qǐng)求:
function xhrPrefetchStrategy(url) {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open(`GET`, url, req.withCredentials=true);
req.onload = () => {
(req.status === 200) ? resolve() : reject();
};
req.send();
});
}
- linkPrefetchStrategy:我們會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)link奏路,并且使用prefetch來進(jìn)行pretch預(yù)加載:
function linkPrefetchStrategy(url) {
return new Promise((resolve, reject) => {
const link = document.createElement(`link`);
link.rel = `prefetch`;
link.href = url;
link.onload = resolve;
link.onerror = reject;
document.head.appendChild(link);
});
};
總結(jié)一下:
來了一個(gè)比較渣渣的流程圖;
相關(guān)文檔
Resource Hints
https://github.com/GoogleChromeLabs/quicklink