【Blazor Server】【ASP.NET Core】CSS isolation と MapControllers
はじめに
この記事は Blazor Advent Calendar 2020 の16日目の記事です。
.NET 5 で追加された CSS Isolation(CSS の分離)を試してみることにしました。
元のプロジェクト
プロジェクト自体は(バージョンは異なりますが)この時のものをベースにしています。
Razor および Blazor は、 Controller クラスから返しています。
HomeController.cs
using System; using Microsoft.AspNetCore.Mvc; namespace BlazorSample.Controllers { public class HomeController: Controller { [Route("")] [Route("{page}")] // <- 後述しますがこれだと問題が発生します public ActionResult OpenPage(string page) { ViewData["Title"] = $"Page {page}"; return View("Views/_Host.cshtml"); } } }
_Layout.cshtml
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>@ViewData["Title"]</title> <base href="~/" /> </head> <body> @RenderBody() <script src="_framework/blazor.server.js"></script> </body> </html>
_Host.cshtml
@namespace BlazorSample.Views
@(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
App.razor
@using Shared <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> <NotFound> <LayoutView Layout="@typeof(MainLayout)"> <p>Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
MainLayout.razor
@inherits LayoutComponentBase @Body
DisplayGridPage.razor
@page "/" <h1>Hello, world!</h1> Welcome to your new app. <div id="sheet_area"> </div>
CSS isolation
先のリンクでも説明されていますが、CSS isolation は各 .razor ごとに個別の CSS を作ることができる、というものです。
手順としては CSS ファイルとして {プロジェクト名}.styles.css を読み込むよう設定すること、(先ほどの DisplayGridPage.razor であれば) DisplayGridPage.razor.css という CSS ファイルを用意する、という2点です。
_Layout.cshtml
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>@ViewData["Title"]</title> <base href="~/" /> <!-- Blazorが読み込まれるときに個別の CSS が渡される --> <link href="BlazorSample.styles.css" rel="stylesheet" /> </head> <body> @RenderBody() <script src="_framework/blazor.server.js"></script> </body> </html>
- ドキュメントなどでは Host.cshtml で設定することになっていますが、 Layout.cshtml がある場合はこちらに書いても問題ありませんでした。
DisplayGridPage.razor.css
#sheet_area { height: 30vh; width: 30vw; background-color: aqua; display: grid; } h1{ color: blue; }
失敗
これで実行すれば CSS が割当たり・・・・ませんorz
Controller クラスで「localhost:5000」と「localhost:5000/{page}」をルーティングして View を返しているため、「localhost:5000/BlazorSample.styles.css」までルーティングされていたという。。。
※2020-12-21 更新
Start.cs の Configure 内で、「app.UseStaticFiles();」より先に「app.UseRouting();」を実行してしまっていたのが原因でした。
検証までしていただいた @jsakamoto さん、ありがとうございます(..)_
何となくで書いてしまっていたところなので、勉強になりました :)
HomeController.cs
... namespace BlazorSample.Controllers { public class HomeController: Controller { [Route("")] [Route("Pages/{page}")] public ActionResult OpenPage(string page) ...
- Middlewareで何とかする、という方法もありそうですが、可能であればパスを変えるのがシンプルな気はします。
わざわざ書くほどのことでもないのですが、他にも起こっていたはずの問題がなぜか再現しなかったため、せめてこれだけは残しておくことにします。
生成される CSS
「BlazorSample.styles.css」として渡される CSS は MainLayout.razor.css など親要素がある場合はそれらをマージしたものとなります。
BlazorSample.styles.css()
/* _content/BlazorSample/Views/DisplayGridPage.razor.rz.scp.css */ #sheet_area[b-p832tuedyv] { height: 30vh; width: 30vw; background-color: aqua; display: grid; } h1[b-p832tuedyv]{ color: blue; } /* _content/BlazorSample/Views/Shared/MainLayout.razor.rz.scp.css */ h1[b-m6a6nzx0h4]{ color: red; } header[b-m6a6nzx0h4]{ background-color: rosybrown; }
PostCSS で Autoprefixer を使ったときのようにベンダープレフィックスをつけたりしてくれるわけではないので、(IE とか IE とか IE とか)必要な場合は(今回の場合) Views に PostCSS から生成した CSS を出力する、というのが良さそうです。
なお、 .razor と .razor.css ファイルは同一階層にある必要があります。
Partial クラス (.razor.cs) と合わせて一か所に置くのが良いですね。