ASP.NET Core Blazor の起動

この記事では、Blazor 起動を構成する方法について説明します。

Blazor 起動プロセスは、スクリプト (Blazor) blazor.{server|webassembly}.js を介して自動かつ非同期に行われます。ここで、{server|webassembly} プレースホルダーは Blazor Server の場合は server、Blazor WebAssembly の場合は webassembly です。 Blazor<script> タグは、Blazor WebAssembly アプリの wwwroot/index.html ファイルまたはBlazor Server アプリの Pages/_Host.cshtml ファイルにあります。

Blazor 起動プロセスは、スクリプト (Blazor) blazor.{server|webassembly}.js を介して自動かつ非同期に行われます。ここで、{server|webassembly} プレースホルダーは Blazor Server の場合は server、Blazor WebAssembly の場合は webassembly です。 Blazor<script> タグは、Blazor WebAssembly アプリの wwwroot/index.html ファイルまたはBlazor Server アプリの Pages/_Layout.cshtml ファイルにあります。

Blazor 起動プロセスは、スクリプト (Blazor) blazor.{server|webassembly}.js を介して自動かつ非同期に行われます。ここで、{server|webassembly} プレースホルダーは Blazor Server の場合は server、Blazor WebAssembly の場合は webassembly です。 Blazor<script> タグは、Blazor WebAssembly アプリの wwwroot/index.html ファイルまたはBlazor Server アプリの Pages/_Host.cshtml ファイルにあります。

Blazor を手動で開始するには:

  • autostart="false" 属性と値を Blazor<script> タグに追加します。
  • Blazor.start() を呼び出すスクリプトを、Blazor<script> タグの後の終了 </body> タグ内に配置します。

JavaScript イニシャライザー

JavaScript (JS) イニシャライザーによって、Blazor アプリの読み込みの前後にロジックが実行されます。 JS イニシャライザーは、次のシナリオで役に立ちます。

  • Blazor アプリの読み込み方法のカスタマイズ。
  • Blazor 開始前のライブラリの初期化。
  • Blazor の設定の構成。

JS イニシャライザーは、ビルド処理の一部として検出されて、Blazor アプリに自動的にインポートされます。 多くの場合、JS イニシャライザーを使用すると、Razor クラス ライブラリ (RCL) を使用するときにアプリからスクリプト関数を手動でトリガーする必要がなくなります。

JS イニシャライザーを定義するには、JS モジュールを {NAME}.lib.module.js という名前のプロジェクトに追加します。{NAME} プレースホルダーは、アセンブリ名、ライブラリ名、またはパッケージ識別子です。 ファイルをプロジェクトの Web ルートに配置します。通常、これは wwwroot フォルダーです。

そのモジュールで、次の従来の関数のいずれかまたは両方をエクスポートします。

  • beforeStart(options, extensions): Blazor が開始する前に呼び出されます。 たとえば、beforeStart は、読み込みプロセス、ログ レベル、およびホスティング モデルに固有のその他のオプションをカスタマイズするために使用されます。
    • Blazor WebAssembly の場合、beforeStart は、発行中に追加される Blazor WebAssembly のオプション (このセクションの例では options) と拡張機能 (このセクションの例では extensions) を受け取ります。 たとえば、オプションによってカスタムのブート リソース ローダーの使用を指定できます。
    • Blazor Server の場合、beforeStart は SignalR 回線開始オプション (このセクションの例では options) を受け取ります。
    • BlazorWebViews では、オプションは渡されません。
  • afterStarted: Blazor が JS からの呼び出しを受け取れる状態になった後で呼び出されます。 たとえば、afterStarted は、JS 相互運用呼び出しを行い、カスタム要素を登録することによって、ライブラリを初期化するために使用されます。 afterStarted には、Blazor インスタンスが引数として渡されます (このセクションの例では blazor)。

