Blazor WebAssembly Loading Optimization
Optimizing Blazor WebAssembly loading is aimed at the initial loading of WebAssembly. Since Blazor WebAssembly loads all .NET Core assemblies into the browser on first load, and may reference many third-party DLLs during use, this can lead to slow loading.
Optimization Solutions:
1. Compression
When publishing a Blazor WebAssembly application, the output content is statically compressed during the publish process, reducing the application size and eliminating runtime compression overhead. The following compression algorithms are used:
Obtain the JavaScript Brotli decoder from the google/brotli GitHub repository. The minified decoder file is named decode.min.js and is located in the js folder of the repository.
Modify the wwwroot/index.html file code, add autostart="false" to prevent the default startup from loading assemblies:
<script src="_framework/blazor.webassembly.js" autostart="false"></script>
After the Blazor <script> tag and before the closing </body> tag, add the following JavaScript code <script> block:
<script type="module">
import { BrotliDecode } from './decode.min.js';
Blazor.start({
loadBootResource: function (type, name, defaultUri, integrity) {
// Note: Compression will not be enabled when using localhost here
if (type !== 'dotnetjs' && location.hostname !== 'localhost') {
return (async function () {
const response = await fetch(defaultUri + '.br', { cache: 'no-cache' });
if (!response.ok) {
throw new Error(response.statusText);
}
const originalResponseBuffer = await response.arrayBuffer();
const originalResponseArray = new Int8Array(originalResponseBuffer);
const decompressedResponseArray = BrotliDecode(originalResponseArray);
const contentType = type ===
'dotnetwasm' ? 'application/wasm' : 'application/octet-stream';
return new Response(decompressedResponseArray,
{ headers: { 'content-type': contentType } });
})();
}
}
});
</script>
The compression solution will reduce loading time to approximately one-third the size of the compressed DLLs. The effect is shown below:

When using the autostart="false" flag, assemblies are not loaded on startup; loading will be executed in the code block above. By default, .br files are loaded.
2. Lazy Loading Assemblies
Improve Blazor WebAssembly application startup performance by deferring the loading of application assemblies until they are needed. This approach is called "lazy loading."
Break down the Blazor WebAssembly project into finer details and improve the initial loading time by lazy loading assemblies. We'll use a case study to explain lazy loading assemblies.
Create an empty Blazor WebAssembly project: Project name Demand:

Disable HTTPS and use Progressive Web Application:

Create a Razor class library with the project name Demand.Components, then create the project with default options:

Create a Components.razor file and delete unnecessary files. The effect is shown below:

