ASP.NET CoreとSPAフレームワークの連携の仕組みを知ろう
ここではASP.NET Core上でどのようにSPAフレームワークを扱っているか、その中心となるJavaScriptServicesについて詳しく解説します。
JavaScriptServicesとは
JavaScriptServicesを大まかに説明すると、クライアントサイドのSPAフレームワークとサーバサイドのASP.NET Coreとのつなぎ目となる機能群の総称です。以下3つのライブラリから構成されています。
Microsoft.AspNetCore.SpaTemplates
.NET Coreでプロジェクトを新規作成する際に、SPA用のテンプレートを提供するためのライブラリです。
ASP.NET Core 2.0ではSPA用テンプレートとしてAngular、React、React + Reduxの3種類がデフォルトで使用可能となっていますが、SpaTemplatesをインストールすることで、Aurelia、Knockout.js、Vue.jsといったフレームワークがプロジェクトテンプレートとして追加で選択可能になります。
[コラム]SPAのプロジェクトテンプレートを追加する方法
.NET Core 2.0のデフォルトでは、SPA用のプロジェクトテンプレートとしてAngular、React、React + Reduxの3種類のテンプレートが用意されていますが、以下のコマンドを実行することでさらに追加でAurelia、Knockout.js、Vue.jsといったSPAフレームワークを追加することができます。
$ dotnet new --install Microsoft.AspNetCore.SpaTemplates::*
Microsoft.AspNetCore.SpaServices
JavaScriptServiceの中心的な機能で、サーバサイドレンダリングやWebpackによるクライアントサイドのビルドなどを提供します。
Microsoft.AspNetCore.NodeServices
NodeServicesは、ASP.NET CoreのアプリケーションからNode.jsのモジュールを呼び出せるようにするためのライブラリです。これによりサーバサイドでのJavaScriptの実行が可能になります。
ここからはSpaServicesの各機能について説明します。
サーバサイドレンダリング
SpaServicesではクライアントコードのプリレンダリングを行います。
通常のSPAの仕組みでは、サイトに初回アクセスしたタイミングでHTML、CSS、JavaScriptや画像等のデータをサーバからダウンロードし、ブラウザ側のタスクとして動的にHTMLページを生成してから画面に表示します。ダウンロードしてからページを生成するため、初回の画面表示までに時間がかかる問題があります。
サーバサイドレンダリングでは、このページ生成をあらかじめサーバのタスクとして行うことで実行時のパフォーマンスを向上させることができます。
もう1点サーバサイドレンダリングの強みとしては、SEO(検索エンジン最適化)対策が挙げられます。各ページはサーバ起動のタイミングで生成が済んでいるため、検索エンジンがサーバをクローリングする際に適切にページを返すことができます。また近年では減少傾向にあるかと思いますが、ブラウザの設定でJavaScriptを無効にしているクライアントに対してもページを提供できるといったメリットもあります。
Webpack dev middleware
WebpackはHTML、CSS、JavaScriptや画像等のファイルを1つあるいは任意の数のファイルにまとめるビルド / モジュールバンドラーツールです。SpaServicesではこのWebpackをWebpack dev middlewareという形でASP.NET Coreに組み込むことで、クライアントサイドコードのビルドをWebpackに任せています。大きな特徴としては、ASP.NET Coreのアプリケーション(サーバ)を起動させたままの状態でも、クライアントサイドのコードを修正するとその変更を検知してWebpackのビルドが実行される点です。用途としては開発時に限られてしまいますが、これによってサーバの再起動なしにクライアントサイドの更新を実現することができ、素早く修正の確認を行えるようになります。
Hot Module Replacement(HMR)
Webpack dev middlewareはサーバ再起動なしでクライアントサイドのコードの再ビルドまでを実現します。実際にブラウザで変更を確認するためには、一度ブラウザでページのリロードを行う必要があります。HMRを有効化すると、ページのリロード無しでページの最新化までを行うことができるようになります。
HMRはWebpack dev middlewareのオプション機能である位置づけのため、Webpack dev middlewareの使用が前提となります。
ルーティングヘルパー
SPAでは通常、一つのHTMLファイル上でJavaScriptを駆使してページ遷移を疑似的に表現しています。URLはSPAフレームワーク内部のルーティング機能によって書き換えられています。もし未定義のURLにアクセスしようとした場合は、404エラーとなります。ASP.NET Coreのルーティングヘルパーを使用すると、未定義のURLアクセス時の遷移先を変更することができます。
ASP.NET CoreとSPAフレームワークの連携箇所を確認しよう(サーバサイド)
SPAテンプレートで作成されたプロジェクトの構成に沿って、JavaScriptServicesの適用箇所について確認していきましょう。SPAテンプレートから作成したプロジェクトのうち、JavaScriptServicesに密接に関わるファイルやディレクトリには以下のものがあります。
spa-angular ┣ Program.cs ┣ Startup.cs ┣ /Controllers ┣ /Views ┣ /ClientApp ┣ webpack.config.js ┗ webpack.config.vendor.js
このうちViewsディレクトリまでがサーバサイドに関する実装で、ClientAppディレクトリ以降がクライアントサイドに関する実装となっています。今回は、サーバサイドの各実装について説明していきます。
Program.cs / Startup.cs
.NET Coreがアプリケーションを起動する際に最初に呼び出すのがProgram.csです。C#アプリケーションとして実行するため、Mainメソッドを実装しています。その次に呼び出されるのがStartup.csです。Startup.csにはアプリケーション全体に関わる設定情報が実装されています。
public class Startup { ・・・中略・・・ public void ConfigureServices(IServiceCollection services) { services.AddMvc(); ・・・(1) } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions ・・・(2) { HotModuleReplacement = true ・・・(3) }); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseMvc(routes => { routes.MapRoute( ・・・(4) name: "default", template: "{controller=Home}/{action=Index}/{id?}"); routes.MapSpaFallbackRoute( ・・・(5) name: "spa-fallback", defaults: new { controller = "Home", action = "Index" }); }); } }
SPAテンプレートのように、SPAとの連携をするアプリケーションをASP.NET Core 2.0で作成する場合は、サーバサイド技術としてASP.NET Core MVCを使用するため、MVCの利用宣言をしています(1)。開発時にアプリケーションを再起動することなくクライアントコードの変更をブラウザに反映するための設定として、Webpack dev middlewareとHot Module ReplacementをConfigureメソッドの中に実装します(2)(3)。これらの機能はセキュリティの観点から、開発時のみ有効化しています。また、アプリケーションのルーティング定義もConfigureメソッドで設定しています。(4)では、アプリケーションのルートの遷移先を定義しています。SPAテンプレートからプロジェクトを作成した場合は、ルートパスに対応するルーティング先として「Home」つまりHomeControllerが、実行されるアクション(メソッド)として「Index」が設定されています。(5)では、クライアント側コードで未定義のURLにアクセスされた場合の遷移先を指定します。(4)と同様にHomeControllerのIndexメソッドを呼び出すように指定しています。
Controllersディレクトリ
ControllersディレクトリにはASP.NET Core MVCのコントローラクラスが配置されています。コントローラクラスは、ユーザがブラウザから指定したURLと対応するように設計されています。以下のリスト5は、Startup.csでデフォルトのルーティング先として指定されているHomeControllerのIndexメソッドです。View()を返却することで、Viewsディレクトリ配下にあるコントローラと同名のディレクトリから、メソッド名と同名のビューが参照されることになります。
public class HomeController : Controller { public IActionResult Index() { return View(); }
この場合は「Views/Home/Index.cshtml」を参照します。
Viewsディレクトリ
ViewsディレクトリにはASP.NET Core MVCのビューを配置します。通常のASP.NET Core MVCアプリケーションではこのViewsディレクトリ配下のファイル(.cshtml)にブラウザで表示するHTMLを記述します。SPA連携するアプリケーションの場合は、基本的にビューの実装はSPA側で行うため、Viewsディレクトリ配下のビューではSPA側にビューの表示を委譲するコードを書きます。
Views/_ViewImports.cshtml
_ViewImports.cshtmlではASP.NET Coreでサーバサイドレンダリングを有効にするためのタグヘルパーをインポートします(以下リスト6の(1))。サーバサイドレンダリング用のタグヘルパーはasp-prerender-moduleとして提供されています。このタグヘルパーをアプリケーションのルートとして指定したページ(つまりViews/Home/Index.cshtml)で使用します。
@using spa_angular @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Microsoft.AspNetCore.SpaServices ・・・(1)
Views/Home/Index.cshtml
これまでの説明の通りSPAテンプレートから作成したプロジェクトでは、Views/Home/Index.cshtmlがアプリケーションのルートページとなります。通常のビューの実装と異なり、HTMLを使った画面の実装を行っていないことが分かるかと思います。
・・・中略・・・ <app asp-prerender-module="ClientApp/dist/main-server">Loading...</app> ・・・(1) <script src="~/dist/vendor.js" asp-append-version="true"></script> @section scripts { <script src="~/dist/main-client.js" asp-append-version="true"></script> }
HTMLの実装の代わりに定義されているのがappタグです(1)。このappタグの属性として先ほど説明したasp-prerender-moduleタグヘルパーを使用しています。asp-prerender-moduleの値にはWebpackによってビルド・バンドルされたクライアントサイドのコード(ClientApp/dist/main-server.js)を指定します。これによってサーバ側で事前に構築したDOMでappタグの内容を書き換えて表示用のHTMLを生成し、ブラウザへ返すことができるようになります。
おわりに
今回はASP.NET Core 2.0でビュー層にSPAフレームワークを使用するための仕組み(JavaScriptServices)の概要と、連携のためのサーバ側の実装についてSPAテンプレート(Angular)をもとに説明しました。
次回は引き続き、クライアント側での連携のための実装箇所について解説し、アプリケーションにページを追加するところまで紹介します。