ファイル名については、次のようにします。

  • JS イニシャライザーがプロジェクトの静的アセットとして使われる場合は、{ASSEMBLY NAME}.lib.module.js という形式を使います。{ASSEMBLY NAME} プレースホルダーはアプリのアセンブリ名です。 たとえば、プロジェクトのアセンブリ名が BlazorSample である場合は、ファイルを BlazorSample.lib.module.js という名前にします。 ファイルをアプリの wwwroot フォルダーに配置します。
  • JS イニシャライザーが RCL から使われる場合は、{LIBRARY NAME/PACKAGE ID}.lib.module.js という形式を使います。{LIBRARY NAME/PACKAGE ID} プレースホルダーは、プロジェクトのライブラリ名またはパッケージ識別子です。 たとえば、RCL のパッケージ識別子が RazorClassLibrary1 である場合は、ファイルを RazorClassLibrary1.lib.module.js という名前にします。 ファイルをライブラリの wwwroot フォルダーに配置します。

次に示すのは、beforeStartafterStarted<head> に追加することで、Blazor が開始される前と後にカスタム スクリプトが読み込まれる JS イニシャライザーの例です。

export function beforeStart(options, extensions) {
  var customScript = document.createElement('script');
  customScript.setAttribute('src', 'beforeStartScripts.js');
  document.head.appendChild(customScript);
}

export function afterStarted(blazor) {
  var customScript = document.createElement('script');
  customScript.setAttribute('src', 'afterStartedScripts.js');
  document.head.appendChild(customScript);
}

前述の beforeStart の例では、Blazor の開始前にカスタム スクリプトが読み込まれることのみが保証されています。 スクリプトで待機中の Promise の実行が Blazor の開始前に完了することは保証されません。

Note

MVC および Razor Pages アプリでは、JS イニシャライザーは自動的に読み込まれません。 ただし、アプリのマニフェストをフェッチして、JS イニシャライザーの読み込みをトリガーするスクリプトを、開発者コードに含めることができます。

JS イニシャライザーの例については、次のリソースを参照してください。

注意

通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。

ライブラリが特定の順序で読み込まれるようにする

カスタム スクリプトを、読み込む必要がある順序で beforeStartafterStarted<head> に追加します。

次の例では、script2.js の前に script1.jsscript4.js の前に script3.js を読み込みます。

export function beforeStart(options, extensions) {
    var customScript1 = document.createElement('script');
    customScript1.setAttribute('src', 'script1.js');
    document.head.appendChild(customScript1);

    var customScript2 = document.createElement('script');
    customScript2.setAttribute('src', 'script2.js');
    document.head.appendChild(customScript2);
}

export function afterStarted(blazor) {
    var customScript1 = document.createElement('script');
    customScript1.setAttribute('src', 'script3.js');
    document.head.appendChild(customScript1);

    var customScript2 = document.createElement('script');
    customScript2.setAttribute('src', 'script4.js');
    document.head.appendChild(customScript2);
}

追加のモジュールをインポートする

JS の初期化子ファイル (*.lib.module.js) で最上位の import ステートメントを使用して、追加のモジュールをインポートします。

additionalModule.js:

export function logMessage() {
  console.log('logMessage is logging');
}
import { logMessage } from "/additionalModule.js";

export function beforeStart(options, extensions) {
  ...

  logMessage();
}

マップをインポートする

マップのインポートは、ASP.NET Core と Blazor でサポートされています。

ドキュメントの準備完了時に Blazor を初期化する

次の例では、ドキュメントの準備が整うと Blazor が起動されます。

<body>
    ...

    <script src="_framework/blazor.{server|webassembly}.js" autostart="false"></script>
    <script>
      document.addEventListener("DOMContentLoaded", function() {
        Blazor.start();
      });
    </script>
</body>

前述のマークアップの {server|webassembly} プレースホルダーは、Blazor Server アプリの場合は server、Blazor WebAssembly アプリの場合は webassembly です。

手動で起動した結果として得た Promise に連結する

JS 相互運用の初期化など、追加のタスクを実行するには、then を使用して、手動で Blazor アプリを起動させた結果として得た Promise に連結します。

<body>
    ...

    <script src="_framework/blazor.{server|webassembly}.js" autostart="false"></script>
    <script>
      Blazor.start().then(function () {
        ...
      });
    </script>
</body>

前述のマークアップの {server|webassembly} プレースホルダーは、Blazor Server アプリの場合は server、Blazor WebAssembly アプリの場合は webassembly です。

Note

