bit Minifier
A smaller Blazor WebAssembly app, for free
Every kilobyte your users download is time they wait before your app starts.
bit Minifier takes around 400 KB off what a Blazor WebAssembly app downloads, and asks nothing in return:
no code changes, no settings and no cost at runtime. Install it in the Blazor WebAssembly project and publish in Release.
dotnet add package Bit.Minifier
Why it costs nothing
- Nothing to set up. Install the package and publish in Release.
- Nothing at runtime. The app runs the same code, only with shorter names and without the attributes nothing reads while it runs.
- Nothing during development.
dotnet build,dotnet runanddotnet watchare never touched; only a Release publish is. - Nothing ships broken. Nothing is written unless every reference still resolves. An assembly that can't be minified safely is left as it was, and the publish says which one and why.
- Still debuggable. The pdbs are rewritten too, so breakpoints and line numbers keep working, and a map turns any stack trace back into the names your source has.
Numbers
The bit BlazorUI demo, its WebAssembly client published in Release, measured as browsers download it, brotli compressed:| Download | Saved | |
|---|---|---|
| Without it | 6,604 KB | |
| Default | 6,132 KB | 472 KB |
| Aggressive | 6,064 KB | 540 KB |
That's 7.2% and 8.2% less to download, for about 8 seconds more of publishing. The libraries give up the most:
Bit.BlazorUI 594 KB of its 3.6 MB (-16%), Newtonsoft.Json 137 KB (-20%),
System.Text.Json 72 KB (-18%). The app's own code keeps every name it has and still gives up 310 KB, all of it attributes.
What it does
Trimming removes the code an app doesn't use. Right after it, bit Minifier rewrites every trimmed assembly, the app's, the libraries' and the framework's, along with its pdb. Every name that isn't public gets a short one, and the attributes only the compiler, debuggers and analyzers read ([CompilerGenerated], [Nullable], [DebuggerDisplay], ...) go.
What something reads by name at runtime stays:
- names a string literal mentions, as
nameof(...),GetMethod("...")and[UnsafeAccessor(Name = "...")]do; - public properties and fields, which serializers read;
- Blazor component types, and members with runtime attributes such as
[Parameter]and[JSInvokable].
To turn it all off without uninstalling anything:
<BitMinify>false</BitMinify>
Aggressive (experimental)
<BitMinifyAggressive>true</BitMinifyAggressive>
Reading a stack trace
Every publish writes a map of the names it changed toobj/<configuration>/<tfm>/bit-minifier.map.
The bit CLI reads a stack trace your app logged back into the names its source has, without installing anything:
dnx Bit.Cli --prerelease -- decode bit-minifier.map trace.txt
at _aN+_a.MoveNext() at Microsoft.Extensions.DependencyInjection._aD.ah[a,b](IServiceCollection services)
at ...Services.HttpMessageHandlers.ExceptionDelegatingHandler+<SendAsync>d__1.MoveNext() at Microsoft.Extensions.DependencyInjection.IServiceCollectionExtensions.AddSessioned[TService,TImplementation](IServiceCollection services)
BitMinifyMapOutputPath copies it out of obj for a CI job to collect:
dotnet publish MyApp.Client.csproj -c Release -p:BitMinifyMapOutputPath=bit-minifier.map