dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.38k stars 10k forks source link

Blazor WASM lazy loading reload the page #27228

Closed aguacongas closed 3 years ago

aguacongas commented 3 years ago

Describe the bug

I've several satellites libraries containing pages I want to lazy load but each time I click on a navigation link the entire site page is reloaded except at the 1st click.

To Reproduce

The code is here

  1. Create a solution with dotnet new blazorwasm -ho -au Individual
  2. Add 3 Razor class libraries LazyLoad1, LazyLoad2 and NotLazyLoaded
  3. Move Index.razor and Authentication.razor in NotLazyLoaded
  4. Move Counter.razor in LazyLoad1
  5. Move FetchData.razor" in LazyLoad2*
  6. Update App.razor to lazy load LazyLoad1 and LazyLoad2 but not NotLazyLoaded:
@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@inject LazyAssemblyLoader _assemblyLoader

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly"
            AdditionalAssemblies="@_lazyLoadedAssemblies" OnNavigateAsync="@OnNavigateAsync">
        <Navigating>
            <div style="padding:20px;background-color:blue;color:white">
                <p>Loading the requested page&hellip;</p>
            </div>
        </Navigating>
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <NotAuthorized>
                    @if (!context.User.Identity.IsAuthenticated)
                    {
                        <RedirectToLogin />
                    }
                    else
                    {
                        <p>You are not authorized to access this resource.</p>
                    }
                </NotAuthorized>
            </AuthorizeRouteView>
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>
@code {
    private readonly List<Assembly> _lazyLoadedAssemblies = new List<Assembly>
    {
            typeof(NotLazyLoaded.Index).Assembly
    };

    private Task OnNavigateAsync(NavigationContext args)
    {
        var path = args.Path;
        if (path.StartsWith("counter"))
        {
            return LoadAssemblyAsync("LazyLoad1.dll");
        }

        if (path.StartsWith("fetchdata"))
        {
            return LoadAssemblyAsync($"LazyLoad2.dll");
        }

        return Task.CompletedTask;
    }

    private async Task LoadAssemblyAsync(string assemblyName)
    {
        var assemblies = await _assemblyLoader.LoadAssembliesAsync(
            new[] { assemblyName }).ConfigureAwait(false);
        _lazyLoadedAssemblies.AddRange(assemblies.Where(a => !_lazyLoadedAssemblies.Any(l => l.FullName == a.FullName)));
    }
}
  1. Add services.AddScoped<LazyAssemblyLoader>(); in LazyLoading.Server/Startup.cs
  2. Update LazyLoading.Client.csproj for lazy loading and project reference
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

    <PropertyGroup>
        <TargetFramework>net5.0</TargetFramework>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0-rc.2.20475.17" />
        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.0-rc.2.20475.17" PrivateAssets="all" />
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\LazyLoad1\LazyLoad1.csproj" />
        <ProjectReference Include="..\LazyLoad2\LazyLoad2.csproj" />
        <ProjectReference Include="..\LazyLoadShared\NotLazyLoaded.csproj" />
        <ProjectReference Include="..\Shared\LazyLoading.Shared.csproj" />
    </ItemGroup>

    <ItemGroup>
        <BlazorWebAssemblyLazyLoad Include="LazyLoad1.dll" />
        <BlazorWebAssemblyLazyLoad Include="LazyLoad2.dll" />
    </ItemGroup>

</Project>
  1. Add necessary references and import in LazyLoad1, LazyLoad2 and NotLazyLoaded
  2. Launch the application. Create a user if needed
  3. Click on counter : LazyLoad1 is loaded
  4. Click on forecast : the site is reloaded
  5. Click on counter again : the site is reloaded again

Further technical details

Runtime Environment: OS Name: Windows OS Version: 10.0.19041 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\5.0.100-rc.2.20479.15\

Host (useful for support): Version: 5.0.0-rc.2.20475.5 Commit: c5a3f49c88

.NET SDKs installed: 2.2.207 [C:\Program Files\dotnet\sdk] 3.1.300-preview-015095 [C:\Program Files\dotnet\sdk] 3.1.300 [C:\Program Files\dotnet\sdk] 3.1.403 [C:\Program Files\dotnet\sdk] 5.0.100-rc.2.20479.15 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.All 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.0-rc.2.20475.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.23 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.0-rc.2.20475.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.0-rc.2.20475.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

To install additional .NET runtimes or SDKs: https://aka.ms/dotnet-download


- The IDE : Microsoft Visual Studio Community 2019 Preview
   Version 16.8.0 Preview 5.0
captainsafia commented 3 years ago

Thanks again for the report and the great repro, @aguacongas!

I played around with the repo project that you provided a bit. At first, I figured that there was an interplay between the CascadingAuthenticationState component and the rendering that occurs in the router. I played around with this by moving the cascading parameter component to the Found render fragment. This didn't make much of a difference.

I also removed the lazy-loading logic altogether (by removing the OnNavigateAsync parameter in the Router and the property group int the project file) and still see reloads occurring when navigating between the the routes.

Eventually, I was able to resolve it by manually adding the lazily-loaded assemblies into the _lazyLoadedAssemblies property that is passed to AdditionalAssemblies.

I suspect that this and your other reported issue #27242 are related because the correct assemblies aren't populated into the additional assemblies list. Can you try the 5.0 nightly and see if that resolves the issue?

aguacongas commented 3 years ago

@captainsafia I confirm it's also fixed in 5.0.0-rtm.20512.8