Blazor の開始後にライブラリで追加のタスクを自動的に実行するには、JavaScript イニシャライザーを使います。 JS イニシャライザーを使う場合は、ライブラリのコンシューマーが JS の呼び出しを Blazor の手動起動にチェーンする必要はありません。

ブート リソースを読み込む

"このセクションは Blazor WebAssembly アプリにのみ適用されます。 "

Blazor WebAssembly アプリがブラウザーに読み込まれると、アプリによってサーバーから次のブート リソースがダウンロードされます。

  • アプリをブートストラップする JavaScript コード
  • .NET ランタイムとアセンブリ
  • ロケール固有のデータ

loadBootResource API を使用して、これらのブート リソースの読み込み方法をカスタマイズします。 loadBootResource 関数によって、組み込みのブート リソース読み込みメカニズムをオーバーライドします。 次のシナリオで loadBootResource を使用します。

  • タイムゾーン データや dotnet.wasm などの静的なリソースを、CDN から読み込む。
  • HTTP 要求を使用して圧縮されたアセンブリを読み込み、サーバーからの圧縮コンテンツのフェッチをサポートしていないホストのクライアントに展開する。
  • fetch 要求を新しい名前にリダイレクトして、リソースを別の名前に設定する。

Note

外部ソースは、ブラウザーがクロスオリジンのリソースの読み込みを許可するために必要なクロス オリジン リソース共有 (CORS) ヘッダーを返す必要があります。 通常、既定では、必要なヘッダーが CDN によって提供されます。

loadBootResource パラメーターは次の表に表示されます。

パラメーター 説明
type リソースの型。 許容される型としては、assemblypdbdotnetjsdotnetwasm、および timezonedata があります。 カスタム動作の型のみ指定する必要があります。 loadBootResource に指定されていない型は、既定の読み込み動作に従ってフレームワークによって読み込まれます。
name リソースの名前。
defaultUri リソースの相対 URI または絶対 URI。
integrity 応答で予想されるコンテンツを表す整合性文字列。

loadBootResource 関数では、URI 文字列を返して、読み込みプロセスをオーバーライドできます。 次の例では、bin/Release/net5.0/wwwroot/_framework からの次のファイルは https://cdn.example.com/blazorwebassembly/5.0.0/ の CDN から提供されます。

  • dotnet.*.js
  • dotnet.wasm
  • タイムゾーン データ

wwwroot/index.html の終了 </body> タグ内:

<script src="_framework/blazor.webassembly.js" autostart="false"></script>
<script>
  Blazor.start({
    loadBootResource: function (type, name, defaultUri, integrity) {
      console.log(`Loading: '${type}', '${name}', '${defaultUri}', '${integrity}'`);
      switch (type) {
        case 'dotnetjs':
        case 'dotnetwasm':
        case 'timezonedata':
          return `https://cdn.example.com/blazorwebassembly/5.0.0/${name}`;
      }
    }
  });
</script>

ブート リソースの URL 以外のものをカスタマイズするには、loadBootResource 関数から fetch を直接呼び出して、結果を返すことができます。 次の例では、カスタム HTTP ヘッダーを送信要求に追加します。 既定の整合性チェックの動作を保持するため、integrity パラメーターを渡します。

wwwroot/index.html の終了 </body> タグ内:

<script src="_framework/blazor.webassembly.js" autostart="false"></script>
<script>
  Blazor.start({
    loadBootResource: function (type, name, defaultUri, integrity) {
      return fetch(defaultUri, { 
        cache: 'no-cache',
        integrity: integrity,
        headers: { 'Custom-Header': 'Custom Value' }
      });
    }
  });
</script>

loadBootResource 関数では、次を返すこともできます。

C# コードでヘッダーを制御する

C# コードで起動時にヘッダーを制御するには、次の方法を使用します。

次の例では、コンテンツ セキュリティ ポリシー (CSP) ヘッダーによって CSP がアプリに適用されます。 {POLICY STRING} プレースホルダーは、CSP ポリシーの文字列です。

  • Blazor Server アプリとプリレンダリング済み Blazor WebAssembly アプリでは、ASP.NET Core ミドルウェアがヘッダー コレクションの制御に使用されます。

Program.cs:

app.Use(async (context, next) =>
{
    context.Response.Headers.Add("Content-Security-Policy", "{POLICY STRING}");
    await next();
});

