-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Add framework support for lazy-loading assemblies on route change #23290
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
Conversation
272caab
to
44d10cc
Compare
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 kinda like this implementation.
src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyNavigationManager.cs
Outdated
Show resolved
Hide resolved
44d10cc
to
de0738a
Compare
src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyNavigationManager.cs
Outdated
Show resolved
Hide resolved
src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs
Outdated
Show resolved
Hide resolved
36714be
to
d8f21e6
Compare
8cea3cf
to
fa9f6f0
Compare
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.
Looks pretty good!
src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyDynamicResourceLoader.cs
Outdated
Show resolved
Hide resolved
src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyDynamicResourceLoader.cs
Outdated
Show resolved
Hide resolved
src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyDynamicResourceLoader.cs
Outdated
Show resolved
Hide resolved
src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyDynamicResourceLoader.cs
Outdated
Show resolved
Hide resolved
src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyDynamicResourceLoader.cs
Outdated
Show resolved
Hide resolved
src/Components/test/testassets/BasicTestApp/RouterTest/TestRouterWithDynamicAssembly.razor
Outdated
Show resolved
Hide resolved
src/Components/test/testassets/BasicTestApp/RouterTest/TestRouterWithDynamicAssembly.razor
Outdated
Show resolved
Hide resolved
77fda21
to
9208509
Compare
src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyDynamicAssemblyLoader.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Steve Sanderson <[email protected]>
b71ee8d
to
7ecc8f8
Compare
7ecc8f8
to
f4b6e5b
Compare
Thanks for the feedback everyone. I'll be merging this PR now since we have consensus on the overall approach. There are follow-up items to address that are tracked under the lazy-loading label. |
@captainsafia on a related note, are there plans to let us cancel the navigation too? It would be good for confirming "Do you want to save your changes?" for example, and your changes should now make it possible. |
This seems like a good idea long-term to me. In fact, I originally started with the assumption that the |
With cancellation, it's not totally clear to me what that means, since the browser's URL and history state has already changed. If there's clear prior art for how this is handled cleanly in other frameworks that would be interesting. |
Yeah, this is a good point. Our cancellation would only support cancelling the on navigate task itself, not the navigation event. A more illustrative name for @guardrex I don't think I included this is as a note in my original documentation text but we might want to add an explicit call out for this to avoid user confusion. Something like:
|
@SteveSandersonMS I was under the impression that Blazor already intercepts all clicks on |
@captainsafia Perhaps OnNavigated to illustrate past tense? I think the current name + the presence of a cancellation token will lead people to ask how to set the cancellation token to cancelled to prevent the navigation. It's the first thing I thought anyway :) |
When the user clicks back/forwards, Blazor isn’t the one changing the URL. Maybe it is possible to do something useful - I’m not sure - but it would be good to point to examples in other frameworks. |
@SteveSandersonMS the url will have changed in SSB by the time the handler runs |
This PR adds support for lazy loading assemblies to the Blazor framework.
In some scenarios, users might want to load assemblies dynamically at runtime based on certain constraints. For example, only load the assemblies needed to render the admin sales page if the user navigates to
mysite.com/admin/sales
.This feature is particularly helpful for users operating in the WebAssembly hosting model with pages that require a large amount of assemblies. In this case, a user can conserve network calls and local resources by not loading the assemblies they do not need.
Note: This feature is not useful in the Server hosting model and won't do anything useful there. If you're running in a Server hosting model, you don't have the same concerns about conserving network requests and local resources.
A user starts using this functionality by labelling the assemblies that should be lazy loaded in their project file. Like so:
At build time, these dependencies will be marked as lazy loaded. This will prevent them from being automatically loaded at app launch time by our startup logic.
Note: Only assemblies that are explicitly referenced from your app can be lazy loaded. If an assembly is not referenced in the app, it won't be lazily-loaded. In fact, it won't even be part of the published output as it will be stripped by the linker.
Once a user has designated the assemblies that should be lazily-loaded, they'll need to write some code to say when they should be loaded. Enter, this PR.
The user starts by adding a
OnNavigateAsync
callback to a router component in their page.The
OnNavigateAsync
handler is a callback that gets invoked whenever the user:This callback provides a
NavigationContext
parameter and expects aTask
in return.The
NavigationContext
object is a sealed class that exposes the following properties:The
Path
property is the route a user is navigating to, such as/blog/post/1
and theCancellationToken
can be used to observe the cancellation of the async task.Note:
OnNavigateAsync
automatically cancels the currently running navigation task when the user navigates away from a page.Inside
OnNavigateAsync
, the user can implement whatever logic is needed to determine what assemblies should be loaded. Options include:OnNavigateAsync
@code
fragmentThis is an implementation detail that is left to the user based on their particular scenarios.
Once I've figured out what assemblies I need to load when a user navigates to a certain
NavigationContext.Path
I need to load them.Enter
LazyAssemblyLoader
.LazyAssemblyLoader
is a singleton registered in the DI. To use it, a user can inject the dependency into their page.The
LazyAssemblyLoader
provides a single public API methodLoadAssembliesAsync
which has the following API interface.Assembly names in, assemblies out. Under the hood, this method makes some JS interop calls to fetch the assemblies via a network call and load them into the runtime running on WASM.
Note: The implementation is designed to support pre-rendering scenarios. When pre-rendering, there is no JS runtime available to interop with. In those scenarios, we're running on the server and can assume that all assemblies are already loaded (see note above about why lazy loading on the server doesn't make sense).
So now, my Router looks something like this.
Note: The lazy-loaded assemblies are passed back to
AdditionalAssemblies
in the event that the assemblies contain routeable components. In that case, we search the new set of assemblies for routes and update our routeable. This enables scenarios where users want to lazy-loaded a collection of pages onto their app.While lazy-loading, the user might want to showcase a UI to their customer to indicate that a page transition is happening. This PR introduces support for a
Navigating
render fragment that is displayed when theOnNavigate
async event is running.In summary, this PR introduces a few changes to support lazy-loading that include:
Router
to enable lazy-loading