В своем предыдущем посте я рассказал о том, как с минимальными изменениями кода перенести расширение Chrome на базе JavaScript в Blazor WASM. Хотя оно успешно перешло на Blazor WASM, оно не полностью использует функцию JavaScript Interoperability (JS interop), которая является мощной особенностью Blazor WASM. Я буду рассматривать эту функцию на протяжении всего этого поста.
Вы можете загрузить пример расширения для Chrome из этого репозитория GitHub:
Содержание
- devkimchi / blazor-wasm-chrome-extension
- Здесь представлены примеры кодов для расширения Chrome, построенного на Blazor WASM
- Образец расширения браузера Blazor WASM
- Благодарность
- Начало работы
- Расширение Chrome — Before JS Interop
- Расширение Chrome — JS Interop Шаг №1
- Расширение Chrome — JS Interop Шаг №2
- Хотите узнать больше о Blazor?
devkimchi / blazor-wasm-chrome-extension
Здесь представлены примеры кодов для расширения Chrome, построенного на Blazor WASM
Образец расширения браузера Blazor WASM
Здесь представлены примеры кода для кросс-браузерного расширения, построенного на Blazor WASM. Этот пример приложения изначально был создан для создания расширения Chrome с помощью Blazor WASM, но теперь он поддерживает кроссбраузерность, включая браузеры на базе Chromium и Mozilla FireFox.
Благодарность
Этот пример кода включает WebExtension
browser
API Polyfill от Mozilla, который лицензирован под MPL 2.0.Начало работы
Создайте приложение
dotnet build .Войдите в полноэкранный режим Выйти из полноэкранного режима Опубликовать приложение
dotnet publish ./src/ChromeExtensionV2/ -c Release -o publishedВойдите в полноэкранный режим Выйти из полноэкранного режима Запуск сценария PowerShell
./Run-PostBuild.ps1Войти в полноэкранный режим Выйти из полноэкранного режима Зарегистрируйте расширение в браузере на базе Chromium, например Chrome или Edge, или Mozilla FireFox.
Посетите любой сайт на https://developer.chrome.com, https://developer.mozilla.org или https://docs.microsoft.com.
Запустите расширение, нажав на значок в верхней части веб-браузера.
Посмотреть на GitHub
Расширение Chrome — Before JS Interop
Файл index.html
, написанный в предыдущем посте, выглядит следующим образом. Сначала загружается blazor.webassembly.js
с опцией autostart="false"
, затем загружается js/main.js
через вызов функции. Ссылка js/main.js
заменяется на js/options.js
или js/popup.js
в процессе генерации артефакта.
<!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>
Меня не устраивает такая загрузка JS по двум причинам, указанным ниже:
- Мне приходится явно указывать опцию
autostart="false"
при загрузке файлаblazor.webassembly.js
, который является дополнительным для bootstrapper. - Мне приходится добавлять
js/main.js
через шаблон Promise послеBlazor.start()
, что является еще одним лишним моментом в бутстраппере.
Можно ли минимизировать эту модификацию оригинального файла index.html
и использовать здесь больше возможностей JS Interop, чтобы сделать его более похожим на Blazor?
Расширение Chrome — JS Interop Шаг №1
Давайте обновим файл index.html
. В отличие от предыдущего обновления, удалите часть JS, вызывающую функцию Blazor.start()
. Загрузите js/main.js
перед загрузкой blazor.webassembly.js
. Удалите также атрибут autostart="false"
.
<!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>
Первоначально файл js/main.js
был пустым, но на этот раз добавим следующую JS-функцию, которая добавляет еще один тег script
для загрузки заданной ссылки на JS-файл.
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);
}
Обновите файл Popup.razor
как показано ниже:
- Добавьте объявление
@inject
для экземпляраIJSRuntime
в качестве зависимости. - Вызовите метод
JS.InvokeVoidAsync
для загрузки файлаjs/popup.js
, вызвав функциюloadJs
из файлаjs/main.js
.
@* 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);
}
}
Обновите файл Options.razor
таким же образом.
@* 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);
}
}
Теперь нам не нужна часть замены ссылок в сценарии PowerShell. Давайте закомментируем их.
# 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"
Создайте и опубликуйте приложение Blazor WASM, затем запустите сценарий PowerShell, чтобы подготовиться к загрузке расширения. После перезагрузки расширения оно работает без проблем. Функция loadJs
является ключом, который использует возможности JS Interop.
Однако меня все еще не устраивает добавление js/main.js
в index.html
. Можно ли удалить эту часть из файла и использовать функцию JS Interop вместо этого?
Расширение Chrome — JS Interop Шаг №2
Давайте вернем index.html
в исходное состояние, когда bootstrapper создает файл. Тогда все, что мы можем увидеть в index.html
— это ссылку на файл blazor.webassembly.js
.
<!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>
Затем добавьте объявление export
перед функцией loadJs
в файл js/main.js
.
export function loadJs(sourceUrl) {
...
}
Обновите Popup.razor
, как показано ниже.
...
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);
Это же изменение должно быть применимо и к Options.razor
. И, наконец, обновите manifest.json
, как показано ниже, поскольку нам больше не нужен хэш-ключ для popup.js
и 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'",
...
}
Создайте и опубликуйте приложение и запустите сценарий PowerShell против артефакта. Затем перезагрузите расширение, и вы увидите тот же результат.
До сих пор мы рассмотрели, как использовать преимущества функции JS Interop, которую предлагает Blazor WASM, для переноса существующего расширения Chrome на Blazor WASM. Каковы могут быть потенциальные преимущества этого упражнения?
- Мы никогда не прикасаемся к кодам бутстраппера, которые Blazor WASM генерирует для нас.
- При необходимости мы загружаем JavaScript для каждой страницы, используя функцию JS Interop. Во время этой практики C# обрабатывает все JS-коды.
Итак, приносит ли это упражнение только пользу? Вот несколько соображений:
- Код становится чрезмерно сложным. Если мы просто импортируем файлы JavaScript через
index.html
/popup.html
/options.html
, нам не нужно выполнять это упражнение. - Не каждый раз динамическая загрузка JS полезна. У нее есть компромиссы. Если вы не хотите трогать файлы бутстраппера, попробуйте использовать подход, описанный в этом посте. Но если вы все же трогаете файлы бутстраппера, то этот подход динамической загрузки JS может оказаться непригодным.
В целом, если мы будем использовать больше возможностей JS Interop должным образом, мы сможем более эффективно создавать приложение Blazor WASM, которое станет еще одним вариантом для создания расширений Chrome. В следующем посте я собираюсь обсудить кроссбраузерную совместимость для этого браузерного расширения на основе Blazor WASM.
Хотите узнать больше о Blazor?
Вот несколько руководств для вас.
- Blazor
- Самоучитель Blazor
- Изучение Blazor