Skip to content

Commit

Permalink
feat(index, prefetch) Add support for higher prio fetches
Browse files Browse the repository at this point in the history
  • Loading branch information
addyosmani committed Nov 24, 2018
1 parent adf645e commit c4fb77a
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 26 deletions.
1 change: 1 addition & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<script src="../dist/quicklink.umd.js"></script>
<script>
quicklink();
// quicklink({ priority: 'high' });
</script>
<script type="module">
// import quicklink from "../dist/quicklink.mjs";
Expand Down
7 changes: 4 additions & 3 deletions src/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ function extractInViewportLinks(el) {
* @param {Object} options - Configuration options for quicklink
* @param {Array} options.urls - Array of URLs to prefetch (override)
* @param {Object} options.el - DOM element to prefetch in-viewport links of
* @param {string} options.priority - Attempt to fetch with higher priority (low or high)
*/
export default function (options) {
options = options || {};
options = options || { priority: 'low' };
requestIdleCallback(() => {
if ('connection' in navigator) {
// Don't prefetch if the user is on 2G..
Expand All @@ -69,12 +70,12 @@ export default function (options) {
}
// Prefetch an array of URLs if supplied (as an override)
if (options.urls !== undefined && options.urls.length > 0) {
prefetchLinks(options.urls);
prefetchLinks(options.urls, options.priority);
} else {
// Element to extract in-viewport links for
const el = options.el || document;
extractInViewportLinks(el).then(urls => {
prefetchLinks(urls);
prefetchLinks(urls, options.priority);
});
}
});
Expand Down
104 changes: 81 additions & 23 deletions src/prefetch.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Inspired by the prefetching logic in Gatsby.js

const support = function (feature) {
if (typeof document === `undefined`) {
return false;
Expand All @@ -11,23 +13,66 @@ const support = function (feature) {
return false;
}
};

const linkPrefetchStrategy = function (url) {
if (typeof document === `undefined`) {
return;
}
const link = document.createElement(`link`);
link.setAttribute(`rel`, `prefetch`);
link.setAttribute(`href`, url);
const parentElement =
document.getElementsByTagName(`head`)[0] ||
document.getElementsByName(`script`)[0].parentNode;
parentElement.appendChild(link);
return new Promise((resolve, reject) => {
if (typeof document === `undefined`) {
reject();
return;
}

const link = document.createElement(`link`);
link.setAttribute(`rel`, `prefetch`);
link.setAttribute(`href`, url);

link.onload = resolve;
link.onerror = reject;

const parentElement =
document.getElementsByTagName(`head`)[0] ||
document.getElementsByName(`script`)[0].parentNode;
parentElement.appendChild(link);
});
};

const xhrPrefetchStrategy = function (url) {
const req = new XMLHttpRequest();
req.open(`GET`, url, true);
req.withCredentials = true;
req.send(null);
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open(`GET`, url, true);
req.withCredentials = true;

req.onload = () => {
if (req.status === 200) {
resolve();
} else {
reject();
}
};

req.send(null);
});
};

const highPriFetchStrategy = function (url) {
return new Promise((resolve, reject) => {
// TODO: Investigate using preload for high-priority
// fetches. May have to sniff file-extension to provide
// valid 'as' values. In the future, we may be able to
// use Priority Hints here.
if (self.fetch === undefined) {
xhrPrefetchStrategy(url)
.then(() => {
resolve();
});
} else {
// As of 2018, fetch() is high-priority in Chrome
// and medium-priority in Safari.
fetch(url, {credentials: `include`})
.then(() => {
resolve();
});
}
});
};

const supportedPrefetchStrategy = support(`prefetch`)
Expand All @@ -36,22 +81,35 @@ const supportedPrefetchStrategy = support(`prefetch`)

const preFetched = {};

const prefetch = function (url) {
if (preFetched[url]) {
return;
}
preFetched[url] = true;
supportedPrefetchStrategy(url);
const prefetch = function (url, priority) {
return new Promise(resolve => {
if (preFetched[url]) {
resolve();
return;
}

if (priority && priority === `high`) {
highPriFetchStrategy(url);
} else {
supportedPrefetchStrategy(url)
.then(() => {
resolve();
preFetched[url] = true;
})
.catch(() => { });
};
});
};

/**
* Prefetch an array of URLs using rel=prefetch
* if supported. Falls back to XHR otherwise.
* @param {Array} urls Array of URLs to prefetch
* @param {Array} urls - Array of URLs to prefetch
* @param {string} priority - "priority" of the request
*/
const prefetchLinks = function (urls) {
const prefetchLinks = function (urls, priority) {
urls.forEach(url => {
prefetch(url);
prefetch(url, priority);
});
};

Expand Down

0 comments on commit c4fb77a

Please sign in to comment.