-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Interactivity API: Improvements to the experimental full-page navigation #64067
Interactivity API: Improvements to the experimental full-page navigation #64067
Conversation
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
What functionality or change from the other pull request does this one need to work? |
Sorry, this comment is outdated and I've removed it now. I made it before updating this PR . We don't need any specific functionality from that PR. |
I added a comment here about the general strategy of preloading and waiting for resources to load. |
I've just noticed that this: // wait for the `load` event to fire before appending the element
return new Promise( ( resolve, reject ) => {
element.onload = resolve;
element.onerror = reject;
} ); actually breaks the full-page navigation. We can't rely on the |
Just coming back to this. The reason I was seeing breakage is because the const script = document.createElement('script');
script.textContent = 'console.log("hi");'
script.onload = () => { console.log("loaded")}; // This will log
document.body.append(script); I'm trying a different approach now:
|
As I mentioned here, you don't need to add an actual script and use the |
export const headElements = new Map< | ||
string, | ||
{ tag: HTMLElement; text?: string } | ||
>(); |
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.
I've exported the headElements
so that we don't have to pass it as an argument to fetchHeadAssets()
.
[ ...headElements.entries() ] | ||
.filter( ( [ , { tag } ] ) => tag.nodeName === 'SCRIPT' ) | ||
.map( async ( [ url ] ) => { | ||
await import( /* webpackIgnore: true */ url ); |
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.
We need the webpack comment here. Otherwise, webpack will try to code-split here into a new chunk and it does not permit using fully dynamic paths.
Yup, this is what I ended up doing. I think there's nothing wrong with adding the scripts and waiting for the I think this is ready for review now. There are 2 issues that I've noticed that should be fixed in follow-ups:
|
You should test it with |
Nice, I didn't think about it, thanks! 🙂 |
ok, I've just tried it and it looks like setting |
This is the reason it uses gutenberg/lib/interactivity-api.php Line 13 in 0e88cda
I don't know where the |
Thanks @gziolo I figured out that gutenberg/bin/generate-gutenberg-php.php Lines 22 to 23 in fb5fa24
For production, there is no problem in that case. In development, we should check if the full-page navigation experiment is enabled. And use the |
Won't that cause problems for people developing in Gutenberg with that option enabled? |
I think that if you are developing with the full-page navigation enabled, then you DO want to disable the cache busting. Otherwise, full-page navigation does not work properly. This is how I did it: gutenberg/lib/interactivity-api.php Lines 17 to 25 in bcb3ff8
Am I missing something? |
Thank you @sirreal ! 🙏 |
There are several follow-up items from this PR that I don't want to slip through the cracks. Are they tracked anywhere? |
Yes, we knew this from the beginning 🙂 My idea is to use something like this for the modules that we load in the new pages. EDIT: If you're curious about how it works, it's based on the es-module-lexer package. |
…ion (WordPress#64067) * fix: Add initial data population in interactivity router * chore: Update element.innerText to element.textContent in head.ts file * feat: Exclude src and href attributes when copying head element attributes * chore: Populate initial data in interactivity router * chore: Move Populate initial data in interactivity router * Wait for `load` event of script element before returning from `fetchHeadAssets()` function * feat: Update head tags to improve prefetching of scripts and stylesheets This commit modifies the `updateHead` function in `head.ts` to improve support for lazy loading of scripts and stylesheets. It preloades the script modules using `modulepreload`, imports the necessary scripts using dynamic imports and adds the `preload` link elements for stylesheets. * Do not load interactivity script modules in development mode when full page navigation is enabled * Format interactivity-api.php * Update interactivity script module registration to use version from asset files - Added logic to retrieve version information from `index.min.asset.php` and `router.min.asset.php` files. - Updated `wp_register_script_module` calls to use the retrieved version instead of the default version when full-page navigation is not enabled. * empty commit * Rename populateInitialData to populateServerData * remove populateServerData * try: remove the webpack comment * try: remove the await import() * bring back the async import * Move headElements to head.ts * Revert "try: remove the webpack comment" This reverts commit 62e527e. * default_version => router_version * Remove the changes to interactivity-api.php * Make `renderRegions` async * Update TS type of the stylesheets variable Co-authored-by: Jon Surrell <[email protected]> * Replace Array.from() with direct forEach() on NodeList in head.ts: * Check if href is null * Clarify why we only prefetch script modules * Add changelog --------- Co-authored-by: michalczaplinski <[email protected]> Co-authored-by: DAreRodz <[email protected]> Co-authored-by: sirreal <[email protected]> Co-authored-by: gziolo <[email protected]> Co-authored-by: luisherranz <[email protected]> Co-authored-by: westonruter <[email protected]>
Regarding the CSS jumbling up, I have a strong suspicion this has to do with the way global styles are generated per block. Those get a suffix generated by I encountered this issue with Swup (a JS framework that is remarkably similar to what the Interactivity API is doing), and 'fixed' it with this incredibly hacky hack: if(is_wp_version_compatible("6.5")) {
add_filter('sanitize_title', function($title) {
if(preg_match("/core-/", $title)) {
$rand = rand(0, 1000);
for($i=0;$i<$rand;$i++) {
wp_unique_prefixed_id("wp-container-$title-is-layout-");
}
}
return $title;
});
} A more proper fix could involve a filter to be able to change those suffixes—there is not one, at the moment. Such a filter could then be called when the Interactivity API is active. |
This PR brings several improvements to experimental full-page navigation.
Fixes #63880
1. Remove
src
attributes from prefetched scriptsWe have to remove the
src
attributes of the prefetched scripts because otherwise the contents are ignored. Reported by @luisherranz in #63880 (comment).2. Use
.textContent
instead of.innerText
to set<script>
contentsPrefetched scripts that are inserted in the the
<head>
have<br>
in the DOM:The article from MDN about
innerText
explains why we see this problem:3.
populateInitialData()
with state from the serverWe have to populate the client-side state with the state coming from the server (as mentioned in this comment:
4. Wait for
load
event of thescript
elementFinally, there is another issue:
Scripts that are added to the DOM with
type="module"
are deferred by default! This means that if we add a script to the<head>
the way we currently do it is not guaranteed to have executed before the HTML of the page is updated.Notice how the DOM had been updated before the
onload
handler fires. The error at the end that is caused by it because the JS store that contains thecallbacks.initTriggerButton
has not yet loaded on the page.output_87404e.mp4
The solution proposed in this PR follows this comment:
<link>
elements withmodulepreload
instead of fetching the modules ourselves.import()
to evaluate the scripts.Testing instructions
You can test this PR manually by using blocks that use the Interactivity API and enabling the client-side navigation in GB settings.
Some things to watch out for:
Example test
Image test
post.Image test
post.Image test
post.Several follow-ups should be done:
Figure out the way to only load the scripts for the new & compatible interactive blocks on the page. Previously mentioned in Image block view script causes errors when client-side navigation is enabled #63880 (comment) & Experiment: Add full page client-side navigation experiment setting #59707 (comment)
Refactor the
fetchHeadAssets()
function so that it's not called inside ofregionsToVdom
.Handle CSS asset loading (not done in this PR):
<style>
tags messes up with how the URLs are interpreted in CSS'surl()
function: Interactivity API: Improvements to the experimental full-page navigation #64067 (comment)Occasionally the CSS for the page after a navigation is messed up (see video). I'm not sure if this is due to missing CSS or or file order or something else.
output_a052aa.mp4