-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
base: trunk
Are you sure you want to change the base?
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? |
…l page navigation is enabled
…sset 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.
This reverts commit 62e527e.
068d62f
to
06fd98d
Compare
Flaky tests detected in a9469d0. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/11016205655
|
What is the best way to test these changes? How much confidence can we put in the existing e2e tests coverage? |
Apologies, I've now added better testing instructions in the PR description. The existing e2e tests do not cover the scenarios in this PR. I have started working on additional e2e tests in #63268 I'm happy to continue working on them as an immediate follow-up. We'll need them before full-page client-side navigation is moved out of GB experiment. For now, as full-page client-side navigation is still experimental, I'm not worried about the impact of this PR. The current implementation has been somewhat broken for months (which this PR attempts to fix 🙂). |
} ); | ||
await fetchHeadAssets( document, headElements ); | ||
[].map.call( | ||
document.querySelectorAll( 'script[type="module"][src]' ), |
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.
That's intriguing, as it skips all scripts that aren't ES Modules moving forward. It never covered inline scripts, to there is no change regarding that.
What's the story for the plugins or themes that add custom JavaScript to the site? It will load on the full-page reload but not when doing the full-page client-side navigation. It would be good to start collecting the requirements for the feature to work correctly.
@@ -29,6 +37,14 @@ export const updateHead = async ( newHead: HTMLHeadElement[] ) => { | |||
} | |||
} | |||
|
|||
await Promise.all( | |||
[ ...headElements.entries() ] |
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 looks good, but I always worry about these data structures because I think of Map as an unordered collection. I was worried about dependencies and ordering not being respected:
The
entries()
method of Map instances returns a new map iterator object that contains the [key, value] pairs for each element in this map in insertion order.
It seems like we're OK since entries returns elements in insertion order 👍
We need to validate whether this PR fixes #63880. If not, then we should not need to backport this PR to WP 6.7, so the corresponding label will have to be removed. |
|
||
const headElement = headElements.get( href ); | ||
const styleElement = doc.createElement( 'style' ); | ||
styleElement.textContent = headElement.text; |
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.
Is it also necessary to store the text
? Couldn't headElements
be Map< string, HTMLElement >
, then this could become styleElement.textContent = headElement.textContent
?
I have found an issue (maybe introduced after merging Following the steps listed in the description, I see this error when I navigate to the "Image test" page:
After a bit of debugging, the cause of the problem seems to be inside the gutenberg/packages/interactivity/src/directives.tsx Lines 149 to 150 in 001241b
This is a quick fix we could use right after initialize the refs: if ( ! client.current ) {
client.current = proxifyState( ns, {} );
}
if ( ! server.current ) {
server.current = proxifyState( ns, {}, { readOnly: true } );
} However, this is weird, and would require further investigation. It could be an issue in Preact, or the way it reconciles elements during rendering. I have no idea at this moment. 😅 |
@michalczaplinski, I've added minor changes to the |
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