The team is now working on the WordPress Interactivity API. This unblocks the same UX Frontity framework enabled but directly in WordPress Core, fully compatible with the new Site Editor.
Right now prefetching has to be done manually by the theme developer. For example, when a post loads, it can use useEffect to prefetch the home.
We can add an autoPrefetch feature to the <Link> component so it does so automatically.
User Stories
As a Frontity developer
I want to auto prefetch the data of all the links that exist in the DOM
so that when they click, the route change is instant
As a Frontity developer
I want to auto prefetch the data when a readers hover on a link
so that when they click, the route change is instant
As a Frontity developer
I want to auto prefetch data of the links on the readersā screen
so that when they click, the route change is instant
Possible solution
It could have four modes:
"no": it doesnāt prefetch
"hover": it does prefetching when the reader hovers over the link.
"in-view": it does prefetching when the link enters the screen.
"all": it does prefetching when the link component is mounted.
That setting would have to be exposed for the final theme user. Maybe it could be in:
state.theme.autoPrefetch.
state.router.autoPrefetch (only if the Link component ends up in libraries.router).
state.components.linkPrefetch.
SUMMARY
Please, bear in mind that this section wasnāt part of the opening post. It has been added afterwards and its purpose is to keep a quick summary with the most relevant information about this FD at the top of the thread.
We can also use mouseout and touchstart for the "hover" option.
luisherranz3
I think that maybe, instead of doing the real prefetch with actions.source.fetch, we should add a <link prefetch src=""> with the URL of the API, so this prefetches donāt impact the performance.
We could do that with a new action, actions.source.prefetch. That way, when we do the route change and do the actual actions.source.fetch, the information is already there and it only has to process it (add it to the state and so on). If we do this, we avoid adding things to the state that are not needed yet.
1 Like
nicholasio.oliveira4
I think we should also be able to disable prefetching at the Link level.
Something like <Link prefetch={false}>Link</>
As a Frontity developer
I want to be able to disable prefetching for specific links
so that I have granular control of which links are worth prefetching
Just created a very basic draft PR. But it already has āallā and āin-viewā mode working.
1 Like
nicholasio.oliveira6
One benefit of actually running fetch and putting things on state is because we know exactly what we need to fetch, as an example, once we prefetch a category route we get back a list of posts. If we have a link to a post from that category in the current page we wonāt need to prefetch again, in my PR Iām relying on state.source.get to decide whether the link needs prefetching or not.
What do you think about prefetching in batches instead of using <link prefetch />? We could limit the max number of prefetches.
On another note any suggestion for mocking intersection observer and testing the in-view mode?
luisherranz7
To be honest, I donāt like the idea of using <link prefetch > that much either.
As you said, itās very convenient to have that data in the state as soon as possible, and on the other hand it wonāt work with requests that require an additional fetch, like for example getting the category ID when only the category slug is known to then fetch the posts from that category.
I prefer your idea of prefetching in batches. To avoid a performance hit on the Time to Interactive metric, maybe we could use requestIdleCallback instead of a setTimeout. It is now supported on all major browsers except Safari. That way, this should not affect the LightHouse score at all.
What do you think?
We could also use requestIdleCallback, or even Reactās new scheduler package, to populate the state once the data has been returned from the REST API. Iāll study that for the v2 of source.
A while ago I thought about that as well and I think the client logic to accomplish that would be too complex. There are two options though:
Create the @frontity/wpgraphl-package, which is something we want to do, and make sure that we combine the requests. Iām not an expert on GraphQL but my understanding is that it should be because GraphQL supports multiple queries in the same request.
This is something we want to after we release Source v2. The main drawback is that setting up a cache for GraphQL is way more complex than for a REST API.
Create a new REST API endpoint that supports multiple requests.
This is more of an idea for the future: create a @frontity/frontity-source package that uses the REST API but with a custom endpoint, like /wp-json/frontity/v1. That endpoint could have these features:
Get any entity only using the URL, to avoid the case where we now have to multiple requests (the example of the category I referred to earlier).
Accept multiple requests.
Merge all embeds (authors, categories, tags, media) together to avoid repetition.
{
"/category/one": [
// Posts of /category/one...
],
"/category/two": [
// Posts of /category/two...
],
"embeds": {
// All the embeds together...
}
}
Using the URLs in the REST API request will also be very convenient to do instant caching invalidations, because if a caching plugin is telling the CDN to purge the /category/one URL, it will also purge the REST API requests that contained it, like /entities?url[]=/category/one. I know itās probably not that simple, but at least it could be more similar to the way WordPress plugins invalidate the cache nowadays.
But of course, itāll take a while until we can do something like that
nicholasio.oliveira8
I agree, Iāll look into these options and update the PR
I agree it is much more complex than I initially thought. And I think if weāre going to officially support a wpgraphql-package it does not make sense to do that when using the rest-api as wpgraphql solves that by design.
I donāt really like the idea of creating and maintaining a new rest API endpoint, we have done that to solve similar issues at 10up but we found ourselves spending too much time maintaining an unofficial implementation and thereās also the fact that frontity wouldnāt just work out of the box with WordPress. If people using frontity care enough about these issues they should be able to switch to GraphQL instead which already has a well-established plugin.
āin-viewā: Only prefetch links that are currently visible in the viewport. (recommended)
āallā: Prefetches all internal links on the page.
āhoverā: Prefetches links on hover.
ānoā: No auto prefetch.
The prefetch mode should be set in state.theme.autoPrefetch.
As mentioned in the PR, we havenāt added the requestIdleCallback optimization at this point, but we can explore that in the future:
There are some other things for us to consider like adding a shim for it. TypeScript also does not recognize requestIdleCallback as it is officially experimental so we would have to figure that out as well. It doesnāt feel right to add a shim and typescript definitions for this in the Link component so Iād rather do that a part of a new FD.
@nicholasio.oliveira while investigating a TypeScript issue when we merge our package types, I noticed that the <Link> component didnāt have any types, so I added them in this PR, in case you want to take a look. They are similar to the types use for packages, we import the types of all the packages the <Link> component needs to know about.
I also moved the component into three files because it was getting a bit too big, and I refactored the process queue to a class. I hope you donāt mind
nicholasio.oliveira17
@luisherranz Looks good to me! (Just left some comments in the PR). Thanks for the ping!