ASP.NET Core で JavaScript サービスを使用してシングル ページ アプリケーションを作成するUse JavaScript Services to Create Single Page Applications in ASP.NET Core

作成者: Scott AddieFiyaz HasanBy Scott Addie and Fiyaz Hasan

シングル ページ アプリケーション (SPA) は、本質的に高度なユーザー エクスペリエンスを提供できることから、広く使われているタイプの Web アプリケーションです。A Single Page Application (SPA) is a popular type of web application due to its inherent rich user experience. クライアント側の SPA フレームワークまたはライブラリ (AngularReact など) と、ASP.NET Core などのサーバー側のフレームワークの統合は、困難な場合があります。Integrating client-side SPA frameworks or libraries, such as Angular or React, with server-side frameworks such as ASP.NET Core can be difficult. JavaScript サービスは、統合プロセスの手間を減らすために開発されました。JavaScript Services was developed to reduce friction in the integration process. これにより、さまざまなクライアントやサーバーのテクノロジ スタック間でシームレスな操作を行うことができます。It enables seamless operation between the different client and server technology stacks.

警告

この記事で説明されている機能は、ASP.NET Core 3.0 時点で互換性のために残されてます。The features described in this article are obsolete as of ASP.NET Core 3.0. より単純な SPA フレームワーク統合メカニズムが Microsoft.AspNetCore.SpaServices.Extensions NuGet パッケージで利用できます。A simpler SPA frameworks integration mechanism is available in the Microsoft.AspNetCore.SpaServices.Extensions NuGet package. 詳細については、Microsoft.AspNetCore.SpaServices と Microsoft.AspNetCore.NodeServices の廃止に関するお知らせのページを参照してください。For more information, see [Announcement] Obsoleting Microsoft.AspNetCore.SpaServices and Microsoft.AspNetCore.NodeServices.

JavaScript サービスとはWhat is JavaScript Services

JavaScript サービスは、ASP.NET Core のクライアント側テクノロジのコレクションです。JavaScript Services is a collection of client-side technologies for ASP.NET Core. その目的は、ASP.NET Core を開発者の SPA 構築に有用なサーバー側プラットフォームとして位置付けることにあります。Its goal is to position ASP.NET Core as developers' preferred server-side platform for building SPAs.

JavaScript サービスは、次の 2 つの異なる NuGet パッケージで構成されています。JavaScript Services consists of two distinct NuGet packages:

これらのパッケージは、次のシナリオで役立ちます。These packages are useful in the following scenarios:

  • サーバーで JavaScript を実行するRun JavaScript on the server
  • SPA フレームワークまたは SPA ライブラリを使用するUse a SPA framework or library
  • Webpack を使用してクライアント側の資産を構築するBuild client-side assets with Webpack

この記事では、SpaServices パッケージを使用することに重点を置いて説明します。Much of the focus in this article is placed on using the SpaServices package.

SpaServices とはWhat is SpaServices

SpaServices は、ASP.NET Core を開発者の SPA 構築に有用なサーバー側プラットフォームとして位置付けるために作成されました。SpaServices was created to position ASP.NET Core as developers' preferred server-side platform for building SPAs. SpaServices は、ASP.NET Core で SPA を開発する際に必ず必要になるのものではありません。また、SpaServices を使っても、開発者が特定のクライアント フレームワークにロックされることはありません。SpaServices isn't required to develop SPAs with ASP.NET Core, and it doesn't lock developers into a particular client framework.

SpaServices は、次のような便利なインフラストラクチャを提供します。SpaServices provides useful infrastructure such as:

これらのインフラストラクチャ コンポーネントを集めると、開発ワークフローとランタイム エクスペリエンスの両方を強化することができます。Collectively, these infrastructure components enhance both the development workflow and the runtime experience. このコンポーネントは、個別に採用できます。The components can be adopted individually.

SpaServices を使用するための前提条件Prerequisites for using SpaServices

