

Chrome Extension with Blazor WASM - The Integration
source link: https://dev.to/dotnet/chrome-extension-with-blazor-wasm-the-integration-5gi2
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.


Chrome Extension with Blazor WASM - The Integration
Browser Extension with Blazor WASM (2 Part Series)
In my previous post, I've walked through how to migrate a JavaScript-based Chrome extension to Blazor WASM with minimal code changes. Although it's successfully migrated to Blazor WASM, it doesn't fully use the JavaScript Interoperability (JS interop) feature, which is the powerful feature of Blazor WASM. I'm going to take this feature throughout this post.
You can download the sample Chrome extension from this GitHub repository:
devkimchi / blazor-wasm-chrome-extension
This provides sample codes for a Chrome Extension app built on Blazor WASM
Blazor WASM Chrome Extension Sample
This provides sample codes for a Chrome Extension app built on Blazor WASM.
Getting Started
Build the app
dotnet build .
Publish the app
dotnet publish ./src/ChromeExtensionV2/ -c Release -o published
Run PowerShell script
./Run-PostBuild.ps1
Register the extension to your Chromium-based browser like Chrome or Edge
Visit any website on https://developer.chrome.com or https://docs.microsoft.com
Run the extension by clicking the icon at the top of your web browser
Chrome Extension – Before JS Interop
The index.html
file written in the previous post looks like the following. It loads blazor.webassembly.js
first with the autostart="false"
option, followed by loading js/main.js
through the function call. The js/main.js
reference is replaced with js/options.js
or js/popup.js
during the artifact generation process.
<!DOCTYPE html>
<html lang="en">
...
<body>
<div id="app">Loading...</div>
...
<!-- Add the 'autostart' attribute and set its value to 'false' -->
<script src="_framework/blazor.webassembly.js" autostart="false"></script>
<!-- ⬇️⬇️⬇️ Add these lines ⬇️⬇️⬇️ -->
<script>
Blazor.start().then(function () {
var customScript = document.createElement('script');
customScript.setAttribute('src', 'js/main.js');
document.head.appendChild(customScript);
});
</script>
<!-- ⬆️⬆️⬆️ Add these lines ⬆️⬆️⬆️ -->
</body>
</html>
I'm not happy with this JS loading due to the two reasons below:
- I have to explicitly give the option of
autostart="false"
while loading theblazor.webassembly.js
file, which is extra to the bootstrapper. - I have to append
js/main.js
through the Promise pattern afterBlazor.start()
, which is another extra point to the bootstrapper.
Can we minimise this modification from the original index.html
file and use more JS Interop capabilities here so that it can be more Blazor-ish?
Chrome Extension – JS Interop Step #1
Let's update the index.html
file. Unlike the previous update, remove the JS part calling the Blazor.start()
function. Load js/main.js
before loading blazor.webassembly.js
. Remove the autostart="false"
attribute as well.
<!DOCTYPE html>
<html lang="en">
...
<body>
<div id="app">Loading...</div>
...
<!-- ⬇️⬇️⬇️ Add this line ⬇️⬇️⬇️ -->
<script src="js/main.js"></script>
<!-- ⬆️⬆️⬆️ Add this line ⬆️⬆️⬆️ -->
<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>
Originally the js/main.js
file was blank, but this time let's add the following JS function that appends another script
tag to load the given JS file reference.
function loadJs(sourceUrl) {
if (sourceUrl.Length == 0) {
console.error("Invalid source URL");
return;
}
var tag = document.createElement('script');
tag.src = sourceUrl;
tag.type = "text/javascript";
tag.onload = function () {
console.log("Script loaded successfully");
}
tag.onerror = function () {
console.error("Failed to load script");
}
document.body.appendChild(tag);
}
Update the Popup.razor
file like below:
- Add the
@inject
declaration for theIJSRuntime
instance as a dependency. - Call the
JS.InvokeVoidAsync
method to load thejs/popup.js
file by invoking theloadJs
function from thejs/main.js
file.
@* Popup.razor *@
@page "/popup.html"
@* Inject IJSRuntime instance *@
@inject IJSRuntime JS
...
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!firstRender)
{
return;
}
var src = "js/popup.js";
// Invoke the `loadJs` function
await JS.InvokeVoidAsync("loadJs", src).ConfigureAwait(false);
}
}
Update the Options.razor
file in the same way.
@* Options.razor *@
@page "/options.html"
@* Inject IJSRuntime instance *@
@inject IJSRuntime JS
...
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!firstRender)
{
return;
}
var src = "js/options.js";
// Invoke the `loadJs` function
await JS.InvokeVoidAsync("loadJs", src).ConfigureAwait(false);
}
}
Now, we don't need the reference replacement part in the PowerShell script. Let's comment them out.
# Run-PostBuild.ps1
...
# Update-FileContent `
# -Filename "./published/wwwroot/popup.html" `
# -Value1 "js/main.js" `
# -Value2 "js/popup.js"
# Update-FileContent `
# -Filename "./published/wwwroot/options.html" `
# -Value1 "js/main.js" `
# -Value2 "js/options.js"
Build and publish the Blazor WASM app, then run the PowerShell script to get ready for the extension loading. Once reload the extension, it works with no issue. The loadJs
function is the key that takes advantage of the JS Interop feature.
However, I'm still not happy with adding js/main.js
to index.html
. Can we also remove this part from the file and use the JS Interop feature instead?
Chrome Extension – JS Interop Step #2
Let's get index.html
back to the original state when a bootstrapper creates the file. Then, all we can see in index.html
is the blazor.webassembly.js
file reference.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>ChromeExtensionV2</title>
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<link href="ChromeExtensionV2.styles.css" rel="stylesheet" />
</head>
<body>
<div id="app">Loading...</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>
Then, add the export
declaration in front of the loadJs
function in the js/main.js
file.
export function loadJs(sourceUrl) {
...
}
Update Popup.razor
like below.
...
var src = "js/popup.js";
// Import the `js/main.js` file
var module = await JS.InvokeAsync<IJSObjectReference>("import", "./js/main.js").ConfigureAwait(false);
// Invoke the `loadJs` function
await module.InvokeVoidAsync("loadJs", src).ConfigureAwait(false);
The same change should also be applicable to Options.razor
. And finally, update the manifest.json
below because we no longer need the hash key for popup.js
and options.js
.
{
"manifest_version": 2,
"version": "1.0",
"name": "Getting Started Example (Blazor WASM)",
"description": "Build an Extension!",
...
"content_security_policy": "script-src 'self' 'unsafe-eval' 'wasm-unsafe-eval' 'sha256-v8v3RKRPmN4odZ1CWM5gw80QKPCCWMcpNeOmimNL2AA='; object-src 'self'",
...
}
Build and publish the app, and run the PowerShell script against the artifact. Then, reload the extension, and you will see the same result.
So far, we've walked through how to take more advantage of the JS Interop feature that Blazor WASM offers to migrate the existing Chrome extension to Blazor WASM. What could be the potential merits of this exercise?
- We never touch any bootstrapper codes that Blazor WASM generate for us.
- If necessary, we load JavaScript for each page using the JS Interop feature. During this practice, C# handles all the JS codes.
Then, does this exercise only brings you benefits? Here are a couple of considerations:
- The code gets overly complex. If we simply import the JavaScript files through the
index.html
/popup.html
/options.html
, we don't need to do this exercise. - Not everytime the dynamic JS loading is useful. It has trade-offs. If you don't want to touch the bootstrapper files, then try this approach discussed in this post. But if you do touch the bootstrapper files, then this dynamic JS loading approach may be unsuitable.
Overall, if we use more JS Interop features appropriately, we can build the Blazor WASM app more effectively, which will be another option for building Chrome extensions.
Do you want to know more about Blazor?
Here are some tutorials for you.
Recommend
-
12
How to have realtime update in your app with Blazor WASM, SignalR and MediatR One of the big problem with web app is real time update : most of the traffic is from the client to the server, so the server cannot say anything to the c...
-
13
ToDo App with Blazor wasm and Grpc Agenda IntroductionProject SetupImplementing gRPC serviceImplement Blazor ApplicationIntegrate Service with Client
-
4
.NET 5 正式发布已经有一段时间了,其中 Blazor 技术是该版本的亮点之一。作为微软技术的被坑者,年少的我曾经以为 SilverLight 能血虐 Flash,Zune 能团灭 iPod,WP 能吊打 iPhone,UWP 能统一全平台…… 可是后…… 最终步入大龄程序员的我发现,只有陪伴了我将近...
-
12
Blazor WASM 404 error and fix for GitHub Pages Written by Kristoffer Strube, April 06, 2021 The project structure of a Blazor WASM project is in some...
-
21
Rendering dynamic content in Blazor Wasm using DynamicComponent Written by Kristoffer Strube, April 20, 2021 Sometimes we need to load different types...
-
7
Ahead-Of-Time Compilation for Blazor Wasm Written by Kristoffer Strube, September 28, 2021 Blazor Wasm applications are being interpreted in the brows...
-
4
Back to all posts MVC vs Blazor WASM: A Different Look At Performance Posted on Mar 29, 2022 When it comes to measuring the performa...
-
8
.NET WASM is still loading. You can interact in this page after it's fully loaded.Current page is prerendered.Get started Please check t...
-
7
August 30, 2022 Troubleshooting .NET Blazor WASM Debugging ...
-
6
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK