WebAssemblyを1MBに最適化する方法?

WebAssemblyを1MBに最適化する方法?

WebAssemblyを1MBに最適化する

最終更新 2023/01/30 22:35
Token
読了目安 5 分
カテゴリ
Blazor
タグ
.NET Blazor Wasm WebAssembly

Blazor WebAssembly 読み込み最適化方案

Blazor WebAssembly の読み込み最適化は、WebAssembly の初回読み込み時に .NET Core のすべてのアセンブリがブラウザに読み込まれ、さらに使用時にサードパーティの DLL が多数参照されることで読み込みが遅くなる問題に対処します。

最適化方案:

1. 圧縮

Blazor WebAssembly アプリケーションを発行する際、出力内容は発行プロセス中に静的に圧縮され、アプリケーションサイズが小さくなり、実行時の圧縮オーバーヘッドが排除されます。以下の圧縮アルゴリズムが使用されます:

google/brotli GitHub リポジトリ から JavaScript Brotli デコーダーを入手してください。縮小されたデコーダーファイルは decode.min.js という名前で、リポジトリの js フォルダ にあります。

wwwroot/index.html ファイルのコードを変更し、autostart="false" を追加してデフォルトの起動時のアセンブリ読み込みを阻止します。

<script src="_framework/blazor.webassembly.js" autostart="false"></script>

Blazor の <script> タグの後、かつ終了 </body> タグの前に、以下の JavaScript コードを含む <script> ブロックを追加します。

<script type="module">
  import { BrotliDecode } from './decode.min.js';
  Blazor.start({
    loadBootResource: function (type, name, defaultUri, integrity) {
      // 注意:localhost を使用する場合、圧縮は有効になりません
      if (type !== 'dotnetjs' && location.hostname !== 'localhost') {
        return (async function () {
          const response = await fetch(defaultUri + '.br', { cache: 'no-cache' });
          if (!response.ok) {
            throw new Error(response.statusText);
          }
          const originalResponseBuffer = await response.arrayBuffer();
          const originalResponseArray = new Int8Array(originalResponseBuffer);
          const decompressedResponseArray = BrotliDecode(originalResponseArray);
          const contentType = type ===
            'dotnetwasm' ? 'application/wasm' : 'application/octet-stream';
          return new Response(decompressedResponseArray,
            { headers: { 'content-type': contentType } });
        })();
      }
    }
  });
</script>

圧縮方式により読み込み時間が短縮され、圧縮 DLL のサイズは約 3 分の 1 になります。効果は以下の図の通りです。

autostart="false" を使用すると、起動時にすぐには読み込まれず、アセンブリの読み込みは上記のコードブロックで実行されます。デフォルトでは br ファイルが読み込まれます。

2. アセンブリの遅延読み込み

必要な時までアプリケーションアセンブリの読み込みを待機させることで、Blazor WebAssembly アプリケーションの起動パフォーマンスを向上させる方法を「遅延読み込み」と呼びます。

Blazor WebAssembly プロジェクトを細かく分割し、アセンブリを遅延読み込みすることで初回読み込み時間を改善します。ここでは、遅延読み込みを説明するサンプルを使用します。

空の Blazor WebAssembly プロジェクトを作成します。プロジェクト名は Demand です。

HTTPS と「Progressive Web アプリケーション」のチェックを外します。

Razor クラスライブラリを作成します。プロジェクト名は Demand.Components とし、デフォルトオプションでプロジェクトを作成します。

Components.razor ファイルを作成し、不要なファイルを削除します。結果は以下の図のようになります。

Components.razor に以下のコードを追加します。

@inject NavigationManager NavigationManager @page "/components"

<div>
  <h1>Components</h1>
</div>
<button @onclick="Goto">トップページへ移動</button>
@code { private void Goto() { NavigationManager.NavigateTo("/"); } }

Demand プロジェクトで Demand.Components プロジェクトを参照します。

App.razor ファイルを以下のように変更します。

@using System.Reflection @using
Microsoft.AspNetCore.Components.WebAssembly.Services @*
ここで注意:WebAssembly ではデフォルトで注入されますが、Server では注入されません。
Server では手動で builder.Services.AddScoped<LazyAssemblyLoader
  >(); を追加する必要があります。 *@ @inject LazyAssemblyLoader AssemblyLoader

  <Router
    AppAssembly="@typeof(App).Assembly"
    AdditionalAssemblies="@lazyLoadedAssemblies"
    OnNavigateAsync="@OnNavigateAsync"
  >
    <Found Context="routeData">
      <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
      <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
      <PageTitle>Not found</PageTitle>
      <LayoutView Layout="@typeof(MainLayout)">
        <p role="alert">申し訳ございません。このアドレスには何もありません。</p>
      </LayoutView>
    </NotFound>
  </Router>

  @code { private List<Assembly>
    lazyLoadedAssemblies = new(); private async Task
    OnNavigateAsync(NavigationContext args) { try { if (args.Path ==
    "components") { // ここで Demand.Components が依存するアセンブリをカスタマイズ var assemblies
    = await AssemblyLoader.LoadAssembliesAsync(new[] { "Demand.Components.dll"
    }); // ルートアセンブリスキャンに追加 lazyLoadedAssemblies.AddRange(assemblies); } }
    catch (Exception ex) { } } }</Assembly
  ></LazyAssemblyLoader
>

指定されたルートコンポーネントに必要なアセンブリを処理します。

Demand プロジェクトファイルを開き、Debug モードでは以下の無視リストを追加できます。

<ItemGroup>
    <BlazorWebAssemblyLazyLoad Include="System.Xml.dll" />
    <BlazorWebAssemblyLazyLoad Include="System.Xml.XmlSerializer.dll" />
    ... (以下、リストは元のまま保持)
</ItemGroup>

これらはあまり使用されないアセンブリです。以下のエラーが発生した場合は、見つからないアセンブリを削除してオンデマンド読み込み設定を調整してください

ただし、上記のオンデマンド読み込み設定を発行時に使用すると、以下の図のような例外が発生する可能性があります。原因は、Blazor WebAssembly が発行時にデフォルトでトリミングを有効にし、以下のアセンブリがちょうど使用されていないため、トリミング後にオンデマンド読み込みが設定されますが、既にトリミングされているため、オンデマンド読み込み対象のアセンブリが見つからなくなります。エラーが発生するアセンブリを削除すれば解決します。この問題は発行時にのみ発生し、Debug では上記のオンデマンド読み込み設定を引き続き使用でき、デバッグ時の応答を速くすることができます。

次に進みます。

指定プロジェクトのオンデマンド読み込み設定を追加します。Demand.Components プロジェクトを設定します。

<ItemGroup>
    <BlazorWebAssemblyLazyLoad Include="Demand.Components.dll" />
</ItemGroup>

Pages/Index.razor ファイルのコードを変更します。

@page "/" @inject NavigationManager NavigationManager
<h1>Hello, world!</h1>

<button @onclick="Goto">components へ移動</button>
@code { private void Goto() { NavigationManager.NavigateTo("/components"); } }

プロジェクトを起動し、F12 開発者ツールを開いて「アプリケーション」タブを選択し、「ストレージ」から「サイトデータを消去」をクリックします(初回読み込み後、アセンブリはキャッシュされます)。

「ネットワーク」タブをクリックし、ページをリフレッシュします。Demand.Components.dll は読み込まれていないことがわかります。ここにあるアセンブリは以下の通りです。

次に、ページ上のボタンをクリックします。

その時点で「ネットワーク」タブを確認すると、Demand.Components.dll が読み込まれていることがわかります。このアセンブリは使用時にのみ読み込まれ、2 回目に同じページに移動しても再読み込みは行われません。

次にプロジェクトを発行します(発行時には、上記で述べたトリミングによるアセンブリ消失でオンデマンド読み込みが使えなくなる問題に注意してください。オンデマンド読み込みの設定からトリミングされたアセンブリを削除すれば解決します)。

次に、docker compose を使用して nginx プロキシをデプロイし、効果を確認します。

docker-compose.yml ファイルを作成し、以下のコードを追加します。docker-compose.yml と同じディレクトリに conf.dwwwroot の 2 つのフォルダを作成します。

services:
  nginx:
    image: nginx:stable-alpine
    container_name: nginx
    volumes:
      - ./conf.d:/etc/nginx/conf.d
      - ./wwwroot:/wwwroot
    ports:
      - 811:80

conf.d フォルダに webassembly.conf を作成し、以下のコードを追加します。

server {
    listen 80;
    server_name http://localhost;

    location / {
        root /wwwroot;
        index index.html;
    }

}

次に、docker-compose.yml が置かれているディレクトリで docker-compose up -d を実行して nginx サービスを起動します。

ブラウザで http://127.0.0.1:811/ にアクセスします(localhost でアクセスしないでください。デフォルトでは圧縮が有効になりません)。F12 開発者ツールを開き、「アプリケーション」タブでストレージを消去し、「ネットワーク」タブを開いてブラウザをリフレッシュします。読み込みが完了し、最適化によって 2.3MB になりました。圧縮が有効になり、発行時に未使用のアセンブリがトリミングされました。

極限最適化 ~ 1MB

Demand プロジェクトファイルに以下の設定を追加します。これらの設定はグローバリゼーションなどの一部機能を無効にします。

<PublishTrimmed>true</PublishTrimmed>
<InvariantGlobalization>true</InvariantGlobalization>
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
<EventSourceSupport>false</EventSourceSupport>
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
<UseNativeHttpHandler>true</UseNativeHttpHandler>

次に、上記の手順を繰り返して発行し、nginx にデプロイします。

ネットワークタブで読み込みサイズを確認すると、1MB になっています。いくつかの JS ファイルを除外すればさらに小さくなるはずで、これにより読み込みの問題が大幅に改善されました(小夜鲲 氏の提案によるものです)。

おわりに

より良い最適化案があれば、ご連絡ください。

Token による共有です。

blazor 交流グループ:452761192

本記事は転載です。

著者:Token

原文タイトル:WebAssembly を 1MB に最適化する方法

原文リンク:https://www.cnblogs.com/hejiale010426/p/17076817.html

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2024/02/29

Winformでもこんなデータ表示ができる

winform開発の過程で、データ表示機能が必要になることがよくあります。これまではgridcontrolコントロールを使用していましたが、今日は例を通して、winform blazor hybridでant design blazorのtableコンポーネントを使ってデータ表示を行う方法を紹介します。

続きを読む
同じカテゴリ / 同じタグ 2024/02/29

Winformの画面も綺麗にできる?

先日、winformでblazor hybridを使用することを紹介しました。また、blazorのUIを組み合わせることでwinformプログラムのデザインをより美しくできると言いました。今回はwinform blazor hybridで描画する例を挙げて説明します。参考になれば幸いです。

続きを読む
同じカテゴリ / 同じタグ 2024/01/07

码坊「記事タイトルURLエイリアス生成器」リリース

码坊は、ウェブオンラインツール、クロスプラットフォームのデスクトップおよびモバイルアプリを提供するオープンソースプロジェクトです。運営者は、より効率的で便利な使い心地を提供することに全力を尽くしています。本日、「記事タイトルURLエイリアス生成器」をリリースし、記事タイトルのURLエイリアスを簡単に作成し、SEO効果とユーザーエクスペリエンスを向上させるお手伝いをします。ぜひ码坊で、さらに便利なツールを見つけてください!

続きを読む