Giter Site home page Giter Site logo

googlechromelabs / quicklink Goto Github PK

View Code? Open in Web Editor NEW
10.9K 98.0 398.0 12.34 MB

⚡️Faster subsequent page-loads by prefetching in-viewport links during idle time

Home Page: https://getquick.link

License: Apache License 2.0

JavaScript 92.10% SCSS 7.90%
performance web-performance prefetch prefetcher speed

quicklink's Introduction


npm gzip size ci

quicklink

Faster subsequent page-loads by prefetching or prerendering in-viewport links during idle time

How it works

Quicklink attempts to make navigations to subsequent pages load faster. It:

  • Detects links within the viewport (using Intersection Observer)
  • Waits until the browser is idle (using requestIdleCallback)
  • Checks if the user isn't on a slow connection (using navigator.connection.effectiveType) or has data-saver enabled (using navigator.connection.saveData)
  • Prefetches (using <link rel=prefetch> or XHR) or prerenders (using Speculation Rules API) URLs to the links. Provides some control over the request priority (can switch to fetch() if supported).

Why

This project aims to be a drop-in solution for sites to prefetch or prerender links based on what is in the user's viewport. It also aims to be small (< 2KB minified/gzipped).

Multi page apps

Installation

For use with Node.js and npm:

npm install quicklink

You can also grab quicklink from unpkg.com/quicklink.

Usage

Once initialized, quicklink will automatically prefetch URLs for links that are in-viewport during idle time.

Quickstart:

<!-- Include quicklink from dist -->
<script src="dist/quicklink.umd.js"></script>
<!-- Initialize (you can do this whenever you want) -->
<script>
  quicklink.listen();
</script>

For example, you can initialize after the load event fires:

<script>
  window.addEventListener('load', () => {
    quicklink.listen();
  });
</script>

ES Module import:

import {listen, prefetch} from 'quicklink';

Single page apps (React)

Installation

First, install the packages with Node.js and npm:

npm install quicklink webpack-route-manifest --save-dev

Then, configure Webpack route manifest into your project, as explained here. This will generate a map of routes and chunks called rmanifest.json. It can be obtained at:

  • URL: site_url/rmanifest.json
  • Window object: window.__rmanifest

Usage

Import quicklink React HOC where want to add prefetching functionality. Wrap your routes with the withQuicklink() HOC.

Example:

import {withQuicklink} from 'quicklink/dist/react/hoc.js';

const options = {
  origins: [],
};

<Suspense fallback={<div>Loading...</div>}>
  <Route path='/' exact component={withQuicklink(Home, options)} />
  <Route path='/blog' exact component={withQuicklink(Blog, options)} />
  <Route path='/blog/:title' component={withQuicklink(Article, options)} />
  <Route path='/about' exact component={withQuicklink(About, options)} />
</Suspense>;

API

quicklink.listen(options)

Returns: Function

A "reset" function is returned, which will empty the active IntersectionObserver and the cache of URLs that have already been prefetched or prerendered. This can be used between page navigations and/or when significant DOM changes have occurred.

options.prerender

  • Type: Boolean
  • Default: false

Whether to switch from the default prefetching mode to the prerendering mode for the links inside the viewport.

Note: The prerendering mode (when this option is set to true) will fallback to the prefetching mode if the browser does not support prerender.

options.prerenderAndPrefetch

  • Type: Boolean
  • Default: false

Whether to activate both the prefetching and prerendering mode at the same time.

options.delay

  • Type: Number
  • Default: 0

The amount of time each link needs to stay inside the viewport before being prefetched, in milliseconds.

options.el

  • Type: HTMLElement|NodeList<A>
  • Default: document.body

The DOM element to observe for in-viewport links to prefetch or the NodeList of Anchor Elements.

options.limit

  • Type: Number
  • Default: Infinity

The total requests that can be prefetched or prerendered while observing the options.el container.

options.threshold

  • Type: Number
  • Default: 0

The area percentage of each link that must have entered the viewport to be fetched, in its decimal form (e.g. 0.25 = 25%).

options.throttle

  • Type: Number
  • Default: Infinity

The concurrency limit for simultaneous requests while observing the options.el container.

options.timeout

  • Type: Number
  • Default: 2000

The requestIdleCallback timeout, in milliseconds.

Note: The browser must be idle for the configured duration before prefetching.

options.timeoutFn

  • Type: Function
  • Default: requestIdleCallback

A function used for specifying a timeout delay.

This can be swapped out for a custom function like networkIdleCallback (see demos).

By default, this uses requestIdleCallback or the embedded polyfill.

options.priority

  • Type: Boolean
  • Default: false

Whether or not the URLs within the options.el container should be treated as high priority.

When true, quicklink will attempt to use the fetch() API if supported (rather than link[rel=prefetch]).

options.origins

  • Type: Array<String>
  • Default: [location.hostname]

A static array of URL hostnames that are allowed to be prefetched.

Defaults to the same domain origin, which prevents any cross-origin requests.

Important: An empty array ([]) allows all origins to be prefetched.

options.ignores

  • Type: RegExp or Function or Array
  • Default: []

Determine if a URL should be prefetched.

When a RegExp tests positive, a Function returns true, or an Array contains the string, then the URL is not prefetched.

Note: An Array may contain String, RegExp, or Function values.

Important: This logic is executed after origin matching!

options.onError

  • Type: Function
  • Default: None

An optional error handler that will receive any errors from prefetched requests.

By default, these errors are silently ignored.

options.hrefFn

  • Type: Function
  • Default: None

An optional function to generate the URL to prefetch. It receives an Element as the argument.

quicklink.prefetch(urls, isPriority)

Returns: Promise

The urls provided are always passed through Promise.all, which means the result will always resolve to an Array.

Important: You much catch you own request error(s).

urls

  • Type: String or Array<String>
  • Required: true

One or many URLs to be prefetched.

Note: Each url value is resolved from the current location.

isPriority

  • Type: Boolean
  • Default: false

Whether or not the URL(s) should be treated as "high priority" targets.

By default, calls to prefetch() are low priority.

Note: This behaves identically to listen()'s priority option.

quicklink.prerender(urls)

Returns: Promise

Important: You much catch you own request error(s).

urls

  • Type: String or Array<String>
  • Required: true

One or many URLs to be prerendered.

Note: Speculative Rules API supports same-site cross origin Prerendering with opt-in header.

Polyfills

quicklink:

  • Includes a very small fallback for requestIdleCallback
  • Requires IntersectionObserver to be supported (see Can I Use). We recommend conditionally polyfilling this feature with a service like Polyfill.io:
<script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver"></script>

Alternatively, see the Intersection Observer polyfill.

Recipes

Set a custom timeout for prefetching resources

Defaults to 2 seconds (via requestIdleCallback). Here we override it to 4 seconds:

quicklink.listen({
  timeout: 4000,
});

Set a specific Anchor Elements NodeList to observe for in-viewport links

Defaults to document otherwise.

quicklink.listen({
  el: document.querySelectorAll('a.linksToPrefetch'),
});

Set the DOM element to observe for in-viewport links

Defaults to document otherwise.

quicklink.listen({
  el: document.getElementById('carousel'),
});

Programmatically prefetch() URLs

If you would prefer to provide a static list of URLs to be prefetched, instead of detecting those in-viewport, customizing URLs is supported.

// Single URL
quicklink.prefetch('2.html');

// Multiple URLs
quicklink.prefetch(['2.html', '3.html', '4.js']);

// Multiple URLs, with high priority
// Note: Can also be use with single URL!
quicklink.prefetch(['2.html', '3.html', '4.js'], true);

Programmatically prerender() URLs

If you would prefer to provide a static list of URLs to be prerendered, instead of detecting those in-viewport, customizing URLs is supported.

// Single URL
quicklink.prerender('2.html');

// Multiple URLs
quicklink.prerender(['2.html', '3.html', '4.js']);

Set the request priority for prefetches while scrolling

Defaults to low-priority (rel=prefetch or XHR). For high-priority (priority: true), attempts to use fetch() or falls back to XHR.

Note: This runs prefetch(..., true) with URLs found within the options.el container.

quicklink.listen({priority: true});

Specify a custom list of allowed origins

Provide a list of hostnames that should be prefetch-able. Only the same origin is allowed by default.

Important: You must also include your own hostname!

quicklink.listen({
  origins: [
    // add mine
    'my-website.com',
    'api.my-website.com',
    // add third-parties
    'other-website.com',
    'example.com',
    // ...
  ],
});

