CSS ライブ リロードを Blazor アプリ開発でも

# 遡ること 5~8 年前...

過去の ASP.NET Web アプリ開発シーンを覚えているでしょうか?

Visual Studio 2012～2015 は、当時、次に挙げるような ASP.NET Web アプリ開発体験を提供していました。

- Page Inspector
- CSS Auto-Sync (いわゆる "CSS ライブ リロード")

以下は Microsoft Channel9 で今も公開されている、7～8年前の Visual Studio 2012 による "Page Inspector" 機能を説明する動画です。

[![fig0](https://github.com/jsakamoto/Toolbelt.AspNetCore.CssLiveReloader/blob/master/.assets/fig2.png?raw=true)](https://channel9.msdn.com/Blogs/ASP-NET-Site-Videos/visual-studio-2012-page-inspector)  
_["Channel 9 | ASP.NET Site Videos | Visual Studio 2012 Page Inspector"](https://channel9.msdn.com/Blogs/ASP-NET-Site-Videos/visual-studio-2012-page-inspector)_

しかしながら、その後の JavaScript SPA ブームの到来により、このようなWeb アプリ開発者にとっての便利機能は、それら SPA フレームワーク開発には使えなくなってしまいました。

あくまでも想像に過ぎませんが、それら SPA フレームワーク群は (その当時) Windows でしか動かない Visual Studio のことなど興味の範囲外でしたでしょうし、また、モジュールバンドルの原理上からも技術上の実装の困難さもあったのでしょう。

こうして、Web アプリ開発体験を素晴らしいものにする技術の内のいくつかは、時代の影に忘れ去られてしまったように感じています。

(※もしかしたら ASP.NET WebForms や ASP.NET MVC (ASP.NET "Core" ではなく) 開発では、今でもバリバリ現役な機能なのかもしれませんが、自分はもう久しく、趣味も仕事も SPA 一辺倒になってしまい...)

# Blazor Web アプリ開発に "CSS ライブ リロード" を取り戻せ!

そして現在、2020年。
ここ最近、自分は、Blazor Web アプリ開発シーンにおいても、かつて実現されていたような "CSS ライブ リロード" の開発体験を取り戻したいという思いが募ってきました。

幸か不幸か、今のところ、Blazor には "CSS コンポーネント" (又はコンポーネントスタイル。コンポーネント内にのみスコープ限定されてスタイル適用される。) の仕組みは搭載されていません。
つまり、古き良き (?) 方式のまま至極単純に、CSS ファイルを link 要素で実直に参照しているだけです。
そんなわけですから、(HTML ドキュメント全体をまるっとリロードではなく) "CSS のみライブ リロード" 機能の導入はたやすいのではないかと考えました。

思ったとおり、数時間、ネット上で検索してみた結果、多数の "ライブ リロード" を実現するライブラリやツール類をいくつも見つけることができました。

# しかし、どれも満足できない...

しかしながら、そうしてネット上で見つけることができた "ライブ リロード" の仕組みのいずれにも、自分は満足ができませんでした。

とりわけ不満だったのは、CSS の再読込みの振る舞いが**スムーズではなかった**点です。

- あるツール/ライブラリは、ただ 1 つの CSS ファイルが変更されただけでも、**HTML ドキュメント全体を再読込み**していました。
- また別のツール/ライブラリは、ただ 1 つの CSS ファイルが変更されただけでも、HTML ドキュメントから link 要素で参照されている**すべての CSS ファイルを再読込み**していました。
- さらに、自分が見つけることのできたいずれのツール/ライブラリにおいても、CSS の再読込時に**激しいちらつき**を引き起こしていました。(いったん全てのスタイル定義が消えて白黒のまっさらな表示になり、それから再読込み後のスタイルが再適用される様子が見えました)

結局、自分が見つけることのできたいずれのツール/ライブラリも、5年前に Visual Studio 2015 上で体験した、あの快適な CSS ライブ リロードには及ばないと感じていました。

# "車輪の再発明" の時間だ!

待っていればその内に誰かが、素晴らしい CSS ライブ リロード実装を、無償で公開してくれるんじゃないだろうか、と思っていた時期もありました。

ありました。
ですが、しかしあれから数年経っても、あのスムーズな CSS ライブ リロード体験ができる実装の情報を掴めていないわけです。
(※自分が発見できていない可能性も高いですし、WebStrom などの JetBrains 社製の製品は、まだ試していないのですが。)

ということで、"車輪の再発明" をする決断を下しました。
つまり、"CSS ライブ リロード" のツール/ライブラリを自作するのです。

数日を経てついに自家製 "CSS ライブ リロード" ライブラリを完成させ、それをオープンソースライブラリとして公開しました。

# "ASP.NET Core CSS Live Reloader"

この成果は、**"ASP.NET Core CSS Live Reloader"** (`Toolbelt.AspNetCore.CssLiveReloader`) という NuGet パッケージとして公開しました ([Mozilla Public License 2.0](https://ja.wikipedia.org/wiki/Mozilla_Public_License))。

- _[NuGet Gallery | Toolbelt.AspNetCore.CssLiveReloader](https://www.nuget.org/packages/Toolbelt.AspNetCore.CssLiveReloader/)_

これは ASP.NET Core 3.1+ Web アプリ用のミドルウェアです。

この NuGet パッケージを、(Blazor WebAssembly アプリの ASP.NET Core ホスティングサーバーなどの) ASP.NET Core Web アプリのプロジェクトに追加し (下記例)、

```shell
$ dotnet add package Toolbelt.AspNetCore.CssLiveReloader
```

さらにこのミドルウェアの使用を `Startup` クラスに追加すれば (下記例)、

```csharp
...
using Toolbelt.Extensions.DependencyInjection; // <- この行を追加し...
...
public class Startup
{
  ...
  public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  {
    ...
    if (env.IsDevelopment())
    {
      ...
      app.UseCssLiveReload(); // <- この行を追加!
```

これで "CSS ライブ リロード" が機能しはじめます!

![fig1](https://raw.githubusercontent.com/jsakamoto/Toolbelt.AspNetCore.CssLiveReloader/master/.assets/fig1.gif)

ご覧のとおり、**ちらつきはなく**, そしてまた、**HTML ドキュメント全体を再読込みすることもありません。**

また、Visual Studio の拡張機能などではなく、単なるミドルウェアです。
ですので、.NET Core SDK が動作する環境であれば **macOS や各種 Linux ディストリ上でも**、また、メモ帳のような**単純なテキストエディタを使ってでも**、バッチリ機能します。

# "Blazor Dev Server with CSS live reloader"

さらに、Blazor WebAssembly アプリのスタンドアロンバージョン (ASP.NET Core ホスティングではなく) を開発中の場合は、もっと簡単な方法があります。

実は、公式の "Blazor Dev Server" (開発サーバ) をフォークし、"CSS ライブ リロード" の機能を組み込んだ、**"Blazor Dev Server with CSS live reloader"** (`Toolbelt.Blazor.DevServer.WithCssLiveReloader`) を開発、公開しました (こちらは fork 元に倣い [Apache License 2.0](https://ja.wikipedia.org/wiki/Apache_License))。

- _[NuGet Gallery | Toolbelt.Blazor.DevServer.WithCssLiveReloader](https://www.nuget.org/packages/Toolbelt.Blazor.DevServer.WithCssLiveReloader/)_

これにより、プロジェクトファイル (.csproj) 中の `Microsoft.AspNetCore.Components.WebAssembly.DevServer` パッケージ参照を `Toolbelt.Blazor.DevServer.WithCssLiveReloader` パッケージへの参照に差し替えるだけで、"CSS ライブ リロード" 機能が有効となります。

![fig2](https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.DevServer.WithCssLiveReloader/master/.assets/fig1.gif)

## 仕組み

基本的なコンセプトは、以下のとおり、他の "CSS ライブ リロード" ツール/ライブラリの実装と、さほど変わりません。

1. サーバープロセスは。要求された HTML ドキュメント中に、小さな JavaScript コードを注入します。
2. その JavaScript コードは、サーバープロセスからのイベント通知を受信できるように、サーバープロセスへの接続を確立します。(ちなみに上記パッケージの実装にあたっては ["Server-Sent Events" (SSE)](https://developer.mozilla.org/ja/docs/Web/API/Server-sent_events) の技法を使いました。)
1. サーバープロセスは CSS ファイルの変更を監視します。
2. サーバープロセスが CSS ファイルの変更を検知すると、その変更された CSS ファイル名を先の JavaScript コードにイベント送信します。

CSS の再読込みがちらつくことの無いように工夫したのはここからです。

1. 先の JavaScript コードは、サーバープロセスからの通知を受信すると、サーバープロセスから指定された、再読込み対象の CSS を参照している `link` 要素を複製します。そしてこの複製した `link` 要素の `onload` イベントにイベントハンドラ関数を設定します。(この JavaScript はまた、ブラウザのキャッシュを迂回するよう、現在時刻に基づいた "再読込トークン" を、`href` 属性に指定された URL に付加します。)
![fig3.1](https://raw.githubusercontent.com/jsakamoto/Toolbelt.AspNetCore.CssLiveReloader/master/.assets/fig3.1.png)
2. 続けてこの JavaScript コードは、この複製した `link` 要素を、複製元の `link` 要素の、ちょうど直前に差し込みます。
3. こうして DOM に挿入された、先に複製された `link` 要素が指す CSS ファイルの読み込みが終わると、ブラウザは、この複製した `link` 要素の `onload` イベントを発火します。すると、先に設定されていたイベントハンドラが実行されるわけですが、このイベントハンドラが、複製元の `link` 要素を DOM から削除します。
![fig3.2](https://raw.githubusercontent.com/jsakamoto/Toolbelt.AspNetCore.CssLiveReloader/master/.assets/fig3.2.png)
![fig3.3](https://raw.githubusercontent.com/jsakamoto/Toolbelt.AspNetCore.CssLiveReloader/master/.assets/fig3.3.png)


# まとめ

"ASP.NET Core CSS Live Reloader" と "Blazor Dev Server with CSS live reloader" は、より心地よい CSS ライブ リロード体験を皆さんに提供できることと考えています。

これらのパッケージによって、皆さんの Blazor プログラミング生活を改善することができれば作者冥利に尽きます。

Happy coding :)