Skip to content

Commit

Permalink
Try prefetching again using a Map<Promise> to avoid duplicate requests
Browse files Browse the repository at this point in the history
  • Loading branch information
jakewhiteley committed Jun 1, 2023
1 parent 19237aa commit ec0d74e
Showing 1 changed file with 43 additions and 4 deletions.
47 changes: 43 additions & 4 deletions src/Core.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const IN_PROGRESS = 'A transition is currently in progress'
/**
* @typedef CacheEntry
* @type {object}
* @property {typeof Renderer} renderer
* @property {typeof Renderer|Renderer} renderer
* @property {Document|Node} page
* @property {array} scripts
* @property {boolean} skipCache
Expand All @@ -30,12 +30,19 @@ export default class Core {
*/
cache = new Map()

/**
* @private
* @type {Map<string, Promise>}
*/
activePromises = new Map()

/**
* @param {{
* links?: string,
* removeOldContent?: boolean,
* allowInterruption?: boolean,
* bypassCache?: boolean,
* enablePrefetch?: boolean,
* renderers?: Object.<string, typeof Renderer>,
* transitions?: Object.<string, typeof Transition>,
* reloadJsFilter?: boolean|function(HTMLElement): boolean
Expand All @@ -47,6 +54,7 @@ export default class Core {
removeOldContent = true,
allowInterruption = false,
bypassCache = false,
enablePrefetch = true,
renderers = {
default: Renderer
},
Expand All @@ -65,6 +73,7 @@ export default class Core {
this.removeOldContent = removeOldContent
this.allowInterruption = allowInterruption
this.bypassCache = bypassCache
this.enablePrefetch = enablePrefetch
this.cache = new Map()
this.isPopping = false

Expand Down Expand Up @@ -190,15 +199,15 @@ export default class Core {
let navigationPromise

if (this.bypassCache || !this.cache.has(this.targetLocation.href) || this.cache.get(this.targetLocation.href).skipCache) {
const fetched = this.fetch(this.targetLocation.raw)
const fetched = this.fetch(this.targetLocation.href)
.then((newPage) => {
this.cache.set(this.targetLocation.href, this.createCacheEntry(newPage))
this.cache.get(this.targetLocation.href).renderer.createDom()
})

navigationPromise = this.beforeFetch(this.targetLocation, TransitionClass, trigger)
.then(async () => {
return fetched.then(async (newPage) => {
return fetched.then(async () => {
return await this.afterFetch(this.targetLocation, TransitionClass, this.cache.get(this.targetLocation.href), trigger)
})
})
Expand Down Expand Up @@ -333,6 +342,10 @@ export default class Core {
attachEvents(links) {
E.delegate('click', links, this.onClick)
E.on('popstate', window, this.onPopstate)

if (this.enablePrefetch) {
E.delegate('mouseenter focus', links, this.onPrefetch)

This comment has been minimized.

Copy link
@tobimori

tobimori Jun 1, 2023

Contributor

I'd add listening to the touchstart event, @jakewhiteley. It's also used in libraries like https://github.com/weebney/tachyon or instant.page. :)

This comment has been minimized.

Copy link
@jakewhiteley

jakewhiteley Jun 3, 2023

Author Member

Surely a touchstart is fractions of a ms before a click event fires for that link? Is it needed? @tobimori

This comment has been minimized.

Copy link
@tobimori

tobimori Jun 3, 2023

Contributor

yes, because it's touchend that starts the navigation. so there's about 50-100ms pre-navigation for mobile users too. you can read more about it on the sites of other preloading libraries like instant.page or tachyon.

}
}

/**
Expand Down Expand Up @@ -390,14 +403,33 @@ export default class Core {
this.navigateTo(window.location.href, false, 'popstate')
}

/**
* @private
* @param {MouseEvent} e
*/
onPrefetch = (e) => {
const target = processUrl(e.currentTarget.href)

if (this.currentLocation.host !== target.host) {
return
}

this.preload(e.currentTarget.href, false)
}

/**
* @private
* @param {string} url
* @param {boolean} [runFallback]
* @return {Promise<Document>}
*/
fetch(url, runFallback = true) {
return new Promise((resolve, reject) => {
// If Taxi is currently performing a fetch for the given URL, return that instead of starting a new request
if (this.activePromises.has(url)) {
return this.activePromises.get(url)
}

const request = new Promise((resolve, reject) => {
fetch(url, {
mode: 'same-origin',
method: 'GET',
Expand Down Expand Up @@ -425,7 +457,14 @@ export default class Core {
window.location.href = url
}
})
.finally(() => {
this.activePromises.delete(url)
})
})

this.activePromises.set(url, request)

return request
}

/**
Expand Down

0 comments on commit ec0d74e

Please sign in to comment.