Skip to content

Commit 8b7b118

Browse files
authored
Add new post (#63)
1 parent b78ea4e commit 8b7b118

File tree

2 files changed

+288
-1
lines changed

2 files changed

+288
-1
lines changed

content/posts/2022/2022-07-08-lift-and-shift-existing-chrome-extension-to-blazor-wasm.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ Your existing JavaScript-based Chrome extension has now successfully been migrat
285285

286286
---
287287

288-
So far, we've migrated the JavaScript-based Chrome extension app to [Blazor WebAssembly][blazor wasm] with minimal code changes. Both extensions are almost identical, except for a couple of points. In other words, your extension can easily be migrated using the "lift & shift" approach. However, it doesn't entirely make use of the powerful [JS interop][blazor wasm jsinterop] feature yet. In the next post, let's explore how we can use the JS interop feature more for this migration.
288+
So far, we've migrated the JavaScript-based Chrome extension app to [Blazor WebAssembly][blazor wasm] with minimal code changes. Both extensions are almost identical, except for a couple of points. In other words, your extension can easily be migrated using the "lift & shift" approach. However, it doesn't entirely make use of the powerful [JS interop][blazor wasm jsinterop] feature yet. In the [next post][post 2], let's explore how we can use the JS interop feature more for this migration.
289289

290290

291291
## Do you want to know more about Blazor? ##
@@ -317,6 +317,9 @@ Here are some tutorials for you.
317317
[image-18]: https://sa0blogs.blob.core.windows.net/devkimchi/2022/07/lift-and-shift-existing-chrome-extension-to-blazor-wasm-18-ko.png
318318

319319

320+
[post 1]: /2022/07/08/lift-and-shift-existing-chrome-extension-to-blazor-wasm/
321+
[post 2]: /2022/07/20/lift-and-shift-existing-chrome-extension-to-blazor-wasm-2/
322+
320323
[gh sample]: https://github.com/devkimchi/blazor-wasm-chrome-extension/tree/the-migration
321324
[gh sample v2 original]: https://github.com/devkimchi/blazor-wasm-chrome-extension/tree/the-migration/src/chrome-extension-v2
322325
[gh sample v2 blazor]: https://github.com/devkimchi/blazor-wasm-chrome-extension/tree/the-migration/src/ChromeExtensionV2
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
---
2+
title: "Lift & Shift Existing Chrome Extension to Blazor WebAssembly #2"
3+
slug: lift-and-shift-existing-chrome-extension-to-blazor-wasm-2
4+
description: "Throughout this post, I'm going to walk through how to migrate the existing Chrome extension with minimal code changes."
5+
date: "2022-07-20"
6+
author: Justin-Yoo
7+
tags:
8+
- dotnet
9+
- blazor-wasm
10+
- chrome-extension
11+
- jsinterop
12+
cover: https://sa0blogs.blob.core.windows.net/devkimchi/2022/07/lift-and-shift-existing-chrome-extension-to-blazor-wasm-00.png
13+
fullscreen: true
14+
---
15+
16+
In my [previous post][post 1], I've walked through how to migrate a JavaScript-based [Chrome extension][chrome extension] to [Blazor WASM][blazor wasm] with minimal code changes. Although it's successfully migrated to Blazor WASM, it doesn't fully use the [JavaScript Interoperability (JS interop)][blazor wasm jsinterop] feature, which is the powerful feature of Blazor WASM. I'm going to take this feature throughout this post.
17+
18+
> You can download the sample app codes from [this GitHub repository][gh sample].
19+
20+
21+
## Chrome Extension – Before JS Interop ##
22+
23+
The `index.html` file written in the [previous post][post 1] 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.
24+
25+
```html
26+
<!DOCTYPE html>
27+
<html lang="en">
28+
...
29+
<body>
30+
<div id="app">Loading...</div>
31+
...
32+
<!-- Add the 'autostart' attribute and set its value to 'false' -->
33+
<script src="_framework/blazor.webassembly.js" autostart="false"></script>
34+
<!-- ⬇️⬇️⬇️ Add these lines ⬇️⬇️⬇️ -->
35+
<script>
36+
Blazor.start().then(function () {
37+
var customScript = document.createElement('script');
38+
customScript.setAttribute('src', 'js/main.js');
39+
document.head.appendChild(customScript);
40+
});
41+
</script>
42+
<!-- ⬆️⬆️⬆️ Add these lines ⬆️⬆️⬆️ -->
43+
</body>
44+
</html>
45+
```
46+
47+
I'm not happy with this JS loading due to the two reasons below:
48+
49+
1. I have to explicitly give the option of `autostart="false"` while loading the `blazor.webassembly.js` file, which is extra to the bootstrapper.
50+
2. I have to append `js/main.js` through the Promise pattern after `Blazor.start()`, which is another extra point to the bootstrapper.
51+
52+
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?
53+
54+
55+
## Chrome Extension &ndash; JS Interop Step #1 ##
56+
57+
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.
58+
59+
```html
60+
<!DOCTYPE html>
61+
<html lang="en">
62+
...
63+
<body>
64+
<div id="app">Loading...</div>
65+
...
66+
<!-- ⬇️⬇️⬇️ Add this line ⬇️⬇️⬇️ -->
67+
<script src="js/main.js"></script>
68+
<!-- ⬆️⬆️⬆️ Add this line ⬆️⬆️⬆️ -->
69+
<script src="_framework/blazor.webassembly.js"></script>
70+
</body>
71+
</html>
72+
```
73+
74+
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.
75+
76+
```javascript
77+
function loadJs(sourceUrl) {
78+
if (sourceUrl.Length == 0) {
79+
console.error("Invalid source URL");
80+
return;
81+
}
82+
83+
var tag = document.createElement('script');
84+
tag.src = sourceUrl;
85+
tag.type = "text/javascript";
86+
87+
tag.onload = function () {
88+
console.log("Script loaded successfully");
89+
}
90+
91+
tag.onerror = function () {
92+
console.error("Failed to load script");
93+
}
94+
95+
document.body.appendChild(tag);
96+
}
97+
```
98+
99+
Update the `Popup.razor` file like below:
100+
101+
1. Add the `@inject` declaration for the `IJSRuntime` instance as a dependency.
102+
2. Call the `JS.InvokeVoidAsync` method to load the `js/popup.js` file by invoking the `loadJs` function from the `js/main.js` file.
103+
104+
```razor
105+
@* Popup.razor *@
106+
107+
@page "/popup.html"
108+
109+
@* Inject IJSRuntime instance *@
110+
@inject IJSRuntime JS
111+
112+
...
113+
114+
@code {
115+
protected override async Task OnAfterRenderAsync(bool firstRender)
116+
{
117+
if (!firstRender)
118+
{
119+
return;
120+
}
121+
122+
var src = "js/popup.js";
123+
124+
// Invoke the `loadJs` function
125+
await JS.InvokeVoidAsync("loadJs", src).ConfigureAwait(false);
126+
}
127+
}
128+
```
129+
130+
Update the `Options.razor` file in the same way.
131+
132+
```razor
133+
@* Options.razor *@
134+
135+
@page "/options.html"
136+
137+
@* Inject IJSRuntime instance *@
138+
@inject IJSRuntime JS
139+
140+
...
141+
142+
@code {
143+
protected override async Task OnAfterRenderAsync(bool firstRender)
144+
{
145+
if (!firstRender)
146+
{
147+
return;
148+
}
149+
150+
var src = "js/options.js";
151+
152+
// Invoke the `loadJs` function
153+
await JS.InvokeVoidAsync("loadJs", src).ConfigureAwait(false);
154+
}
155+
}
156+
```
157+
158+
Now, we don't need the reference replacement part in the PowerShell script. Let's comment them out.
159+
160+
```powershell
161+
# Run-PostBuild.ps1
162+
163+
...
164+
165+
# Update-FileContent `
166+
# -Filename "./published/wwwroot/popup.html" `
167+
# -Value1 "js/main.js" `
168+
# -Value2 "js/popup.js"
169+
170+
# Update-FileContent `
171+
# -Filename "./published/wwwroot/options.html" `
172+
# -Value1 "js/main.js" `
173+
# -Value2 "js/options.js"
174+
```
175+
176+
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.
177+
178+
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?
179+
180+
181+
## Chrome Extension &ndash; JS Interop Step #2 ##
182+
183+
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.
184+
185+
```html
186+
<!DOCTYPE html>
187+
<html lang="en">
188+
189+
<head>
190+
<meta charset="utf-8" />
191+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
192+
<title>ChromeExtensionV2</title>
193+
<base href="/" />
194+
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
195+
<link href="css/app.css" rel="stylesheet" />
196+
<link href="ChromeExtensionV2.styles.css" rel="stylesheet" />
197+
</head>
198+
199+
<body>
200+
<div id="app">Loading...</div>
201+
202+
<div id="blazor-error-ui">
203+
An unhandled error has occurred.
204+
<a href="" class="reload">Reload</a>
205+
<a class="dismiss">🗙</a>
206+
</div>
207+
<script src="_framework/blazor.webassembly.js"></script>
208+
</body>
209+
</html>
210+
```
211+
212+
Then, add the `export` declaration in front of the `loadJs` function in the `js/main.js` file.
213+
214+
```javascript
215+
export function loadJs(sourceUrl) {
216+
...
217+
}
218+
```
219+
220+
Update `Popup.razor` like below.
221+
222+
```csharp
223+
...
224+
var src = "js/popup.js";
225+
226+
// Import the `js/main.js` file
227+
var module = await JS.InvokeAsync<IJSObjectReference>("import", "./js/main.js").ConfigureAwait(false);
228+
229+
// Invoke the `loadJs` function
230+
await module.InvokeVoidAsync("loadJs", src).ConfigureAwait(false);
231+
```
232+
233+
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`.
234+
235+
```json
236+
{
237+
"manifest_version": 2,
238+
"version": "1.0",
239+
"name": "Getting Started Example (Blazor WASM)",
240+
"description": "Build an Extension!",
241+
242+
...
243+
244+
"content_security_policy": "script-src 'self' 'unsafe-eval' 'wasm-unsafe-eval' 'sha256-v8v3RKRPmN4odZ1CWM5gw80QKPCCWMcpNeOmimNL2AA='; object-src 'self'",
245+
246+
...
247+
}
248+
```
249+
250+
Build and publish the app, and run the PowerShell script against the artifact. Then, reload the extension, and you will see the same result.
251+
252+
---
253+
254+
So far, we've walked through how to take more advantage of the [JS Interop][blazor wasm jsinterop] feature that [Blazor WASM][blazor wasm] offers to migrate the existing Chrome extension to Blazor WASM. What could be the potential merits of this exercise?
255+
256+
1. We never touch any bootstrapper codes that Blazor WASM generate for us.
257+
2. If necessary, we load JavaScript for each page using the JS Interop feature. During this practice, C# handles all the JS codes.
258+
259+
If we use more JS Interop features, we can build the Blazor WASM app more effectively, which will be another option for building Chrome extensions.
260+
261+
262+
## Do you want to know more about Blazor? ##
263+
264+
Here are some tutorials for you.
265+
266+
* [Blazor][blazor]
267+
* [Blazor Tutorial][blazor tutorial]
268+
* [Blazor Learn][blazor learn]
269+
270+
271+
[post 1]: /2022/07/08/lift-and-shift-existing-chrome-extension-to-blazor-wasm/
272+
[post 2]: /2022/07/20/lift-and-shift-existing-chrome-extension-to-blazor-wasm-2/
273+
274+
[gh sample]: https://github.com/devkimchi/blazor-wasm-chrome-extension/tree/the-integration
275+
[gh sample v2 blazor]: https://github.com/devkimchi/blazor-wasm-chrome-extension/tree/the-integration/src/ChromeExtensionV2
276+
277+
[chrome extension]: https://developer.chrome.com/docs/extensions/
278+
[chrome extension v2]: https://developer.chrome.com/docs/extensions/mv2/getstarted/
279+
280+
[blazor]: https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor?WT.mc_id=dotnet-70466-juyoo
281+
[blazor tutorial]: https://dotnet.microsoft.com/learn/aspnet/blazor-tutorial/intro?WT.mc_id=dotnet-70466-juyoo
282+
[blazor learn]: https://docs.microsoft.com/learn/paths/build-web-apps-with-blazor/?WT.mc_id=dotnet-70466-juyoo
283+
[blazor wasm]: https://docs.microsoft.com/aspnet/core/blazor/host-and-deploy/webassembly?view=aspnetcore-6.0&WT.mc_id=dotnet-70466-juyoo
284+
[blazor wasm jsinterop]: https://docs.microsoft.com/aspnet/core/blazor/javascript-interoperability/?view=aspnetcore-6.0&WT.mc_id=dotnet-70466-juyoo

0 commit comments

Comments
 (0)