Startup.csStartup.Configure で:

app.Use(async (context, next) =>
{
    context.Response.Headers.Add("Content-Security-Policy", "{POLICY STRING}");
    await next();
});

前の例ではインライン ミドルウェアを使用していますが、カスタム ミドルウェア クラスを作成し、Program.cs で拡張メソッドを使用してミドルウェアを呼び出すこともできます。 詳細については、「カスタム ASP.NET Core ミドルウェアを記述する」を参照してください。

Server プロジェクトの Program.cs:

var staticFileOptions = new StaticFileOptions
{
    OnPrepareResponse = context =>
    {
        context.Context.Response.Headers.Add("Content-Security-Policy", 
            "{POLICY STRING}");
    }
};

...

app.MapFallbackToFile("index.html", staticFileOptions);

Server プロジェクトの Startup.Configure (Startup.cs):

var staticFileOptions = new StaticFileOptions
{
    OnPrepareResponse = context =>
    {
        context.Context.Response.Headers.Add("Content-Security-Policy", 
            "{POLICY STRING}");
    }
};

...

app.MapFallbackToFile("index.html", staticFileOptions);

CSP について詳しくは、「ASP.NET Core Blazor のコンテンツ セキュリティ ポリシーを適用する」をご覧ください。

読み込みの進行状況の指標

"このセクションは アプリにのみ適用されます。 "

Blazor WebAssembly プロジェクト テンプレートには、スケーラブル ベクター グラフィックス (SVG) と、アプリの読み込みの進行状況を示すテキスト インジケーターが含まれています。

進行状況インジケーターは、Blazor WebAssembly で指定された次の 2 つの CSS カスタム プロパティ (変数) を使用して、HTML と CSS で実装されます。

  • --blazor-load-percentage: 読み込まれたアプリ ファイルの割合。
  • --blazor-load-percentage-text: 読み込まれたアプリ ファイルの割合。最も近い整数に四捨五入されます。

上記の CSS 変数を使用して、アプリのスタイルに一致するカスタム進行状況インジケーターを作成できます。

次に例を示します。

  • resourcesLoaded は、アプリの起動時に読み込まれたリソースのある時点でのカウントです。
  • totalResources は、読み込むリソースの合計数です。
const percentage = resourcesLoaded / totalResources * 100;
document.documentElement.style.setProperty(
  '--blazor-load-percentage', `${percentage}%`);
document.documentElement.style.setProperty(
  '--blazor-load-percentage-text', `"${Math.floor(percentage)}%"`);

既定の円形進行状況インジケーターは、wwwroot/index.html ファイル内の HTML に実装されます。

<div id="app">
    <svg class="loading-progress">
        <circle r="40%" cx="50%" cy="50%" />
        <circle r="40%" cx="50%" cy="50%" />
    </svg>
    <div class="loading-progress-text"></div>
</div>

Blazor WebAssembly プロジェクト テンプレートのマークアップと既定の進行状況インジケーターのスタイルを確認するには、ASP.NET Core 参照ソースをご覧ください。

注意

通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。

既定の円形進行状況インジケーターを使用する代わりに、次の例では線形進行状況インジケーターを実装する方法を示します。

wwwroot/css/app.css に次のスタイルを追加します。

.linear-progress {
    background: silver;
    width: 50vw;
    margin: 20% auto;
    height: 1rem;
    border-radius: 10rem;
    overflow: hidden;
    position: relative;
}

.linear-progress:after {
    content: '';
    position: absolute;
    inset: 0;
    background: blue;
    scale: var(--blazor-load-percentage, 0%) 100%;
    transform-origin: left top;
    transition: scale ease-out 0.5s;
}

CSS 変数 (var(...)) を使用して、--blazor-load-percentage の値を、アプリのファイルの読み込みの進行状況を示す青い擬似要素の scale プロパティに渡します。 アプリが読み込まれると、--blazor-load-percentage が自動的に更新され、進行状況インジケーターの視覚的表現が動的に変更されます。

wwwroot/index.html で、<div id="app">...</div> の既定の SVG 円形インジケーターを削除し、次のマークアップに置き換えます。

<div class="linear-progress"></div>

その他の技術情報