Add the following code to Components.razor:
@inject NavigationManager NavigationManager @page "/components"
<div>
<h1>Components</h1>
</div>
<button @onclick="Goto">Go to Home</button>
@code { private void Goto() { NavigationManager.NavigateTo("/"); } }
In the Demand project, reference the Demand.Components project.
Modify the App.razor file with the following code:
@using System.Reflection @using
Microsoft.AspNetCore.Components.WebAssembly.Services @*
Note: WebAssembly injects this by default, but Server does not.
In Server, manually inject builder.Services.AddScoped<LazyAssemblyLoader
>(); *@ @inject LazyAssemblyLoader AssemblyLoader
<Router
AppAssembly="@typeof(App).Assembly"
AdditionalAssemblies="@lazyLoadedAssemblies"
OnNavigateAsync="@OnNavigateAsync"
>
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
@code { private List<Assembly>
lazyLoadedAssemblies = new(); private async Task
OnNavigateAsync(NavigationContext args) { try { if (args.Path ==
"components") { // Customize the assemblies that Demand.Components depends on here var assemblies
= await AssemblyLoader.LoadAssembliesAsync(new[] { "Demand.Components.dll"
}); // Add to the route assembly scanning list lazyLoadedAssemblies.AddRange(assemblies); } }
catch (Exception ex) { } } }</Assembly
></LazyAssemblyLoader
>
Handle the assemblies that need to be loaded for specific route components.
Open the Demand project file. In Debug mode, you can add the following ignore list:
<ItemGroup>
<BlazorWebAssemblyLazyLoad Include="System.Xml.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.XmlSerializer.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.XmlDocument.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.XPath.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.XPath.XDocument.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.XDocument.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.Serialization.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.ReaderWriter.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.Linq.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Windows.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Quic.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Compression.ZipFile.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Runtime.Numerics.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Collections.Immutable.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Win32.Registry.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Web.HttpUtility.dll" />
<BlazorWebAssemblyLazyLoad Include="System.ValueTuple.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.AccessControl.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Mail.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.NameResolution.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.UnmanagedMemoryStream.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Pipes.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Pipes.AccessControl.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Pipelines.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.FileSystem.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.FileSystem.Watcher.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.FileSystem.Primitives.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.FileSystem.DriveInfo.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.FileSystem.AccessControl.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Data.Common.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.CSharp.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Console.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Core.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Data.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Data.DataSetExtensions.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Drawing.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Drawing.Primitives.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.TraceSource.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.Tools.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.TextWriterTraceListener.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.StackTrace.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.Process.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.FileVersionInfo.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.DiagnosticSource.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.Debug.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.Contracts.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.AspNetCore.Authorization.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.AspNetCore.Components.Forms.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.AspNetCore.Metadata.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Extensions.Configuration.Binder.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Extensions.FileProviders.Abstractions.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Extensions.FileProviders.Physical.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Extensions.Configuration.FileExtensions.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Extensions.FileSystemGlobbing.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.MemoryMappedFiles.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.IsolatedStorage.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Compression.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Compression.FileSystem.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Compression.Brotli.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Formats.Tar.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Formats.Asn1.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.WebSockets.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Private.DataContractSerialization.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Private.Xml.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.VisualBasic.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.VisualBasic.Core.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Threading.Tasks.Dataflow.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Text.Encoding.CodePages.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.WebSockets.Client.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Private.Xml.Linq.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Text.RegularExpressions.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Sockets.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.WebClient.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.WebProxy.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Ping.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.X509Certificates.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.WebHeaderCollection.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.OpenSsl.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.Encoding.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.Csp.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.Cng.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Claims.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.Algorithms.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Win32.Primitives.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.HttpListener.dll" />
<BlazorWebAssemblyLazyLoad Include="System.AppContext.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.NetworkInformation.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Requests.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Primitives.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Security.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.ServicePoint.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Http.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Globalization.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Globalization.Calendars.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Globalization.Extensions.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Http.Json.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Web.dll" />
<BlazorWebAssemblyLazyLoad Include="WindowsBase.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Resources.Writer.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Resources.ResourceManager.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Resources.Reader.dll" />
</ItemGroup>
These are some less commonly used assemblies. If the following error occurs, delete the assemblies that cannot be found and configure on-demand loading,

However, if you use the on-demand loading configuration above, an exception may occur during publishing, as shown in the figure below; the error is caused by Blazor WebAssembly using trimming by default during publishing. Since the following assemblies are not used, they are configured for on-demand loading after trimming, but they have already been trimmed, so the on-demand loading assemblies cannot be found. Just delete the assemblies that report errors; this only occurs during publishing. Debug mode can still use the on-demand loading configuration above, which can make debugging faster:

Next step.
Add the on-demand loading configuration for the specified project. Configure the Demand.Components project:
<ItemGroup>
<BlazorWebAssemblyLazyLoad Include="Demand.Components.dll" />
</ItemGroup>
Modify the Pages/Index.razor file code:
@page "/" @inject NavigationManager NavigationManager
<h1>Hello, world!</h1>
<button @onclick="Goto">Go to components</button>
@code { private void Goto() { NavigationManager.NavigateTo("/components"); } }
Then start the project, open F12 developer tools, click Application, find Storage, and click Clear site data (assemblies will be cached after the first load):

Click Network, then refresh the page. We can see that Demand.Components.dll is not loaded here, but the assemblies shown here are:

Then click the button on the page:

Now go back to the Network tab in the debug tools. We can see that Demand.Components.dll has been loaded. This assembly is loaded only when we use it, and it will not be reloaded when entering the interface a second time:

Then publish the project (remember the issue mentioned above about assembly loss due to trimming causing on-demand loading to fail; just remove the trimmed assemblies from the on-demand loading configuration):

Then use docker compose to deploy an nginx proxy to view the effect:
Create a docker-compose.yml file and add the following code. In the same directory as docker-compose.yml, create two folders: conf.d and wwwroot:
services:
nginx:
image: nginx:stable-alpine
container_name: nginx
volumes:
- ./conf.d:/etc/nginx/conf.d
- ./wwwroot:/wwwroot
ports:
- 811:80
Create webassembly.conf in conf.d and add the following code:
server {
listen 80;
server_name http://localhost;
location / {
root /wwwroot;
index index.html;
}
}
Then, in the directory where docker-compose.yml is located, use docker-compose up -d to start the nginx service.
Open a browser and visit http://127.0.0.1:811/ (do not use localhost, as compression is disabled by default). Then open F12 debug tools, clear storage in the Application tab, open the Network option, refresh the browser, and the loading is complete. Optimized to 2.3MB, compression is enabled, and unused assemblies are trimmed during publishing:

Extreme Optimization to 1MB
Add the following configuration to the Demand project file. This configuration disables some features, such as globalization:
<PublishTrimmed>true</PublishTrimmed>
<InvariantGlobalization>true</InvariantGlobalization>
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
<EventSourceSupport>false</EventSourceSupport>
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
<UseNativeHttpHandler>true</UseNativeHttpHandler>
Then follow the steps above to publish it and deploy it to nginx.
Check the loading size in the Network tab. We can see it has come down to 1MB. Removing some JS files would make it even smaller, thus significantly solving the loading issue (suggestion from Xiaoye Kun):

Conclusion
If you have a better optimization solution, you can contact me.
Shared by Token.
- Demo online access: http://webassembly.tokengo.top
- GitHub: https://github.com/239573049/demand
- Gitee: https://gitee.com/hejiale010426/demand
Blazor discussion group: 452761192
This article is reproduced.
Author: Token
Original title: How to optimize WebAssembly to 1MB?
Original link: https://www.cnblogs.com/hejiale010426/p/17076817.html