-
Notifications
You must be signed in to change notification settings - Fork 432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
pre populate cache with views based on links with turbo_preload
attribute
#552
Conversation
turbo-preload
attributeturbo-preload
attribute
Thank you for opening this PR, the introduction of the I wonder, had you considered reading from an There are existing semantics for values like rel="prefetch" and rel="preload", which could pose opportunities for progressive enhancement while gracefully degrading to built-in browser support when JS is disabled or Turbo fails to load. |
src/core/drive/preloader.ts
Outdated
} | ||
} | ||
|
||
preloadURL(link: Element) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this method accepted HTMLAnchorElement as a type, we could read the link.href directly, without the need to call expandURL
or || ""
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback @seanpdoyle I'll have a go at refining the types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/core/drive/preloader.ts
Outdated
.then(res => res.text()) | ||
.then(responseText => PageSnapshot.fromHTMLString(responseText)) | ||
.then(snapshot => this.snapshotCache.put(url, snapshot)) | ||
.catch(() => {}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This codebase utilizes async
/await
syntax, which enables this to be implemented without chaining:
const response = await fetch(url.toString(), { headers: { 'VND.PREFETCH': 'true', 'Accept': 'text/html' } })
const responseText = await response.text()
const snapshot = PageSnapshot.fromHTMLString(responseText)
this.snapshotCache.put(url, snapshot)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that does seem to be the convention. I will tidy this up 👍. Thank you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point This is a great thought. The "prefetch" and "preload" types are not currently supported by anchor tags (link only) at this point. See this table for rel type support. But based on moz docs it appears it is at least being considered here. What is your thoughts on adopting this pre-emptively. I am open to the refactor if we think this is a good approach? EDIT: I have gone ahead and updated the PR to use |
src/core/drive/preloader.ts
Outdated
@@ -28,23 +27,25 @@ export class Preloader { | |||
const links = element.querySelectorAll('a[data-turbo-preload="load"]') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does executing this query with additional type information help avoid the instances
check?
for (const link of element.querySelectorAll<HTMLAnchorElement>('a[data-turbo-preload="load"]')) {
this.preloadURL(link)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolved in 15fedf0
turbo-preload
attribute
I'm concerned about Turbo re-purposing existing HTML attributes. Currently, On top of that, I'm not sure this feature is needed in Turbo. You can add |
TLDR – Preloading a preview gives you instant feel but you still load the most up to date content giving much better ergonomics!
@rik you are correct that they are not supported on anchors at this point. However mozilla's documentation demonstrates an openness to to accept it. Aside from this, the more I get in the weeds on this PR I am realizing there are some fundamental differences between the changes in this PR and
After my exploration I am not sure if
Basically the only way to use
|
turbo_preload
attribute
src/core/drive/preloader.ts
Outdated
|
||
export class Preloader { | ||
readonly delegate: PreloaderDelegate | ||
readonly selector: string = 'a[data-turbo-preload="true"]' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
readonly selector: string = 'a[data-turbo-preload="true"]' | |
readonly selector: string = 'a[data-turbo-preload]' |
I think this is the preferred style here when a truthy attribute is expected, for example see
Line 29 in 8221c1e
return [ ...this.element.querySelectorAll("[id][data-turbo-permanent]") ] |
9e3b3fc
to
520a132
Compare
520a132
to
f2b94bd
Compare
Hey turbo team, I wanted to circle back here as I have not stayed on top of this. I can say that we have been using this in production to preload our hottest app paths with success. I am wondering if there is interest in merging this feature or a revision of this? @seanpdoyle do you have any opinions? |
@dhh do you have any feedback on the concept here? |
Hoping this gets merged. Some of us have already implemented pre-fetching via 3rd party scripts mentioned in this old issue #174 would be great to have it built in. |
This is great. Agree it would be nice to lean on a href's support when that's there, but don't think we should get ahead of the spec. If it ends up happening, it wouldn't be a big deal to go from data-turbo-preload to whatever that is. But as with the link preloads, it might well be that the cache-control dynamics don't match with what we're trying to do here. |
Should we update https://github.com/hotwired/turbo-site to note this feature? Maybe as a new item in https://turbo.hotwired.dev/handbook/drive ? |
Yes please do add a PR we can ship with 7.2.0 👍 |
Thanks for taking the time to understand the PR @dhh. I think your analysis pretty spot on as well.
Good suggestion. I will put something together 👍 |
Are the linked pages all preloaded on page load or only when each link becomes visible? If not, I think that could be a great addition in a new PR. Using Intersection Observers. I think Next.js and Nuxt.js do that automatically. |
That could be a pretty cool idea @clementmas |
In that case I'd prefer to have links annotated with A different approach to this could be to implement the functionality in this PR using MutationObserver instead of preloading links after a frame visit or on document load. This would allow developers to define their own behavior on top, eg. by implementing an Interaction Observer that sets the It could also be used to add |
Yes I don't think there is any reason to remove the current functionality for our purpose I'm not sure we would need the intersection observer but I cannot see why we can't have both approaches. Having said that if we wanted to keep the API simple I don't see any downside to the intersection observer approach for us. E.g. most things we are preloading are defined in fixed navbars and they usually load in such a small time frame that the time between the link coming into frame, and pressing it, it would likely have already been loaded. |
First off, we ❤️ developing with hotwire and turbo at my workplace. So I want to thank all the contributors and the team.
Description
At my workplace we have released a new mobile application using hotwire and coming from a react native application one part of the UX that felt a bit less refined was the first 0-300ms of navigation. When navigating to a page for a second time turbo loads a snapshot from cache which makes for a pretty snappy (pun intended) experience. However for hot paths we wanted to make this the reality on first visits.
Prior Art
Before investigating this solution we did look at leveraging browser based prefetching. This can be achieved via the usage of
<link rel="prefetch" href="/app/path/here" />
. However our experience showed a couple ergonomic challenges using this for app screens:Cache-control
header.The implementation in this PR makes it possible to pre populate the cache by marking links with
data-turbo-preload
. which makes for better user experience without needing to worry about stale views.