SpaServices を使用するには、以下をインストールします。To work with SpaServices, install the following:

  • Node.js (バージョン 6 以降) と npmNode.js (version 6 or later) with npm

    • これらのコンポーネントがインストールされていて検出可能であることを確認するには、コマンド ラインから次のコマンドを実行します。To verify these components are installed and can be found, run the following from the command line:

      node -v && npm -v
      
    • Azure の Web サイトにデプロイする場合、操作は必要ありません — サーバー環境に Node.js がインストールされて使用できるようになります。If deploying to an Azure web site, no action is required—Node.js is installed and available in the server environments.

  • .NET Core SDK 2.0 以降.NET Core SDK 2.0 or later

    • Visual Studio 2017 を使用している Windows では、 .NET Core クロスプラットフォーム開発 ワークロードを選択すると SDK がインストールされます。On Windows using Visual Studio 2017, the SDK is installed by selecting the .NET Core cross-platform development workload.
  • Microsoft.AspNetCore.SpaServices NuGet パッケージMicrosoft.AspNetCore.SpaServices NuGet package

サーバー側の事前レンダリングServer-side prerendering

ユニバーサル (アイソモーフィックとも呼ばれます) アプリケーションは、サーバーとクライアントの両方で実行できる JavaScript アプリケーションです。A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular、React などの一般的なフレームワークは、このアプリケーション開発スタイル用のユニバーサル プラットフォームを提供します。Angular, React, and other popular frameworks provide a universal platform for this application development style. まず、サーバーで Node.js を使用してフレームワーク コンポーネントをレンダリングし、続く処理の実行をクライアントに委任します。The idea is to first render the framework components on the server via Node.js, and then delegate further execution to the client.

SpaServices が提供する ASP.NET Core タグ ヘルパーは、サーバーで JavaScript 関数を呼び出すことで、サーバー側の事前レンダリングの実装が簡単にします。ASP.NET Core Tag Helpers provided by SpaServices simplify the implementation of server-side prerendering by invoking the JavaScript functions on the server.

サーバー側の事前レンダリングの前提条件Server-side prerendering prerequisites

aspnet-prerendering npm パッケージをインストールします。Install the aspnet-prerendering npm package:

npm i -S aspnet-prerendering

サーバー側の事前レンダリングの構成Server-side prerendering configuration

プロジェクトの _ViewImports.cshtml ファイルに名前空間を登録すると、タグ ヘルパーを検出できるようになります。The Tag Helpers are made discoverable via namespace registration in the project's _ViewImports.cshtml file:

@using SpaServicesSampleApp
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
@addTagHelper "*, Microsoft.AspNetCore.SpaServices"

タグ ヘルパーは、Razor ビューで HTML に似た構文を利用することで、低レベルの API と直接通信する複雑な部分を抽象化します。These Tag Helpers abstract away the intricacies of communicating directly with low-level APIs by leveraging an HTML-like syntax inside the Razor view:

<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>

asp-prerender-module タグ ヘルパーasp-prerender-module Tag Helper

前のコード例で使用されている asp-prerender-module タグ ヘルパーは、サーバーの Node.js で ClientApp/dist/main-server.js を実行します。The asp-prerender-module Tag Helper, used in the preceding code example, executes ClientApp/dist/main-server.js on the server via Node.js. わかりやすくするために、main-server.js ファイルは、Webpack ビルド プロセスで TypeScript から JavaScript へのトランスパイル タスクの成果物とします。For clarity's sake, main-server.js file is an artifact of the TypeScript-to-JavaScript transpilation task in the Webpack build process. Webpack では main-server のエントリ ポイントの別名が定義されています。また、この別名の依存関係グラフの走査は、ClientApp/boot-server.ts ファイルから開始します。Webpack defines an entry point alias of main-server; and, traversal of the dependency graph for this alias begins at the ClientApp/boot-server.ts file:

entry: { 'main-server': './ClientApp/boot-server.ts' },

次の Angular の例では、ClientApp/boot-server.ts ファイルが aspnet-prerendering npm パッケージの createServerRenderer 関数と RenderResult 型を使用して Node.js によるサーバー レンダリングを構成しています。In the following Angular example, the ClientApp/boot-server.ts file utilizes the createServerRenderer function and RenderResult type of the aspnet-prerendering npm package to configure server rendering via Node.js. サーバー側のレンダリング用の HTML マークアップは、resolve 関数呼び出しに渡されます。これは、厳密に型指定された JavaScript の Promise オブジェクトでラップされています。The HTML markup destined for server-side rendering is passed to a resolve function call, which is wrapped in a strongly-typed JavaScript Promise object. Promise オブジェクトは、DOM のプレースホルダー要素に注入する HTML マークアップを非同期にページに渡すという重要な役割を果たしています。The Promise object's significance is that it asynchronously supplies the HTML markup to the page for injection in the DOM's placeholder element.

