Blazor WebAssembly アプリの GitHub Pages への発行を、より楽にする


Blazor WebAssembly アプリは、GitHub Pages 上にも配置できる

Blazor は C# で SPA を実装するフレームワークやランタイムでありますが、とりわけ Blazor の WebAssembly 版は、JavaScript 製の SPA フレームワークと同じくブラウザ上で動作します。

そのため、静的コンテンツを配置・配信可能な Web サーバーであれば何であっても、そのサーバー上に Blazor WebAssembly アプリの発行成果物を配置すれば、それをブラウザで開いて動作させることができます。

実際、Blazor WebAssembly アプリは、GitHub Pages 上に配置することもできます。

自分が GitHub Pages 上に配置・公開している Blazor WebAssembly アプリを以下に2つほど例示しておきます。

Awesome Blazor Browser

https://jsakamoto.github.io/ (私個人のポートフォリオサイト)

だがしかし GitHub Pages 用に調整するのがダルい...

さて、Blazor WebAssembly (に限らず、JavaScript 製の各種 SPA フレームワークでも同じだと思いますが) を GitHub Pages 向けに "発行" するのは、難しくはないのですが、いくつか "調整" 作業が必要です。

  • (GitHub Pages が備える Jekyll 変換は使わないので) .nojekyll ファイルを追加
  • Git による迂闊な変換がされないよう .gitattribute ファイルを作成
  • index.html 中の <base href="..."/> が指すベース URL を、GitHub Pages に配置後の URL に合せて書き換え ( (a) 発行後に index.html を書き換えるか、(b) 事前に (index.html はもちろん) launchSettings.json も含めてプロジェクトそのもののベース URL を変更しておくか、のいずれか)
  • (index.html をコピーして) 404.html ファイルを追加

ひとつひとつは大した作業ではないです。
っていうか、機械的な単純作業の部類かと思います。

ですが、index.html の変更も含むようなアプリの改訂後に再発行、など、発行作業を繰り返し行なうとき、こんな単純作業を手作業で行なうのもなんだかなぁ、と、常々思っていました。

GitHub Pages 向け発行作業を自動化 & NuGet パッケージ化

そこで、ある日ついに一念発起して、Blazor WebAssembly アプリを GitHub Pages 向けに発行する作業を自動化し、NuGet パッケージとして公開しました。

その NuGet パッケージがこちらです。

ということで、この NuGet パッケージを使って、Blazor WebAssembly アプリを GitHub Pages 向けに発行する作業を説明します。

1. NuGet パッケージ参照をプロジェクトに追加

まずは、なにはともあれ、対象の Blazor WebAssembly プロジェクトに、この NuGet パッケージ参照を追加します。

Windows OS 上で Visual Studio を使って開発中であれば、Visual Studio の NuGet パッケージマネージャウィンドウから "PublishSPAforGitHubPages.Build" を検索して追加したり、

あるいは Visual Studio の NuGet パッケージマネージャコンソールウィンドウにて、追加することもできます。

VisualStudioのNuGetパッケージマネージャコンソールウィンドウにて
PM> Install-Package PublishSPAforGitHubPages.Build 

Visual Studio Code など任意のエディタで作業中でしたら、対象の Blazor WebAssembly のプロジェクトファイル (.csproj) を直接編集し、適当な <ItemGroup> ノードの中に (なければ <ItemGroup> ノードの追加も含めて) 以下のように NuGet パッケージ参照の記述を追記すればよいでしょう。

*.csproj
...
<ItemGroup>
  ...
  <PackageReference Include="PublishSPAforGitHubPages.Build" Version="1.1.0.1" />
  ...
</ItemGroup>
...

もちろん、dotnet CLI で NuGet パッケージ参照を追加することもできます。

プロジェクトファイルがあるフォルダをカレントフォルダとしたターミナルで
$ dotnet add package PublishSPAforGitHubPages.Build

2. GHPages MSBuild プロパティを指定して発行すれば OK!

ひとたび PublishSPAforGitHubPages.Build をプロジェクトに追加した以降は、そのプロジェクトを、GHPages MSBuild プロパティに true を指定して発行するだけです。

例えば dotnet CLI を使って発行する場合は以下のように "-p:GHPages=true" を付けて実行すればよいです。

プロジェクトファイルがあるフォルダをカレントフォルダとしたターミナルで
$ dotnet publish -c:Release -p:GHPages=true

Visual Studio の GUI からファイルへ発行する場合は、発行プロファイルファイル (.pubxml) か、あるいは、プロジェクトファイル (.csproj) を事前に編集しておいて、MSBuild スクリプト内に GHPages MSBuild プロパティに true を指定しておくとよいでしょう。

*.csprojまたは*.pubxml
<PropertyGroup>
  <GHPages>true</GHPages>
</PropertyGroup>

以上、このように GHPages=true の MSBuild プロパティを指定して発行するだけで、発行先のフォルダには、本投稿の冒頭で説明したとおりの、GitHub Pages 向けの調整がすべて施された発行成果物ファイルが生成されるようになります。

さらに最新プレビュー版では、Brotli 圧縮も有効に!

さらに、この NuGet パッケージ PublishSPAforGitHubPages.Build の、(今日時点ではまだプレビュー版に差し止めている) 最新バージョン v.1.1.0.7 Preview 1 を用いると、さらなる特典があります。

なんと、GitHub Pages に配置した Blazor WebAssembly アプリにおいて、事前 Brotli 圧縮されたアセンブリ読み込みが有効になるのです!

実は Blazor WebAssembly プロジェクトを発行すると、素の .NET アセンブリファイル (.dll) に加えて、GZip 圧縮した同名のファイル (.dll.gz) および Brotli 圧縮した同名のファイル (.dll.br) が同時に生成されています。

ただ、これら事前圧縮されたアセンブリファイルは、そのままでは採用されません。

Blazor WebAssembly アプリのローダー側で、明示的に .dll.br を Web サーバーから取得し、Brotli 圧縮を伸張する必要があるのです。

詳しくは下記公式ドキュメントに解説があります。

PublishSPAforGitHubPages.Build 最新バージョン v.1.1.0.7 Preview 1 を使って、GHPages=true を指定しての発行を行なうと、発行処理の一環として、発行後の index.html ファイルを書き換えて、事前 Brotli 圧縮されたアセンブリ読み込みを行なう JavaScript コードの注入が行なわれるようになる仕掛けです。

発行後のindex.html
  ...
  <!-- 👇 autostart="false" 属性が書き加えられる -->
  <script src="_framework/blazor.webassembly.js" autostart="false"></script>

  <!-- 👇 以下 2行の script 要素が追加される -->
  <script src="decode.min.js"></script>
  <script src="brotliloader.min.js"></script>
</body>

Blazor WebAssembly は、どうしても .NET アセンブリファイル群 (.dll) の総サイズが膨らみがちです。
もっとも、二回目以降のアクセスでは、初回アクセス時にブラウザの Cache Storage に保存されたデータを使うので、さくっと開きます。

ですが、そうは言っても、とにかく初回アクセス時には、どうしてもそれら多くの .NET アセンブリファイル群 (.dll) を転送せざるを得ず、通信量がかさみ、初期ロード時間が長くなる懸念があります。

その点、事前 Brotli 圧縮されたアセンブリファイル (.dll.br) を取得するようにすることで、結構なサイズの通信量を削減することができ、初回アクセス時の初期ロード時間の短縮が期待できます。

おまけ: GitHub Actions を使った配置

以上、Blazor WebAssembly アプリを GitHub Pages 向けに発行するにあたり、つまらぬ調整作業を一手に引き受ける上に、事前 Brotli 圧縮されたアセンブリファイル読み込み機能も織り込んでしまう NuGet パッケージ PublishSPAforGitHubPages.Build の紹介でした。

なお、この PublishSPAforGitHubPages.Build パッケージと GitHub Actions を組み合わせると、さらに GitHub Pages への配置が簡単になります。

例えば以下のような GitHub Actions 用の YAML ファイルを、リポジトリの .github/workflows フォルダに収録しておくと、GitHub に push するたびに自動で、gh-pages ブランチに Blazor WebAssembly アプリを配置してくれるようになります。

.github/workflows/gh-pages.yml
name: github pages

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
      # Checkout the code
      - uses: actions/checkout@v2

      # Install .NET Core SDK
      - name: Setup .NET Core
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 5.0.100

      # Publish the site
      - name: Publish
        run: dotnet publish (プロジェクトフォルダ)/(プロジェクト名).csproj -p:GHPages=true -c:Release -o:public

      # Deploy the site
      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: public/wwwroot
          force_orphan: true

これでより手軽に、自作の Blazor WebAssembly アプリを、GitHub Pages 上で公開できるようになりますね!

Learn, Practice, Share!