Allow all origins

Enables all cross-origin requests to be made.

Note: You may run into CORB and CORS issues!

quicklink.listen({
  origins: true,
  // or
  origins: [],
});

Custom Ignore Patterns

These filters run after the origins matching has run. Ignores can be useful for avoiding large file downloads or for responding to DOM attributes dynamically.

// Same-origin restraint is enabled by default.
//
// This example will ignore all requests to:
//  - all "/api/*" pathnames
//  - all ".zip" extensions
//  - all <a> tags with "noprefetch" attribute
//
quicklink.listen({
  ignores: [
    /\/api\/?/,
    uri => uri.includes('.zip'),
    (uri, elem) => elem.hasAttribute('noprefetch'),
  ],
});

You may also wish to ignore prefetches to URLs which contain a URL fragment (e.g. index.html#top). This can be useful if you (1) are using anchors to headings in a page or (2) have URL fragments setup for a single-page application, and which to avoid firing prefetches for similar URLs.

Using ignores this can be achieved as follows:

quicklink.listen({
  ignores: [
    uri => uri.includes('#'),
    // or RegExp: /#(.+)/
    // or element matching: (uri, elem) => !!elem.hash
  ],
});

Custom URL to prefetch via hrefFn callback

The hrefFn method allows to build the URL to prefetch (e.g. API endpoint) on the fly instead of the prefetching the href attribute URL.

quicklink.listen({
  hrefFn(element) {
    return element.href.replace('html', 'json');
  },
});

Browser Support

The prefetching provided by quicklink can be viewed as a progressive enhancement. Cross-browser support is as follows:

  • Without polyfills: Chrome, Safari ≥ 12.1, Firefox, Edge, Opera, Android Browser, Samsung Internet.
  • With Intersection Observer polyfill ~6KB gzipped/minified: Safari ≤ 12.0, IE11
  • With the above and a Set() and Array.from polyfill: IE9 and IE10. Core.js provides both Set() and Array.from() shims. Projects like es6-shim are an alternative you can consider.

Certain features have layered support:

  • The Network Information API, which is used to check if the user has a slow effective connection type (via navigator.connection.effectiveType) is only available in Chrome 61+ and Opera 57+
  • If opting for {priority: true} and the Fetch API isn't available, XHR will be used instead.

Using the prefetcher directly

A prefetch method can be individually imported for use in other projects.

This method includes the logic to respect Data Saver and 2G connections. It also issues requests thru fetch(), XHRs, or link[rel=prefetch] depending on (a) the isPriority value and (b) the current browser's support.

After installing quicklink as a dependency, you can use it as follows:

<script type="module">
  import {prefetch} from 'quicklink';
  prefetch(['1.html', '2.html']).catch(error => {
    // Handle own errors
  });
</script>

Demo

Glitch demos

Research

Here's a WebPageTest run for our demo improving page-load performance by up to 4 seconds via quicklink's prefetching. A video comparison of the before/after prefetching is on YouTube.

For demo purposes, we deployed a version of the Google Blog on Firebase hosting. We then deployed another version of it, adding quicklink to the homepage and benchmarked navigating from the homepage to an article that was automatically prefetched. The prefetched version loaded faster.

Please note: this is by no means an exhaustive benchmark of the pros and cons of in-viewport link prefetching. Just a demo of the potential improvements the approach can offer. Your own mileage may heavily vary.

Additional notes

Session Stitching

Cross-origin prefetching (e.g a.com/foo.html prefetches b.com/bar.html) has a number of limitations. One such limitation is with session-stitching. b.com may expect a.com's navigation requests to include session information (e.g a temporary ID - e.g b.com/bar.html?hash=<>&timestamp=<>), where this information is used to customize the experience or log information to analytics. If session-stitching requires a timestamp in the URL, what is prefetched and stored in the HTTP cache may not be the same as the one the user ultimately navigates to. This introduces a challenge as it can result in double prefetches.

To workaround this problem, you can consider passing along session information via the ping attribute (separately) so the origin can stitch a session together asynchronously.

Ad-related considerations

Sites that rely on ads as a source of monetization should not prefetch ad-links, to avoid unintentionally counting clicks against those ad placements, which can lead to inflated Ad CTR (click-through-rate).

Ads appear on sites mostly in two ways:

  • Inside iframes: By default, most ad-servers render ads within iframes. In these cases, those ad-links won't be prefetched by Quicklink, unless a developer explicitly passes in the URL of an ads iframe. The reason is that the library look-up for in-viewport elements is restricted to those of the top-level origin.

  • Outside iframes:: In cases when the site shows same-origin ads, displayed in the top-level document (e.g. by hosting the ads themselves and by displaying the ads in the page directly), the developer needs to explicitly tell Quicklink to avoid prefetching these links. This can be achieved by passing the URL or subpath of the ad-link, or the element containing it to the custom ignore patterns list.

Related projects

License

Licensed under the Apache-2.0 license.

quicklink's People

Contributors

0o001 avatar addyosmani avatar anton-karlovskiy avatar coliff avatar demianrenzulli avatar dependabot[bot] avatar developit avatar dieulot avatar gilbertococchi avatar hadyan avatar hifaraz avatar jadjoubran avatar jonchenn avatar jothy1023 avatar leepowelldev avatar lete114 avatar luehrsen avatar lukeed avatar mgechev avatar mzaini30 avatar neo avatar rafaelstz avatar rangerz avatar rayrutjes avatar styfle avatar surma avatar tatchi avatar udassi-pawan avatar verlok avatar xhmikosr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

quicklink's Issues

Usage with barba/turbolinks/etc

Heyo..

This looks super neat.

I'd like to be able to use this alongside something like barba.js to handle page transitions.

If I add an event listener to all of my links, would there be a way of checking to see if the url has already been downloaded, and if so trigger the page transition, and update the dom, without having to make another xhr request?

function onClick(e) {
  // magic code
  if(cached(e)) {
    startAnimation(cached(e).html)
    return
   }

   fetch(e.pathname).then(etc => etc(etc)).then((html) => startAnimation(html))
}

any thoughts?

thanks

Add demos using `quicklink`

These are already deployed with before/afters on WebPageTest. Just need to decide whether to include them in the README or wiki.

Does link prefetching work with target="_blank"?

I added quicklink to a Chrome extension I have. The thing is, all of my links have the target="_blank" attribute, because that's the only way to open up links from the extension popup (otherwise, you click on them and nothing happens).

So far it's adding the correctly for each of links, but I don't notice a speed improvement when I click on them.

My question is: is it possible that when a prefetch link is opened in a different tab, the browser simply forgets the fetching?

Not working on Firefox v65

Hello,

I've tested quicklink using the test/ page with Chrome/Chromium which works very well, however, it doesn't work at all with Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0 (On Linux Mint 19.1 Tessa), both the current latest 8c007ab and v1.0.0, and there is no error or warning message in the developer tool console. I haven't tested on other environments yet.

Not sure if anyone else has the same issue and what should I do to help locate that issue, let me know if I missed anything or how can I help please, thank you!

Option to ignore certain URLs by extension

Doing a quick and dirty test of this library on my personal site, I have noticed that (possibly large) zip download files are being fetched, too... it would be interesting to be able to restrict which kind of URLs would be fetched.

Unwanted behavior of the library

Hello,

I just realized something after noticing a "bug" on my site using Laravel:

The pages of the site using the library "pre-load", the php sessions record this load and propel unwanted behaviors.

For example, Laravel's back() function, which redirects to the previous page, redirects the user to a random page because it is pre-loaded by the browser.

It will be necessary to be able to say to the backend not to record this pre-loading.

Facilitate development of plugin in existing WordPress dev environment

It is difficult to hack on this plugin on an existing WordPress development environment. For example, Gutenberg, AMP, and PWA plugins all can just be cloned into wp-content/plugins of any existing WordPress install and then with npm install and npm run build they can be activated in-place.

Can that be implemented for Quicklink as well? Part of this would include making setup-local-env.sh optional.

Ignore elements?

Can you ignore specific elements? "Quicklink everything but [...]" case

how about fix it about cors?

"No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."
QQ截图20190406150429

Feature request: ability to set rough link fetching order

I would love the ability to set a rough "order" on the prefetching of links through something like:

quicklink({
  order: [
    (uri, elem) => {
      const importantSelector = '.primary a';
      if (elem.matches(importantSelector)) return 0; // Zero would indicate super important;
    }
  ]
});

image

Add option to ignore href with target="_blank"

First thank you for this nice plugin.

Something that could improve it is an option to ignore all link that redirect outside of the current website.

It could be done with a new option to ignore link href with target="_blank"

Cheers

IE11 doesn't seem to play ball with quicklink();

So, I've been having some fun with this over the past couple of days - everything is fine and dandy in Chrome - I'm yet to test other browsers such as Safari and Firefox!
However on IE11, as soon as I call quicklink(); (after polyfilling!) IE just stops completely.
Any ideas/pointers as to where I'm going wrong!

//This function is called if InterserctionObserver is not natively 
//supported by the browser. The function takes 1 argument - URL.
//It is at this point that we polyfill the feature using polyfill.io
//based on the URL checkIntersectionObserver passes on line 49.
function loadIntersectionObserver(url) {
	return new Promise(function(resolve, reject) { 
		var script = document.createElement('script'); 
		script.src = url; 
		document.getElementsByTagName('body')[0].appendChild(script); 
		console.log('Grabbing IntersectionObserver polyfill from: ', script.src);
		script.onload = resolve;
		script.onerror = reject; 
	});
}
function loadquicklink() {
	return new Promise(function(resolve, reject) { 
			var qlscript = document.createElement('script'); 
			qlscript.src = 'https://cdn.jsdelivr.net/npm/[email protected]/dist/quicklink.umd.js'; 
			qlscript.defer = true; 
			document.getElementsByTagName('body')[0].appendChild(qlscript);
			console.log('Grabbing quicklink JS!')
			qlscript.onload = resolve;
			qlscript.onerror = reject; 
		});
}
//This function checks whether we need to polyfill IntersectionObserver or not
//If we don't, we can use the native API. If we do need to, then we go to the 
//loadIntersectionObserver function to polyfill the functionality.
function checkIntersectionObserver() {
	if ( 
		'IntersectionObserver' in window && 
		'IntersectionObserverEntry' in window 
	) {
		console.log( 'Nice one - your browser supports IntersectionObserver. No polyfills necessary!' );
		return loadquicklink();
	} else { 
		console.log( "Ahhh, your browser doesn't support IntersectionObserver. I'm gonna have to polyfill it so stuff works. Hang on one sec!" );
		return loadIntersectionObserver(
			'https://polyfill.io/v2/polyfill.js?features=IntersectionObserver&callback=loadquicklink' 
		);
	}
}
window.onload = function quicklinkloaded() {
	checkIntersectionObserver().then( 
		function() { 
			console.log( 'Aha! Everything is ready! Calling quicklink to get potential future pages ready for you to visit in advance!' );
			quicklink(); 
			console.log( "Et voila! We're now prefetching things you might click on - hopefully that speeds things up for you!!!" );
		},
		function() { 
			console.log( "So there was an issue polyfilling IntersectionObserver, which means that I can't preload future pages for you. Sorry! :(" );
			console.log( "If you want this to work, I'd recommend upgrading to a browser that supports IntersectionObserver natively. You can find out which browsers do by visting: https://caniuse.com/#feat=intersectionobserver" );
		}
	);
}

Note: Please excuse my messy code - this one is a learning curve for me and I'm fairly new to JS :)

Requests blocked because of mixed content

When using this locally it works great, but when in production, the prefetched links get blocked because of 'mixed content' and I get the 'This page is trying to load scripts from unauthenticated sources' alert.

This comes up in the console:
Mixed Content: The page at 'https://livi.com.ar/evaluacion?id=24' was loaded over HTTPS, but requested an insecure prefetch resource 'http://livi.com.ar/evaluacion?id=14'. This request has been blocked; the content must be served over HTTPS.

Any help?

Quicklink is not actually IUU

In the Idle-Until-Urgent (IUU) pattern you delay loading a resource using requestIdleCallback (or similar). But when a resource becomes urgent, you short-circuit rIC and load it immediately.

AFAIU, quicklink is supposed to use Intersection Observer to determine when a resource becomes urgent. The current code, however, only start observing elements in rIC. Those two need to get decoupled.

Happy to take this once #9 lands.

Still fetch ignored URL

First I tried this to exclude a element:

const elem = document.querySelectorAll(':not(.post-copyright-link)');

But I got this error:

Uncaught TypeError: e.el.querySelectorAll is not a function

So I put code into ignores, here's all my script:

<script src="dist/quicklink.umd.js"></script>
<script>
  window.addEventListener('load', () => {
    const elem = document;
    quicklink({
      el: elem,
      priority: true,
      ignores: [
        uri => uri.includes('#'),
        uri => uri.includes('.xml'),
        (uri, elem) => elem.querySelector('li.post-copyright-link')
      ]
    });
  });
</script>

There's no error but still fetch ignored URL in li.post-copyright-link, how can I solve it ?

net::ERR_UNKNOWN_URL_SCHEME

Hi, I'm having an issue with quicklinks.umd when using tel links. When a tel link is used the console error is then produced:

net::ERR_UNKNOWN_URL_SCHEME

I've tried using the ignores but I just can't get it to not include tel or mailto links.

<script id="quicklink">
	window.addEventListener('load', () =>{
		quicklink({
			timeout: 4000,
			ignores: [
				/tel:/g,
				/#(.+)/,
                                 uri => uri.includes('tel:')
				(uri, elem) => elem.hasAttribute('noprefetch')
			]
		});
	});
</script>

I just can't seem to figure it out. Any help would be appricated. Thanks

Facilitate async loading of quicklink.umd.js

Currently the readme suggests to include the script into the page via:

<!-- Include quicklink from dist -->
<script src="dist/quicklink.umd.js"></script>
<!-- Initialize (you can do this whenever you want) -->
<script>
quicklink();
</script>

or at load event via:

<script>
window.addEventListener('load', () =>{
   quicklink();
});
</script>

However, shouldn't the quicklink.umd.js script be loaded with async to begin with? The blocking script is not good for performance.

Ideally you should be able to do something like this, similar to the AMP shadow doc API:

<script async src="dist/quicklink.umd.js"></script>

<script>
(window.quicklink = window.quicklink || []).push(function(quicklink) {
  quicklink();
});
// Or...
window.addEventListener('load', () =>{
  (window.quicklink = window.quicklink || []).push(function(quicklink) {
    quicklink();
  });
});
</script>

Why not a chrome plugin?

I am wondering why there is no chrome plugin that would load this on any page? Am I overseeing something?

`prefetch` in combination of `Cache-Control`

Is it necessary to set the Cache-Control Header to a specifc value to take advantage of prefetching?

On the demo Site https://keyword-2-ecd7b.firebaseapp.com/ I can see a Cache-Control Header of max-age=3600. Would quicklink also work with a Cache-Control: no-cache header?

Consider allowing quicklink to fail softly if IntersectionObserver isn't available.

I just recently launched a new web site that uses quicklink. It's impressive, and improves the perception of speed to a point where a static website feels almost like an SPA at times. Good work!

One thing I noticed, though, is that quicklink crashes when Intersection Observer is not available. I'm aware that the documentation recommends a polyfill, but do you think it would make sense to fail softly when Intersection Observer is unavailable? Perhaps using this check and using console.warn?

In any event, great work on this. This script has tremendous utility in improving perceived performance, and I look forward to using it in future projects.

ignore configuration filter can be bypassed by links sharing survivors urls

Not sure it is a bug but i found the api to be misleading:

I expect the ignore configuration to filter the links to observe and so to limit to the survivors the "in viewport" mechanism.
It seems to be not exactly the case: any links sharing the same url that a link that was not ignore by the configuration will be observed.

For example, I used the ignore function receiving an element (uri, elem) => {...} to apply prefetching mechanism only on link having a specific attribute.
So it allows me to decide which link should be prefetching by adding a dedicated attribute.

On my page I have 2 links redirecting to the very same url.
The first one is in my header (not visible by default) and the second one is in my footer.
I decided to put my prefetching attribute only on the link in the footer.

But reloading the page, the prefetching is always done even if my footer link is not in the viewport because the link in the header (having not the dedicated attribute and so that should have be ignored) has the same url...

I could force my intended behavior by specifying a region to observe (the footer) but it adds some complexity and code more difficult to maintain.

So I expect a link that doesn't survive the ignore test to not be observed

Unobserve prefetched elements

Depending on the number of links on a page, or if new links are added programmatically after the initial call. Both the query selector in the timeout method https://github.com/GoogleChromeLabs/quicklink/blob/master/src/index.mjs#L70, and/or the entries loop in the observer callback https://github.com/GoogleChromeLabs/quicklink/blob/master/src/index.mjs#L23, could be problematic for subsequent calls.

I think it would be worth to unobserve elements that are already prefetched.

Additionally, instead of requiring additional calls to quicklink({ urls: [..]}), which I think it is still needed to override the intersection observer; a mutationobserver.mjs could be enabled through an option, or to keep the lib size down, expose it in dist and allow its default export to be passed as an option for runtime.

Let me know what you think and I can tackle these. 👍
Thanks!

Ability to exclude some links from prefetching

Thank you for such a nice library :)