import { createServerRenderer, RenderResult } from 'aspnet-prerendering';

export default createServerRenderer(params => {
    const providers = [
        { provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
        { provide: 'ORIGIN_URL', useValue: params.origin }
    ];

    return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
        const appRef = moduleRef.injector.get(ApplicationRef);
        const state = moduleRef.injector.get(PlatformState);
        const zone = moduleRef.injector.get(NgZone);
        
        return new Promise<RenderResult>((resolve, reject) => {
            zone.onError.subscribe(errorInfo => reject(errorInfo));
            appRef.isStable.first(isStable => isStable).subscribe(() => {
                // Because 'onStable' fires before 'onError', we have to delay slightly before
                // completing the request in case there's an error to report
                setImmediate(() => {
                    resolve({
                        html: state.renderToString()
                    });
                    moduleRef.destroy();
                });
            });
        });
    });
});

asp-prerender-data タグ ヘルパーasp-prerender-data Tag Helper

asp-prerender-data タグ ヘルパーと asp-prerender-module タグ ヘルパーと組み合わせると、Razor ビューからサーバー側 JavaScript にコンテキスト情報を渡すことができます。When coupled with the asp-prerender-module Tag Helper, the asp-prerender-data Tag Helper can be used to pass contextual information from the Razor view to the server-side JavaScript. たとえば、次のマークアップは、ユーザー データを main-server モジュールに渡します。For example, the following markup passes user data to the main-server module:

<app asp-prerender-module="ClientApp/dist/main-server"
        asp-prerender-data='new {
            UserName = "John Doe"
        }'>Loading...</app>

渡された UserName 引数は、組み込みの JSON シリアライザーによってシリアル化され、params.data オブジェクトに格納されます。The received UserName argument is serialized using the built-in JSON serializer and is stored in the params.data object. 次の Angular の例では、このデータを使用して、h1 の要素内に独自の挨拶文を作成します。In the following Angular example, the data is used to construct a personalized greeting within an h1 element:

import { createServerRenderer, RenderResult } from 'aspnet-prerendering';

export default createServerRenderer(params => {
    const providers = [
        { provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
        { provide: 'ORIGIN_URL', useValue: params.origin }
    ];

    return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
        const appRef = moduleRef.injector.get(ApplicationRef);
        const state = moduleRef.injector.get(PlatformState);
        const zone = moduleRef.injector.get(NgZone);
        
        return new Promise<RenderResult>((resolve, reject) => {
            const result = `<h1>Hello, ${params.data.userName}</h1>`;

            zone.onError.subscribe(errorInfo => reject(errorInfo));
            appRef.isStable.first(isStable => isStable).subscribe(() => {
                // Because 'onStable' fires before 'onError', we have to delay slightly before
                // completing the request in case there's an error to report
                setImmediate(() => {
                    resolve({
                        html: result
                    });
                    moduleRef.destroy();
                });
            });
        });
    });
});

タグ ヘルパーで渡されるプロパティ名は、パスカルケース 表記で表されます。Property names passed in Tag Helpers are represented with PascalCase notation. 同じプロパティ名が キャメルケース で表される JavaScript とは対照的です。Contrast that to JavaScript, where the same property names are represented with camelCase. 既定の JSON シリアル化構成が、この違いに対応します。The default JSON serialization configuration is responsible for this difference.

上のコード例を発展させると、サーバーからビューにデータを渡すことができます。そのためには、resolve 関数に渡す globals プロパティを設定します。To expand upon the preceding code example, data can be passed from the server to the view by hydrating the globals property provided to the resolve function:

import { createServerRenderer, RenderResult } from 'aspnet-prerendering';

export default createServerRenderer(params => {
    const providers = [
        { provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
        { provide: 'ORIGIN_URL', useValue: params.origin }
    ];

    return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
        const appRef = moduleRef.injector.get(ApplicationRef);
        const state = moduleRef.injector.get(PlatformState);
        const zone = moduleRef.injector.get(NgZone);
        
        return new Promise<RenderResult>((resolve, reject) => {
            const result = `<h1>Hello, ${params.data.userName}</h1>`;

            zone.onError.subscribe(errorInfo => reject(errorInfo));
            appRef.isStable.first(isStable => isStable).subscribe(() => {
                // Because 'onStable' fires before 'onError', we have to delay slightly before
                // completing the request in case there's an error to report
                setImmediate(() => {
                    resolve({
                        html: result,
                        globals: {
                            postList: [
                                'Introduction to ASP.NET Core',
                                'Making apps with Angular and ASP.NET Core'
                            ]
                        }
                    });
                    moduleRef.destroy();
                });
            });
        });
    });
});

globals オブジェクト内で定義されている postList 配列は、ブラウザーのグローバル window オブジェクトにアタッチされます。The postList array defined inside the globals object is attached to the browser's global window object. この変数はグローバル スコープに含められ、それによって作業の重複がなくなります。具体的に言えば、同じデータをサーバーで一度、クライアントで再度読み込むことがなくなるためです。This variable hoisting to global scope eliminates duplication of effort, particularly as it pertains to loading the same data once on the server and again on the client.

window オブジェクトにアタッチされたグローバル postList 変数

Webpack Dev ミドルウェアWebpack Dev Middleware

Webpack Dev ミドルウェア は、Webpack が必要に応じてリソースを構築することで、合理化された開発ワークフローを実現します。Webpack Dev Middleware introduces a streamlined development workflow whereby Webpack builds resources on demand. ページがブラウザーに再読み込みされると、ミドルウェアはクライアント側のリソースを自動的にコンパイルして提供します。The middleware automatically compiles and serves client-side resources when a page is reloaded in the browser. 別の方法として、サードパーティの依存関係またはカスタム コードが変更されたときに、プロジェクトの npm ビルド スクリプトを介して Webpack を手動で呼び出す方法もあります。The alternate approach is to manually invoke Webpack via the project's npm build script when a third-party dependency or the custom code changes. 次の例では、package.json ファイル内の npm ビルド スクリプトを示しています。An npm build script in the package.json file is shown in the following example:

"build": "npm run build:vendor && npm run build:custom",

Webpack Dev ミドルウェアの前提条件Webpack Dev Middleware prerequisites

aspnet-webpack npm パッケージをインストールします。Install the aspnet-webpack npm package:

npm i -D aspnet-webpack

Webpack Dev ミドルウェアの構成Webpack Dev Middleware configuration

Webpack Dev ミドルウェアは、Startup.cs ファイルの Configure メソッドにある次のコードによって、HTTP 要求パイプラインに登録されます。Webpack Dev Middleware is registered into the HTTP request pipeline via the following code in the Startup.cs file's Configure method:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseWebpackDevMiddleware();
}
else
{
    app.UseExceptionHandler("/Home/Error");
}

// Call UseWebpackDevMiddleware before UseStaticFiles
app.UseStaticFiles();

UseStaticFiles 拡張メソッドを使用して静的ファイル ホスティングを登録する前に、UseWebpackDevMiddleware 拡張メソッドを呼び出す必要があります。The UseWebpackDevMiddleware extension method must be called before registering static file hosting via the UseStaticFiles extension method. セキュリティ上の理由から、アプリが開発モードで実行されている場合にのみ、ミドルウェアを登録してください。For security reasons, register the middleware only when the app runs in development mode.

webpack.config.js ファイルの output.publicPath プロパティは、dist フォルダーの変更を監視するようにミドルウェアに指示します。The webpack.config.js file's output.publicPath property tells the middleware to watch the dist folder for changes:

module.exports = (env) => {
        output: {
            filename: '[name].js',
            publicPath: '/dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix
        },

ホット モジュール置換Hot Module Replacement

Webpack のホット モジュール置換 (HMR) 機能は、Webpack Dev ミドルウェアが進化したものと考えることができます。Think of Webpack's Hot Module Replacement (HMR) feature as an evolution of Webpack Dev Middleware. HMR は同じ利点をすべて実現しますが、変更をコンパイルした後にページ コンテンツを自動的に更新するので、開発ワークフローがさらに効率化されます。HMR introduces all the same benefits, but it further streamlines the development workflow by automatically updating page content after compiling the changes. これをブラウザーの更新と混同しないでください。ブラウザーの更新は、現在のメモリ内の状態と SPA のデバッグ セッションに影響します。Don't confuse this with a refresh of the browser, which would interfere with the current in-memory state and debugging session of the SPA. Webpack Dev ミドルウェア サービスとブラウザーの間には、ライブ リンクがあります。これは、変更がブラウザーにプッシュされることを意味します。There's a live link between the Webpack Dev Middleware service and the browser, which means changes are pushed to the browser.

ホット モジュール置換の前提条件Hot Module Replacement prerequisites

webpack-hot-middleware npm パッケージをインストールします。Install the webpack-hot-middleware npm package:

npm i -D webpack-hot-middleware

ホット モジュール置換の構成Hot Module Replacement configuration

HMR コンポーネントは、Configure メソッドで MVC の HTTP 要求パイプラインに登録する必要があります。The HMR component must be registered into MVC's HTTP request pipeline in the Configure method:

app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
    HotModuleReplacement = true
});

Webpack Dev ミドルウェアの場合と同様に、UseWebpackDevMiddleware 拡張メソッドを UseStaticFiles 拡張メソッドの前に呼び出す必要があります。As was true with Webpack Dev Middleware, the UseWebpackDevMiddleware extension method must be called before the UseStaticFiles extension method. セキュリティ上の理由から、アプリが開発モードで実行されている場合にのみ、ミドルウェアを登録してください。For security reasons, register the middleware only when the app runs in development mode.

webpack.config.js ファイルでは、plugins 配列を定義する必要があります。これは空のままでもかまいません。The webpack.config.js file must define a plugins array, even if it's left empty:

module.exports = (env) => {
        plugins: [new CheckerPlugin()]

ブラウザーでアプリを読み込むと、開発者ツールのコンソールのタブに HMR がアクティブ化されたことの確認が表示されます。After loading the app in the browser, the developer tools' Console tab provides confirmation of HMR activation:

ホット モジュール置換の接続メッセージ

ルーティング ヘルパーRouting helpers

ほとんどの ASP.NET Core ベースの SPA では、サーバー側のルーティングに加えて、クライアント側のルーティングが必要になります。In most ASP.NET Core-based SPAs, client-side routing is often desired in addition to server-side routing. SPA および MVC のルーティング システムは、干渉せずに個別に動作させることができます。The SPA and MVC routing systems can work independently without interference. しかし、1 つのエッジケースでは、HTTP 応答 404 を認識するという課題があります。There's, however, one edge case posing challenges: identifying 404 HTTP responses.

拡張子のない /some/page というルートを使用するシナリオを考えてみましょう。Consider the scenario in which an extensionless route of /some/page is used. 要求がサーバー側ルートとはパターン一致せず、クライアント側のルートと一致すると仮定します。Assume the request doesn't pattern-match a server-side route, but its pattern does match a client-side route. ここで、/images/user-512.png の受信要求について考えてみます。通常は、サーバー上のイメージ ファイルを検索する必要があります。Now consider an incoming request for /images/user-512.png, which generally expects to find an image file on the server. 要求されたリソース パスがどのサーバー側ルートまたは静的ファイルとも一致しない場合、クライアント側アプリケーションがこれを処理することはほとんどありません — 通常は、HTTP 状態コード 404 が返されます。If that requested resource path doesn't match any server-side route or static file, it's unlikely that the client-side application would handle it—generally returning a 404 HTTP status code is desired.

ルーティング ヘルパーの前提条件Routing helpers prerequisites

クライアント側ルーティング npm パッケージをインストールします。Install the client-side routing npm package. Angular を使用する場合の例を示します。Using Angular as an example:

npm i -S @angular/router

ルーティング ヘルパーの構成Routing helpers configuration

Configure メソッドで MapSpaFallbackRoute という名前の拡張メソッドを使用します。An extension method named MapSpaFallbackRoute is used in the Configure method:

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");

    routes.MapSpaFallbackRoute(
        name: "spa-fallback",
        defaults: new { controller = "Home", action = "Index" });
});

