Расширение Chrome с Blazor WASM — интеграция

В своем предыдущем посте я рассказал о том, как с минимальными изменениями кода перенести расширение Chrome на базе JavaScript в Blazor WASM. Хотя оно успешно перешло на Blazor WASM, оно не полностью использует функцию JavaScript Interoperability (JS interop), которая является мощной особенностью Blazor WASM. Я буду рассматривать эту функцию на протяжении всего этого поста.

Вы можете загрузить пример расширения для Chrome из этого репозитория GitHub:

Образец расширения браузера Blazor WASM

Здесь представлены примеры кода для кросс-браузерного расширения, построенного на Blazor WASM. Этот пример приложения изначально был создан для создания расширения Chrome с помощью Blazor WASM, но теперь он поддерживает кроссбраузерность, включая браузеры на базе Chromium и Mozilla FireFox.

Благодарность

Этот пример кода включает WebExtension browser API Polyfill от Mozilla, который лицензирован под MPL 2.0.

Начало работы

  1. Создайте приложение

    dotnet build .
    Войдите в полноэкранный режим Выйти из полноэкранного режима
  2. Опубликовать приложение

    dotnet publish ./src/ChromeExtensionV2/ -c Release -o published
    Войдите в полноэкранный режим Выйти из полноэкранного режима
  3. Запуск сценария PowerShell

    ./Run-PostBuild.ps1
    Войти в полноэкранный режим Выйти из полноэкранного режима
  4. Зарегистрируйте расширение в браузере на базе Chromium, например Chrome или Edge, или Mozilla FireFox.

  5. Посетите любой сайт на https://developer.chrome.com, https://developer.mozilla.org или https://docs.microsoft.com.

  6. Запустите расширение, нажав на значок в верхней части веб-браузера.

Посмотреть на 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 по двум причинам, указанным ниже:

  1. Мне приходится явно указывать опцию autostart="false" при загрузке файла blazor.webassembly.js, который является дополнительным для bootstrapper.
  2. Мне приходится добавлять 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 как показано ниже:

  1. Добавьте объявление @inject для экземпляра IJSRuntime в качестве зависимости.
  2. Вызовите метод 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. Каковы могут быть потенциальные преимущества этого упражнения?

  1. Мы никогда не прикасаемся к кодам бутстраппера, которые Blazor WASM генерирует для нас.
  2. При необходимости мы загружаем JavaScript для каждой страницы, используя функцию JS Interop. Во время этой практики C# обрабатывает все JS-коды.

Итак, приносит ли это упражнение только пользу? Вот несколько соображений:

  1. Код становится чрезмерно сложным. Если мы просто импортируем файлы JavaScript через index.html/popup.html/options.html, нам не нужно выполнять это упражнение.
  2. Не каждый раз динамическая загрузка JS полезна. У нее есть компромиссы. Если вы не хотите трогать файлы бутстраппера, попробуйте использовать подход, описанный в этом посте. Но если вы все же трогаете файлы бутстраппера, то этот подход динамической загрузки JS может оказаться непригодным.

В целом, если мы будем использовать больше возможностей JS Interop должным образом, мы сможем более эффективно создавать приложение Blazor WASM, которое станет еще одним вариантом для создания расширений Chrome. В следующем посте я собираюсь обсудить кроссбраузерную совместимость для этого браузерного расширения на основе Blazor WASM.

Хотите узнать больше о Blazor?

Вот несколько руководств для вас.

  • Blazor
  • Самоучитель Blazor
  • Изучение Blazor

Оцените статью
devanswers.ru
Добавить комментарий