Removing Blazor From My Blog
14/07/2024This blog is written using the Web UI framework Blazor.
I love Blazor, it is so simple to write interactive websites while spending most of your day writing C#.
So when I decided to create my own blogging platform, Blazor was my tool of choice.
One of the other choices I made, was to use Blazor WebAssembly (WASM), so that I could host the blog for free using Azure Static Web Apps.
This works great, but I have never been happy with the performance of the site on first load, and I think that has hurt my traffic.
In the past I have made significant improvements to the load speed by changing how the site is created, and so have Microsoft by working on download sizes of Blazor and dotnet runtimes too, but I have never been satisfied.
Find out more about how I built this blog, and the journey to improve it, here
I have been writing more and more posts recently, and that slow initial load has been really bothering me, so it has been working in the back of my mind of how to improve it again.
As always I kept coming back to removing the significant download size, when it dawned on me, that I don't need Blazor!
Wait, What?
Yes that is correct, I realised I don't need Blazor for the majority of the pages on my site as MOST of the pages on my site don't require any code to work as it is mainly links between pages.
Blazor is used to generate the pages, and the layout etc, but once the page is rendered, it is not needed.
There is one exception though, and that is the Search page. That page is fully interactive, and definitely requires Blazor code.
So the question became, Can I remove Blazor for 99.9% of the pages on my site, but include it for the pages that needed it?
Now Blazor activates on a web page, by including a boot-loader
in javascript, this causes the dotnet webassembly runtimes to be downloaded, as well as the code of the website, and activates the code in the runtime.
This is where all the startup time is - don't get me wrong, the download is small and fast (especially when you see Hero images on e-commerce sites MegaBytes in size!), so it is great for what you can do with it, but as a technical focussed blog, most engineers don't want to wait 5 seconds before they can read the content!
Therefore if I can ensure my cached statically rendered pages are exactly the same as the interactive version of them, I can remove the Blazor javascript boot-loader from the pages that don't need Blazor, and I should end up with an HTML page and a tiny load time.
That is exactly what I did, keep reading to see how:
Ensure all the statically rendered HTML is exactly the same as the dynamically rendered version
This involved removing all Blazor component level css, and moving it to the app.css
file (not ideal, but worth the payoff).
One issue here is when a Component had the same CSS class name as the top level page it would get the wrong css. This is handled fine within the Blazor component, but not when statically rendering it. Therefore a css class had to be renamed.
I had to ensure that consistency with links worked. e.g. Tags. The current Blazor code would be agnostic of casing, this does not work with Azure Static Web App HTML files. e.g. Category1.html
would not be served when category1.html
was requested.
Basically, this meant confirming all site links worked with a link checker.
Only add the Javscript Blazor boot-loader to certain pages
I changed index.html from
<script src="_framework/blazor.webassembly.js"></script>
to the following, only allowing it to be written when a URL ends in "/Search".
Note - It has to be written out in this way, as if you include <script>
within a string, then javascript/html parsers get confused.
<script>
if (window.location.pathname.endsWith('/Search')) {
var script = document.createElement('script');
script.src = '_framework/blazor.webassembly.js';
document.write(script.outerHTML);
}
</script>
Fix Code highlighting
I use highlight.js
to colorise the code snippets on the site. This is currently activated by Blazor due to the dynamic nature of the Single Page App (SPA). So without Blazor, I need to activate it via Javascript, I needed to change this
<script>
window.highlightSnippet = function () {
document.querySelectorAll('pre code').forEach((el) => {
hljs.highlightElement(el);
});
}
</script>
to this
<script>
window.highlightSnippet = function () {
document.querySelectorAll('pre code').forEach((el) => {
hljs.highlightElement(el);
});
}
window.highlightSnippet(); //We call it this way, so it works with and without Blazor for static pages too
</script>
Note - I tried several nuget packages for MarkDig
extension claiming to do code highlighting, but none worked very well with Blazor WebAssembly, so it is still in javascript.
I also noticed that the static pages were not honouring the code snippets line endings in some places.
This turned out to be the way I was creating the cached/static file using .ToMarkup()
instead of .ToHtml()
in AngleSharp.Html.Dom.IHtmlDocument
.
Create a static index.html
The index.html
page is the "Single Page" in Blazor, so as I am disabling Blazor (and its loading screen), I had to make this page static too (read about that here)
Conclusion
If we ignore fixing the static content output to be more accurate, the changes equate to a small change in index.html
.
The overall speed results was that because of this change are:
BEFORE:
- load time: 4.4seconds
- download size 8.4 MB
- 183 requests
AFTER:
- load time: 0.74seconds
- download size 876kB
- 19 requests
That is 83% faster, 90% smaller and 90% fewer requests.
Note - Tested in an incognito window with "Disable Cache" enabled in Dev Tools on this page: https://blog.markoliver.website/This-Service-Descriptor-Is-Keyed-Your-Service-Provider-May-Not-Support-Keyed-Services
And to top it all off, the Search page still works interactively!
Thank you for your time.
If you want to reach out, catch me on Twitter!