ルートは、構成された順序で評価されます。Routes are evaluated in the order in which they're configured. そのため、上のコード例の default ルートは、最初にパターン マッチングに使用されます。Consequently, the default route in the preceding code example is used first for pattern matching.

新しいプロジェクトを作成するCreate a new project

JavaScript サービスには、事前構成済みのアプリケーション テンプレートが用意されています。JavaScript Services provide pre-configured application templates. これらのテンプレートでは、Angular、React、Redux などのさまざまなフレームワークやライブラリと共に SpaServices を使用します。SpaServices is used in these templates in conjunction with different frameworks and libraries such as Angular, React, and Redux.

これらのテンプレートは、次のコマンドを実行して .NET Core CLI 経由でインストールできます。These templates can be installed via the .NET Core CLI by running the following command:

dotnet new --install Microsoft.AspNetCore.SpaTemplates::*

使用可能な SPA テンプレートの一覧が表示されます。A list of available SPA templates is displayed:

テンプレートTemplates 短い形式の名前Short Name 言語Language TagsTags
MVC ASP.NET Core with AngularMVC ASP.NET Core with Angular angularangular [C#][C#] Web/MVC/SPAWeb/MVC/SPA
MVC ASP.NET Core with React.jsMVC ASP.NET Core with React.js reactreact [C#][C#] Web/MVC/SPAWeb/MVC/SPA
MVC ASP.NET Core with React.js and ReduxMVC ASP.NET Core with React.js and Redux reactreduxreactredux [C#][C#] Web/MVC/SPAWeb/MVC/SPA

SPA テンプレートの 1 つを使用して新しいプロジェクトを作成するには、dotnet new コマンドでテンプレートの 短い形式の名前 を指定します。To create a new project using one of the SPA templates, include the Short Name of the template in the dotnet new command. 次のコマンドは、サーバー側用に構成された ASP.NET Core MVC を使用して、Angular アプリケーションを作成します。The following command creates an Angular application with ASP.NET Core MVC configured for the server side:

dotnet new angular

ランタイム構成モードの設定Set the runtime configuration mode

2 つのプライマリ ランタイム構成モードがあります。Two primary runtime configuration modes exist:

  • 開発:Development:
    • デバッグを容易にするソース マップが含まれています。Includes source maps to ease debugging.
    • パフォーマンス向上のためにクライアント側のコードを最適化することはしません。Doesn't optimize the client-side code for performance.
  • 実稼働:Production:
    • ソース マップを除外します。Excludes source maps.
    • バンドルと縮小を使用して、クライアント側のコードを最適化します。Optimizes the client-side code via bundling and minification.

ASP.NET Core では、ASPNETCORE_ENVIRONMENT という名前の環境変数を使用して構成モードを格納します。ASP.NET Core uses an environment variable named ASPNETCORE_ENVIRONMENT to store the configuration mode. 詳細については、「環境を設定する」を参照してください。For more information, see Set the environment.

.NET Core CLI で実行するRun with .NET Core CLI

プロジェクト ルートで次のコマンドを実行して、必要な NuGet パッケージと npm パッケージを復元します。Restore the required NuGet and npm packages by running the following command at the project root:

dotnet restore && npm i

アプリケーションをビルドして実行します。Build and run the application:

dotnet run

アプリケーションは、ランタイム構成モードに従って、localhost で開始されます。The application starts on localhost according to the runtime configuration mode. ブラウザーで http://localhost:5000 に移動すると、ランディング ページが表示されます。Navigating to http://localhost:5000 in the browser displays the landing page.

Visual Studio 2017 で実行するRun with Visual Studio 2017

dotnet new コマンドで生成された .csproj ファイルを開きます。Open the .csproj file generated by the dotnet new command. 必要な NuGet および npm パッケージは、プロジェクトを開いたときに自動的に復元されます。The required NuGet and npm packages are restored automatically upon project open. この復元プロセスには数分かかる場合があり、これが完了するとアプリケーションを実行する準備ができています。This restoration process may take up to a few minutes, and the application is ready to run when it completes. 緑色の実行ボタンをクリックするか Ctrl + F5 キーを押すと、ブラウザーが開いてアプリケーションのランディング ページが表示されます。Click the green run button or press Ctrl + F5, and the browser opens to the application's landing page. アプリケーションは、ランタイム構成モードに従って、localhost で実行されます。The application runs on localhost according to the runtime configuration mode.

アプリのテストTest the app

SpaServices テンプレートは、KarmaJasmine を使用してクライアント側のテストを実行するように事前構成されています。SpaServices templates are pre-configured to run client-side tests using Karma and Jasmine. Jasmine は JavaScript 用の一般的な単体テスト フレームワークであるのに対し、Karma はそれらのテストのテスト ランナーです。Jasmine is a popular unit testing framework for JavaScript, whereas Karma is a test runner for those tests. Karma は Webpack Dev ミドルウェアと連携するように構成されています。これにより、開発者は、変更が行われるたびにテストを停止して実行する必要がなくなります。Karma is configured to work with the Webpack Dev Middleware such that the developer isn't required to stop and run the test every time changes are made. テスト ケースまたはテスト ケース自体に対してコードが実行されているかどうかにかかわらず、テストは自動的に実行されます。Whether it's the code running against the test case or the test case itself, the test runs automatically.

例として、Angular アプリケーションを使用します。counter.component.spec.ts ファイルの CounterComponent に対し、2 つの Jasmine テスト ケースが既に提供されています。Using the Angular application as an example, two Jasmine test cases are already provided for the CounterComponent in the counter.component.spec.ts file:

it('should display a title', async(() => {
    const titleText = fixture.nativeElement.querySelector('h1').textContent;
    expect(titleText).toEqual('Counter');
}));

it('should start with count 0, then increments by 1 when clicked', async(() => {
    const countElement = fixture.nativeElement.querySelector('strong');
    expect(countElement.textContent).toEqual('0');

    const incrementButton = fixture.nativeElement.querySelector('button');
    incrementButton.click();
    fixture.detectChanges();
    expect(countElement.textContent).toEqual('1');
}));

ClientApp ディレクトリで、コマンド プロンプトを開きます。Open the command prompt in the ClientApp directory. 次のコマンドを実行します。Run the following command:

npm test

このスクリプトは、Karma テスト ランナーを起動します。これにより、karma.conf.js ファイルで定義されている設定が読み取られます。The script launches the Karma test runner, which reads the settings defined in the karma.conf.js file. その他の設定として、karma.conf.jsfiles 配列で実行するテスト ファイルを指定できます。Among other settings, the karma.conf.js identifies the test files to be executed via its files array:

module.exports = function (config) {
    config.set({
        files: [
            '../../wwwroot/dist/vendor.js',
            './boot-tests.ts'
        ],

アプリの発行Publish the app

Azure への発行の詳細については、こちらの GitHub の問題のページを参照してください。See this GitHub issue for more information on publishing to Azure.

生成されたクライアント側アセットと発行された ASP.NET Core 成果物をすぐにデプロイ可能なパッケージにまとめると、煩雑になることがあります。Combining the generated client-side assets and the published ASP.NET Core artifacts into a ready-to-deploy package can be cumbersome. ありがたいことに、SpaServices は、RunWebpack という名前のカスタム MSBuild ターゲットを使用して、発行プロセス全体を調整します。Thankfully, SpaServices orchestrates that entire publication process with a custom MSBuild target named RunWebpack:

<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
  <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
  <Exec Command="npm install" />
  <Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
  <Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />

  <!-- Include the newly-built files in the publish output -->
  <ItemGroup>
    <DistFiles Include="wwwroot\dist\**; ClientApp\dist\**" />
    <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
      <RelativePath>%(DistFiles.Identity)</RelativePath>
      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    </ResolvedFileToPublish>
  </ItemGroup>
</Target>

この MSBuild ターゲットには、次の役割があります。The MSBuild target has the following responsibilities:

  1. npm パッケージを復元するRestore the npm packages.
  2. サードパーティ製クライアント側アセットの実稼働レベルのビルドを作成するCreate a production-grade build of the third-party, client-side assets.
  3. カスタムのクライアント側アセットの実稼働レベルのビルドを作成するCreate a production-grade build of the custom client-side assets.
  4. Webpack が生成したアセットを発行フォルダーにコピーするCopy the Webpack-generated assets to the publish folder.

次を実行すると、MSBuild ターゲットが呼び出されます。The MSBuild target is invoked when running:

dotnet publish -c Release

その他の技術情報Additional resources