One of the links which are visible on my website is /logout link. As you can imagine this link log me out from the web application. Is there ability to exclude that link from being prefetched 😄?

shouldn't window.addEventListener (in readme) use passive and/or once ?

As it doesn't use Event.preventDefault(), it should use the passive boolean to save a few cpu time.

cf: https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners

I am less sure, but quicklink() is called once and if there is a full reload, the addeventlistener is redownloaded.
So the once boolean should be used ? (it will automatically remove the listerner after it as been called once.
cf: https://developers.google.com/web/updates/2016/10/addeventlistener-once

So the readme should have:
window.addEventListener('load', {passive: true, once: true}, () =>{
quicklink();
});

(I may be wrong !)

Does not work as a traditional JS resource – Uncaught ReferenceError: module is not defined

Hello, this looks like a very handy library. However, I'm having trouble using it without using it as an ES-module.

My source code:

<script src="https://unpkg.com/[email protected]/dist/quicklink.js"></script>
<script>
  quicklink();
</script>

The output:

quicklink.js:1 Uncaught ReferenceError: module is not defined
    at quicklink.js:1
(anonymous) @ quicklink.js:1
(index):142 Uncaught ReferenceError: quicklink is not defined
    at (index):142

Expected result
I would expect it to work since the readme says so.

Uncaught (in promise)

Hi,

I've included the umd version of quicklinks but I keep getting several console errors repeating the error:

Uncaught (in promise) Event {isTrusted: true, type: "error", target: link, currentTarget: null, eventPhase: 0, …}bubbles: falsecancelBubble: falsecancelable: falsecomposed: falsecurrentTarget: nulldefaultPrevented: falseeventPhase: 0isTrusted: truepath: (5) [link, head, html.js.no-focus-outline.sizes.customelements.history.pointerevents.postmessage.webgl.websockets.css…, document, Window]returnValue: truesrcElement: linktarget: linktimeStamp: 2088.500000001659type: "error"__proto__: Event
Promise.then (async)
r @ quicklink.umd.js:1
c @ quicklink.umd.js:1
(anonymous) @ quicklink.umd.js:1
(anonymous) @ quicklink.umd.js:1

Any advice with resolving these errors would be great. Thanks.

Set does not work in <IE11

Hello! 👋

Not flagging this to suggest it should work in anything below IE11, but the README currently says quicklink supports IE9+ as long as you have an Intersection Observer polyfill. However Set is not available until IE11. I think a user would need to have a Set polyfill in place to ensure it works that far back?

Apologies if I'm missing something! But wanted to flag just in case.

Normalize URLs before checking the `toPrefetch` set

For example, these three URLs are all equivalent:

https://www.google.com
https://www.google.com/
https:\\www.google.com/

(That last one is an extreme example, but it’s pretty common to see the first and second variation used interchangeably.)

When reading an element’s .href like in

const url = entry.target.href;
, you’d get the following normalized URL for each of those three:

https://www.google.com/

However, the same normalization is not applied when the user manually passes in URLs:

options.urls.forEach(prefetcher);

Because of this, the toPrefetch set currently ends up with three separate entries in this case, even though all of them normalize to the same URL:

quicklink({
   urls: [
    'https://www.google.com',
    'https://www.google.com/',
    'https:\\www.google.com/',
  ],
});

It can be solved by forcing URL normalization before calling prefetcher on this line:

options.urls.forEach(prefetcher);

-      options.urls.forEach(prefetcher);
+      options.urls.forEach(url => prefetcher(new URL(url).toString()));

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.