Skip to content
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

Improve Nav block loading UX by preloading Navigation menu requests #48683

Merged
merged 12 commits into from
May 11, 2023

Conversation

getdave
Copy link
Contributor

@getdave getdave commented Mar 2, 2023

What?

Preloads Navigation entities in the Site Editor.

Why?

We have lots of requests going out async for Navigation entities. This causes the block (and resultingly the editor) to feel very sluggish as these requests can take a while to resolve on slower connections.

The waterfall is:

  • request for all Navigation menus (needed to find a fallback for other things).
  • request for individual Navigation menu (by ID).

These also have OPTIONS requests which take time to resolve. By preloading these requests we improve the perceived performance in the editor as the requests are not dispatched.

How?

Preload the requests that cause network waterfalls for Navigation posts.

We can't preload the individual Nav block, but we can preload a list of all the Nav blocks and OPTIONS requests as well.

In the future, once we reference Navigation posts by slug then it will be possible to preload the Nav with the header slug which will allow us to have the primary Navigation menu available in editor fetch cache on editor being loaded.

⚠️ Potential Considerations

  • may increase TTFB
  • not all sites will use a Navigation block (i'd argue that 80% will).

Testing Instructions

⚠️ Some of the perceived performance may be difficult to perceive on fast environments. You may need to throttle your network speed in Chrome dev tools.

On trunk

  • Activate TT3 Theme.
  • Ensure you have at least one Navigation menu at http://localhost:8888/wp-admin/edit.php?post_type=wp_navigation
  • Open the Site Editor
  • See header template part load and once resolved, see a long additional loading state for the Navigation block itself.

On this branch

Do the same as the above but this time notice that once the template part is loaded there is almost no (or a reduced) loading state for the Nav block at all. The menu is almost instantly available.

Observe the network tab (filtered by "navigation") and notice that only a single requests is dispatched for OPTIONS for the single Navigation by ID. This is actually a request to get permissions on that Navigation.

Testing Instructions for Keyboard

Screenshots or screencast

Editor loading UX

Before

Notice how once the template part resolves the Navigation block shows its own loading state for quite a while before resolving to the menu:

Screen.Capture.on.2023-03-08.at.09-58-12.mp4

After

Notice how once the template part resolves the menu is almost instantly available in the Navigation block:

Screen.Capture.on.2023-03-08.at.09-56-57.mp4

Network timings

Before

Notice the request for the single Navigation menu is not dispatched until the requests for all Navigation Menus are resolved.

Screen Shot 2023-03-08 at 09 53 39

After

Now only a single OPTIONS request for the single Navigation Menu.

Screen Shot 2023-03-08 at 09 54 14

@github-actions
Copy link

github-actions bot commented Mar 2, 2023

Flaky tests detected in 1fb6fae.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/4313891258
📝 Reported issues:

@getdave
Copy link
Contributor Author

getdave commented Mar 8, 2023

@getdave getdave changed the title [WIP] Preload Navigation menu requests Improve Nav block loading UX by preloading Navigation menu requests Mar 8, 2023
@getdave getdave added [Type] Performance Related to performance efforts [Block] Navigation Affects the Navigation Block labels Mar 8, 2023
@getdave getdave self-assigned this Mar 8, 2023
@getdave
Copy link
Contributor Author

getdave commented Mar 8, 2023

Outstanding questions

  • Why are there x2 requests for all Menus. One for "draft & published" and one for just "published" 🤔
  • How can we assess the performance impact of these additional queries on the editor PHP page load time?
  • Is it possible to preload the OPTIONS request for the single menu? I think this is a request generated by permissions 🤔

@getdave getdave marked this pull request as ready for review March 8, 2023 12:01
@getdave getdave requested a review from spacedmonkey as a code owner March 8, 2023 12:01
@Mamaduka
Copy link
Member

Mamaduka commented Mar 8, 2023

This is a tricky problem since server-side preloading can affect TTFB. Preloading also will affect every editor, even if that page/post or template doesn't use the Navigation block.

There was an interesting conversation on a similar subject in this core PR WordPress/wordpress-develop#3894 (review). So maybe folks from the performance team could help us with the metrics.

Why are there x2 requests for all Menus. One for "draft & published" and one for just "published" 🤔

The first comes from the Site Editor's Navigation sidebar and the second from the Navigation block (that request is only used for fallback).

Is it possible to preload the OPTIONS request for the single menu? I think this is a request generated by permissions 🤔

Not on the server side since we need to know the ID(s) used in the editor.

@getdave
Copy link
Contributor Author

getdave commented Mar 8, 2023

This is a tricky problem since server-side preloading can affect TTFB

Yes this is what I was concerned about. You articulated it much better than me though!

My arguments would probably be that

  • TTFB is less jarring than network waterfall within the editor.
  • most sites will have a Nav block.

Not on the server side since we need to know the ID(s) used in the editor.

Once Nav blocks move to using slug to reference a menu then we might be able to do this. Depending on how the implementation goes.

@getdave
Copy link
Contributor Author

getdave commented Mar 8, 2023

Related #48924

@getdave getdave requested review from tyxla and swissspidy March 8, 2023 13:00
Copy link
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this, @getdave! It's always great to see performance improvements!

I've left a few questions, let me know what you think.

lib/compat/wordpress-6.3/navigation-block-preloading.php Outdated Show resolved Hide resolved
lib/compat/wordpress-6.3/navigation-block-preloading.php Outdated Show resolved Hide resolved
* @param array $preload_paths Preload paths to be filtered.
* @return array
*/
function gutenberg_preload_navigation_posts( $preload_paths, $context ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implies that regardless of the theme and current setup of the site, the site will always have a navigation menu. I think that's a false assertion - maybe the theme doesn't provide a navigation by default, or maybe the user has currently removed it from their site, or used a different block to set a navigation menu up. That way we'll always preload the wp_navigation posts, regardless of whether they're necessary.

Just imagine if the previous theme supported those and the site has a ton of those navigation posts, but the current theme doesn't make use of them. That will preload all those posts in vain.

Could be a longshot, but I was wondering if we could add some sort of a check to ensure that these navigation posts are actually going to be necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implies that regardless of the theme and current setup of the site, the site will always have a navigation menu. I think that's a false assertion...

That's certainly an argument that can be made. On the other hand you're probably talking about an 20% use case, with the other 80% of sites being almost certain to be using some kind of Nav menu.

That way we'll always preload the wp_navigation posts, regardless of whether they're necessary.

My question is how bad is it exactly for those 20% users?

Just imagine if the previous theme supported those and the site has a ton of those navigation posts, but the current theme doesn't make use of them. That will preload all those posts in vain.

This is certainly valid but very much an edge case IMHO. How does this weight against the majority of users who will experience a poor initial editor experience due to interdependent, network requests running in sequence and blocking the initial "load" state of the block (and thus the editor as a whole)?

Could be a longshot, but I was wondering if we could add some sort of a check to ensure that these navigation posts are actually going to be necessary?

Yes ideally we would do that. But I cannot see a way to do that without making more queries which is then self defeating.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the explanation. That makes sense to me 👍 It's a compromise and it seems like it's better to have the optimization than to not have it. Would be nice to see some measurements and statistics that confirm the 80% and 20% assumptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it would be useful. Obviously I'm generalising heavily from intuition only 😅

I wonder how we could determine stats on how many Themes include a Nav block. Maybe @MaggieCabrera or @scruffian would have an idea?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It definitely would be hard to say 😉 Especially given that one could install a plugin for their menu (like one of those megamenu plugins) and not use the Nav block at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting that it's possible to disable preloading by filtering on block_editor_rest_api_preload_paths and removing all requests that contain wp/v2/navigation. So it's not something folks are locked into. But a non-developer user would find this difficult.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

most sites will have a Nav block.

I think this is a safe assumption, also keep in mind that fresh installs have tt3 on them, which has the block on the theme.

@scruffian
Copy link
Contributor

scruffian commented Mar 14, 2023

In my opinion the time to first byte is much more of a concern on the frontend of the site, where it impacts all users and has SEO implications, than in the editor where only the site owner is impacted. The increased speed of preloading navigations is vastly better than very slight slow down in TTFB, and given how many sites use navigation this seems a good compromise.

Having said that, if we can find a way to only do this if a site uses a navigation menu that would be even better.

I also notice that we do the same thing with the list of pages, so if my navigation block contains a page list (and many do) then I still get a loading state while the list of pages preloads. It might be worth considering also preloading the list of pages, in a different PR of course :)

@draganescu
Copy link
Contributor

Does the code highlight some kind of problem in the navigation block? We need to preload published and draft menus and then preload only published menus. We should have published menus from the previous call, right?

I agree with @scruffian that TTFB in admin seems less of a problem when it improves the experience so much, but I wonder if the block itself is not really trying too much with so many requests?

I wonder, since the navigation is such an essential feature, could these data be preloaded clientside in the site editor? In WordPress/wordpress-develop#3894 (review) the result was to change the approach and warm up the client before the user opens the view that needs the data. Since we're not using menu data in the sidebar, that could be a potential route too?

To summarize:

  • I think the TTFB is not a problem here, menus are data that is likely needed, the edge case mentioned by @tyxla (here) is super edge case - as if I have a whole shop with 10 menus and turn that into a landing page, unlikely?
  • I think preloading makes the block seem fixed
  • I don't understand the two calls we need to preload, why not one?

@getdave
Copy link
Contributor Author

getdave commented Mar 15, 2023

Thank you for your feedback @draganescu.

Does the code highlight some kind of problem in the navigation block? We need to preload published and draft menus and then preload only published menus. We should have published menus from the previous call, right?

Yes but I see it as a tangential issue. We need to look separately at making the initial Nav block render require a single "all menus" request only.

I wonder, since the navigation is such an essential feature, could these data be preloaded clientside in the site editor?

Yes we should be exploring how the client side network requests can avoid being run in sequence and avoid be dependent on each other.

However, again I see this as an aside. Even if we make all the requests run in parallel then we still need a network request to resolve for one of the most important pieces of data to making the editor feel "ready" more quickly. (As you know) I think as developers we are often guilty of assuming everyone is on a Fibre connection when in fact that isn't the case for the majority.

As a result I think the key issue is minimising the need for additional network requests for "essential" data (from the Gutenberg SPA) after the initial request to the server for the site-editor.php page resolves. By preloading as we do here all the data is available immediately and therefore the perceived load time is reduced because users on slow connections don't have to wait for another X number of network requests.

I think it's super easy to hope that new client side tech will solve these problems (and it will help) but sometimes it's just down to ensuring the key data is available immediately.

Since we're not using menu data in the sidebar, that could be a potential route too?

We will be using that data very shortly in Browse Mode so I don't think that's a viable route. IMHO It's skirting around the actual issue.

@getdave
Copy link
Contributor Author

getdave commented Mar 15, 2023

Having said that, if we can find a way to only do this if a site uses a navigation menu that would be even better.

@scruffian Yes that would be ideal. But you can't know that without doing more queries so its self defeating.

@scruffian
Copy link
Contributor

I created a PR to remove the extra request for drafts: #49143

@getdave
Copy link
Contributor Author

getdave commented May 4, 2023

We could/should revisit this in context of changes in #48698

We could now preload the fallback request which would mean it would return very quickly and the resolver would immediately push the resulting embedded entity into state. Only issue is that this request would be preloaded even in situations where a fallback is not required (i.e. all the Nav blocks already have ref set).

I think Nav loading is still a big enough pain point that this is worth revisiting, especially now we can almost guarantee that every single site will have a Navigation menu as a result of #48698

@getdave getdave force-pushed the try/preload-navigation-requests branch from b72b923 to c28dcb2 Compare May 11, 2023 09:47
@getdave
Copy link
Contributor Author

getdave commented May 11, 2023

Noting that we could also preload the fallback requests by:

$preload_paths[] = array(
	add_query_arg(
		array(
			'_embed' => true,
		),
		'wp-block-editor/v1/navigation-fallback'
	),
	'GET',
);

...and then changing the related resolver to use 1 rather than true for the _embed query param as it seems the preloading doesn't use true in it's cache but rather transforms to 1.

export const getNavigationFallbackId =
	() =>
	async ( { dispatch } ) => {
		const fallback = await apiFetch( {
			path: addQueryArgs( '/wp-block-editor/v1/navigation-fallback', {
				_embed: 1,
			} ),
		} );
///

Copy link
Contributor

@draganescu draganescu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this preloading technique does not come for free but:

  • the number of websites with no menu is infinitely smaller than the ones with menus
  • the preloading happens only in the site editor, not in all block editors
  • the preloading theoretially slows down only admin sections
  • the navigation REST endpoints for menus and fallback are now needed in two places
    • the navigation block
    • the navigation screen of the site editor sidebar
  • all navigation requires multiple calls because our API is RESTful so we do OPTIONS and then call various resources in turn, we don't just smash everything into one endpoint
  • successive network requests are terrible to fix in UI in a proper way
    • if the navigation block is bearable to load async and show spinners and placeholders, as other items in the canvas do this, the same is not true for the navigation screen of the site editor canvas which is "chrome" UI which should not blink in and out of existence (nobody expects the taskbar or menu bar or home screen icons to load async on their devices)

For these reasons I think we should not only merge this PR but also add the fallback endpoint as @getdave described above.

The performance effects can always be mititgated on a case by case basis by:

  • filtering out what the REST API does
  • using good cache setups

Copy link
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy we've weighed the tradeoffs and personally agree with them.

This is a great improvement! Thanks @getdave 🚀

@getdave
Copy link
Contributor Author

getdave commented May 11, 2023

Ok we have a general consensus here. I think we should merge and follow up with work to preload the fallbacks.

@getdave getdave merged commit e7e1b7c into trunk May 11, 2023
@getdave getdave deleted the try/preload-navigation-requests branch May 11, 2023 12:42
@github-actions github-actions bot added this to the Gutenberg 15.9 milestone May 11, 2023
@getdave
Copy link
Contributor Author

getdave commented May 11, 2023

@draganescu the follow up to preload fallbacks is here #50545

@getdave getdave mentioned this pull request May 17, 2023
10 tasks
@ramonjd ramonjd added the Needs PHP backport Needs PHP backport to Core label Jun 5, 2023
@ramonjd ramonjd removed the Needs PHP backport Needs PHP backport to Core label Jun 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Navigation Affects the Navigation Block [Type] Performance Related to performance efforts
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants