ASP.NET Core での静的資産のバンドルと縮小Bundle and minify static assets in ASP.NET Core

作成者: Scott AddieDavid PineBy Scott Addie and David Pine

この記事では、ASP.NET Core Web アプリでこれらの機能を使用する方法など、バンドルと縮小を適用する利点について説明します。This article explains the benefits of applying bundling and minification, including how these features can be used with ASP.NET Core web apps.

バンドルと縮小とはWhat is bundling and minification

バンドルと縮小は、Web アプリに適用できる 2 種類のパフォーマンス最適化です。Bundling and minification are two distinct performance optimizations you can apply in a web app. バンドルと縮小を併用すると、サーバー要求の数が減り、要求される静的資産のサイズが小さくなるため、パフォーマンスが向上します。Used together, bundling and minification improve performance by reducing the number of server requests and reducing the size of the requested static assets.

バンドルと縮小を使用すると、主に最初のページ要求の読み込み時間が短縮されます。Bundling and minification primarily improve the first page request load time. Web ページが要求されると、ブラウザーによって静的資産 (JavaScript、CSS、および画像) がキャッシュされます。Once a web page has been requested, the browser caches the static assets (JavaScript, CSS, and images). そのため、同じ資産を要求する同じサイト上で、同じページまたは複数のページを要求する場合、バンドルと縮小を使用してもパフォーマンスは改善されません。Consequently, bundling and minification don't improve performance when requesting the same page, or pages, on the same site requesting the same assets. expires ヘッダーが資産に正しく設定されておらず、バンドルと縮小が使用されていない場合、ブラウザーの更新の間隔ヒューリスティックにより、数日後には資産が古くなっているとマークされます。If the expires header isn't set correctly on the assets and if bundling and minification isn't used, the browser's freshness heuristics mark the assets stale after a few days. さらに、ブラウザーでは、各資産に対する検証要求が必要です。Additionally, the browser requires a validation request for each asset. この場合、最初のページ要求の後でも、バンドルと縮小によりパフォーマンスが向上します。In this case, bundling and minification provide a performance improvement even after the first page request.

バンドルBundling

バンドルでは、複数のファイルを単一のファイルに連結します。Bundling combines multiple files into a single file. バンドルを使用すると、Web ページなどの Web 資産のレンダリングに必要なサーバー要求の数を減らすことができます。Bundling reduces the number of server requests that are necessary to render a web asset, such as a web page. CSS、JavaScript など専用の個別のバンドルをいくつでも作成できます。ファイルが少なくなると、ブラウザーからサーバーへ、またはアプリケーションを提供するサービスからの HTTP 要求が少なくなることを意味します。You can create any number of individual bundles specifically for CSS, JavaScript, etc. Fewer files means fewer HTTP requests from the browser to the server or from the service providing your application. その結果、最初のページ読み込みのパフォーマンスが向上します。This results in improved first page load performance.

縮小Minification

縮小を使用すると、機能を変更せずにコードから不要な文字を削除できます。Minification removes unnecessary characters from code without altering functionality. その結果、要求される資産 (CSS、画像、JavaScript ファイルなど) のサイズが大幅に減ります。The result is a significant size reduction in requested assets (such as CSS, images, and JavaScript files). 縮小の一般的な副作用として、変数名を 1 文字に短縮する機能や、コメントと不要な空白を削除する機能があります。Common side effects of minification include shortening variable names to one character and removing comments and unnecessary whitespace.

次の JavaScript 関数を考えてみます。Consider the following JavaScript function:

AddAltToImg = function (imageTagAndImageID, imageContext) {
    ///<signature>
    ///<summary> Adds an alt tab to the image
    // </summary>
    //<param name="imgElement" type="String">The image selector.</param>
    //<param name="ContextForImage" type="String">The image context.</param>
    ///</signature>
    var imageElement = $(imageTagAndImageID, imageContext);
    imageElement.attr('alt', imageElement.attr('id').replace(/ID/, ''));
}

縮小により、関数が次のように短縮されます。Minification reduces the function to the following:

AddAltToImg=function(t,a){var r=$(t,a);r.attr("alt",r.attr("id").replace(/ID/,""))};

コメントと不要な空白の削除に加えて、次のパラメーターと変数名は次のように名前が変更されました。In addition to removing the comments and unnecessary whitespace, the following parameter and variable names were renamed as follows:

Original 名前の変更Renamed
imageTagAndImageID t
imageContext a
imageElement r

バンドルと縮小の影響Impact of bundling and minification

次の表は、個別に資産を読み込む場合とバンドルと縮小を使用する場合の違いを示しています。The following table outlines differences between individually loading assets and using bundling and minification:

アクションAction バンドルと縮小ありWith B/M バンドルと縮小なしWithout B/M 変更Change
ファイル要求File Requests 77 1818 157%157%
転送済み (KB)KB Transferred 156156 264.68264.68 70%70%
読み込み時間 (ミリ秒)Load Time (ms) 885885 23602360 167%167%

ブラウザーは、HTTP 要求ヘッダーに関してかなり冗長です。Browsers are fairly verbose with regard to HTTP request headers. 送信された合計バイト数メトリックは、バンドル時に大幅に減りました。The total bytes sent metric saw a significant reduction when bundling. 読み込み時間は大幅に改善されていますが、この例はローカルで実行されています。The load time shows a significant improvement, however this example ran locally. ネットワークを経由で転送される資産にバンドルと縮小を使用すると、パフォーマンスが大幅に向上します。Greater performance gains are realized when using bundling and minification with assets transferred over a network.

バンドルと縮小の戦略を選択するChoose a bundling and minification strategy

MVC および Razor Pages プロジェクト テンプレートには、JSON 構成ファイルで構成されるバンドルおよび縮小のためのソリューションが用意されています。The MVC and Razor Pages project templates provide a solution for bundling and minification consisting of a JSON configuration file. Grunt タスク ランナーなどのサードパーティ ツールの場合、同じタスクを実行するにはもう少し複雑です。Third-party tools, such as the Grunt task runner, accomplish the same tasks with a bit more complexity. サードパーティ製のツールは、リンティングや画像の最適化など、バンドルと縮小を超える処理が開発ワークフローに必要な場合に最適です。A third-party tool is a great fit when your development workflow requires processing beyond bundling and minification—such as linting and image optimization. 設計時にバンドルと縮小を使用することで、アプリのデプロイ前に縮小されたファイルが作成されます。By using design-time bundling and minification, the minified files are created prior to the app's deployment. デプロイ前のバンドルと縮小によって、サーバーの負荷が軽減されます。Bundling and minifying before deployment provides the advantage of reduced server load. ただし、設計時にバンドルと縮小を使用するとビルドの複雑さが増すので、静的ファイルでのみ機能することを認識することが重要です。However, it's important to recognize that design-time bundling and minification increases build complexity and only works with static files.

バンドルと縮小を構成するConfigure bundling and minification

注意

これを機能させるには、BuildBundlerMinifier NuGet パッケージをプロジェクトに追加する必要があります。The BuildBundlerMinifier NuGet package needs to be added to your project for this to work.

ASP.NET Core 2.0 以前では、MVC および Razor Pages プロジェクト テンプレートには、各バンドルのオプションが定義された bundleconfig.json 構成ファイルが用意されています。In ASP.NET Core 2.0 or earlier, the MVC and Razor Pages project templates provide a bundleconfig.json configuration file that defines the options for each bundle:

ASP.NET Core 2.1 以降では、bundleconfig.json という名前の新しい JSON ファイルを MVC または Razor Pages プロジェクトのルートに追加します。In ASP.NET Core 2.1 or later, add a new JSON file, named bundleconfig.json, to the MVC or Razor Pages project root. 開始点としてそのファイルに次の JSON を含めます。Include the following JSON in that file as a starting point:

[
  {
    "outputFileName": "wwwroot/css/site.min.css",
    "inputFiles": [
      "wwwroot/css/site.css"
    ]
  },
  {
    "outputFileName": "wwwroot/js/site.min.js",
    "inputFiles": [
      "wwwroot/js/site.js"
    ],
    "minify": {
      "enabled": true,
      "renameLocals": true
    },
    "sourceMap": false
  }
]

bundleconfig.json ファイルには、各バンドルのオプションが定義されています。The bundleconfig.json file defines the options for each bundle. 上記の例では、カスタムの JavaScript (wwwroot/js/site.js) とスタイルシート (wwwroot/css/site.css) ファイルに対して 1 つのバンドル構成が定義されています。In the preceding example, a single bundle configuration is defined for the custom JavaScript (wwwroot/js/site.js) and stylesheet (wwwroot/css/site.css) files.

構成のオプションには、次のようなものがあります。Configuration options include:

  • outputFileName:出力するバンドル ファイルの名前。outputFileName: The name of the bundle file to output. bundleconfig.json ファイルからの相対パスを含めることができます。Can contain a relative path from the bundleconfig.json file. 必須required
  • inputFiles:まとめてバンドルするファイルの配列。inputFiles: An array of files to bundle together. これらは、構成ファイルへの相対パスです。These are relative paths to the configuration file. 省略可能、* 値が空の場合、空の出力ファイルになります。optional, *an empty value results in an empty output file. globbing パラメーターがサポートされます。globbing patterns are supported.
  • minify:出力の種類の縮小オプション。minify: The minification options for the output type. 省略可能既定値 - minify: { enabled: true }optional, default - minify: { enabled: true }
  • includeInProject:生成されたファイルをプロジェクト ファイルに追加するかどうかを示すフラグ。includeInProject: Flag indicating whether to add generated files to project file. 省略可能既定値 - falseoptional, default - false
  • sourceMap:バンドルされたファイルのソース マップを生成するかどうかを示すフラグ。sourceMap: Flag indicating whether to generate a source map for the bundled file. 省略可能既定値 - falseoptional, default - false
  • sourceMapRootPath:生成されたソース マップ ファイルを格納するためのルート パス。sourceMapRootPath: The root path for storing the generated source map file.

ワークフローにファイルを追加するAdd files to workflow

次のような新しい custom.css ファイルが追加される例を考えてみましょう。Consider an example in which an additional custom.css file is added resembling the following:

.about, [role=main], [role=complementary] {
    margin-top: 60px;
}

footer {
    margin-top: 10px;
}

custom.css を縮小し、それと site.csssite.min.css ファイルにバンドルするには、相対パスを bundleconfig.json に追加します。To minify custom.css and bundle it with site.css into a site.min.css file, add the relative path to bundleconfig.json:

[
  {
    "outputFileName": "wwwroot/css/site.min.css",
    "inputFiles": [
      "wwwroot/css/site.css",
      "wwwroot/css/custom.css"
    ]
  },
  {
    "outputFileName": "wwwroot/js/site.min.js",
    "inputFiles": [
      "wwwroot/js/site.js"
    ],
    "minify": {
      "enabled": true,
      "renameLocals": true
    },
    "sourceMap": false
  }
]

注意

または、次の glob パターンを使用できます。Alternatively, the following globbing pattern could be used:

"inputFiles": ["wwwroot/**/!(*.min).css" ]

この glob パターンはすべての CSS ファイルを照合し、縮小されたファイル パターンは除外されます。This globbing pattern matches all CSS files and excludes the minified file pattern.

アプリケーションをビルドします。Build the application. site.min.css を開き、ファイルの最後に custom.css のコンテンツが追加されていることに注意します。Open site.min.css and notice the content of custom.css is appended to the end of the file.

環境ベースのバンドルと縮小Environment-based bundling and minification

ベスト プラクティスとして、アプリのバンドルおよび縮小されたファイルを運用環境で使用することをお勧めします。As a best practice, the bundled and minified files of your app should be used in a production environment. 開発中は、元のファイルがあるので、アプリのデバッグが容易になります。During development, the original files make for easier debugging of the app.

ビューで Environment Tag Helper を使用して、ページに含めるファイルを指定します。Specify which files to include in your pages by using the Environment Tag Helper in your views. Environment Tag Helper を使用すると、特定の環境で実行されている場合にのみコンテンツがレンダリングされます。The Environment Tag Helper only renders its contents when running in specific environments.

Development 環境で実行されている場合、次の environment タグを使用すると、未処理の CSS ファイルがレンダリングされます。The following environment tag renders the unprocessed CSS files when running in the Development environment:

<environment include="Development">
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment names="Development">
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</environment>

Development 以外の環境で実行されている場合に、次の environment タグを使用すると、バンドルおよび縮小された CSS ファイルがレンダリングされます。The following environment tag renders the bundled and minified CSS files when running in an environment other than Development. たとえば、Production または Staging で実行すると、これらのスタイルシートのレンダリングがトリガーされます。For example, running in Production or Staging triggers the rendering of these stylesheets:

<environment exclude="Development">
    <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
          asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
          asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
    <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>
<environment names="Staging,Production">
    <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
          asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
          asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
    <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>

Gulp から bundleconfig.json を使用するConsume bundleconfig.json from Gulp

アプリのバンドルおよび縮小ワークフローで追加の処理が必要になる場合があります。There are cases in which an app's bundling and minification workflow requires additional processing. たとえば、画像の最適化、キャッシュ バスティング、CDN 資産の処理などです。Examples include image optimization, cache busting, and CDN asset processing. これらの要件を満たすために、Gulp を使用するようにバンドルおよび縮小ワークフローを変換できます。To satisfy these requirements, you can convert the bundling and minification workflow to use Gulp.

Gulp を使用するようにバンドルおよび縮小ワークフローを手動で変換できます。Manually convert the bundling and minification workflow to use Gulp

次の devDependencies を使用して package.json ファイルをプロジェクトのルートに追加します。Add a package.json file, with the following devDependencies, to the project root:

警告

gulp-uglify モジュールは、ECMAScript (ES) 2015 / ES6 以降をサポートしていません。The gulp-uglify module doesn't support ECMAScript (ES) 2015 / ES6 and later. ES2015 / ES6 以降を使用するには、gulp-uglify ではなく gulp-terser をインストールします。Install gulp-terser instead of gulp-uglify to use ES2015 / ES6 or later.

"devDependencies": {
  "del": "^3.0.0",
  "gulp": "^4.0.0",
  "gulp-concat": "^2.6.1",
  "gulp-cssmin": "^0.2.0",
  "gulp-htmlmin": "^3.0.0",
  "gulp-uglify": "^3.0.0",
  "merge-stream": "^1.0.1"
}

package.json と同じレベルで次のコマンドを実行して、依存関係をインストールします。Install the dependencies by running the following command at the same level as package.json:

npm i

グローバルな依存関係として Gulp CLI をインストールします。Install the Gulp CLI as a global dependency:

npm i -g gulp-cli

次の gulpfile.js ファイルをプロジェクトのルートにコピーします。Copy the gulpfile.js file below to the project root:

'use strict';

var gulp = require('gulp'),
    concat = require('gulp-concat'),
    cssmin = require('gulp-cssmin'),
    htmlmin = require('gulp-htmlmin'),
    uglify = require('gulp-uglify'),
    merge = require('merge-stream'),
    del = require('del'),
    bundleconfig = require('./bundleconfig.json');

const regex = {
    css: /\.css$/,
    html: /\.(html|htm)$/,
    js: /\.js$/
};

gulp.task('min:js', async function () {
    merge(getBundles(regex.js).map(bundle => {
        return gulp.src(bundle.inputFiles, { base: '.' })
            .pipe(concat(bundle.outputFileName))
            .pipe(uglify())
            .pipe(gulp.dest('.'));
    }))
});

gulp.task('min:css', async function () {
    merge(getBundles(regex.css).map(bundle => {
        return gulp.src(bundle.inputFiles, { base: '.' })
            .pipe(concat(bundle.outputFileName))
            .pipe(cssmin())
            .pipe(gulp.dest('.'));
    }))
});

gulp.task('min:html', async function () {
    merge(getBundles(regex.html).map(bundle => {
        return gulp.src(bundle.inputFiles, { base: '.' })
            .pipe(concat(bundle.outputFileName))
            .pipe(htmlmin({ collapseWhitespace: true, minifyCSS: true, minifyJS: true }))
            .pipe(gulp.dest('.'));
    }))
});

gulp.task('min', gulp.series(['min:js', 'min:css', 'min:html']));

gulp.task('clean', () => {
    return del(bundleconfig.map(bundle => bundle.outputFileName));
});

gulp.task('watch', () => {
    getBundles(regex.js).forEach(
        bundle => gulp.watch(bundle.inputFiles, gulp.series(["min:js"])));

    getBundles(regex.css).forEach(
        bundle => gulp.watch(bundle.inputFiles, gulp.series(["min:css"])));

    getBundles(regex.html).forEach(
        bundle => gulp.watch(bundle.inputFiles, gulp.series(['min:html'])));
});

const getBundles = (regexPattern) => {
    return bundleconfig.filter(bundle => {
        return regexPattern.test(bundle.outputFileName);
    });
};

gulp.task('default', gulp.series("min"));

Gulp タスクを実行するRun Gulp tasks

Visual Studio でプロジェクトをビルドする前に Gulp 縮小タスクをトリガーするには:To trigger the Gulp minification task before the project builds in Visual Studio:

  1. BuildBundlerMinifier NuGet パッケージをインストールします。Install the BuildBundlerMinifier NuGet package.

  2. 次の MSBuild ターゲットをプロジェクト ファイルに追加します。Add the following MSBuild Target to the project file:

    <Target Name="MyPreCompileTarget" BeforeTargets="Build">
      <Exec Command="gulp min" />
    </Target>
    

この例では、MyPreCompileTarget ターゲット内で定義されたタスクは、事前に定義された Build ターゲットの前に実行されます。In this example, any tasks defined within the MyPreCompileTarget target run before the predefined Build target. 次のような出力が Visual Studio の出力ウィンドウに表示されます。Output similar to the following appears in Visual Studio's Output window:

1>------ Build started: Project: BuildBundlerMinifierApp, Configuration: Debug Any CPU ------
1>BuildBundlerMinifierApp -> C:\BuildBundlerMinifierApp\bin\Debug\netcoreapp2.0\BuildBundlerMinifierApp.dll
1>[14:17:49] Using gulpfile C:\BuildBundlerMinifierApp\gulpfile.js
1>[14:17:49] Starting 'min:js'...
1>[14:17:49] Starting 'min:css'...
1>[14:17:49] Starting 'min:html'...
1>[14:17:49] Finished 'min:js' after 83 ms
1>[14:17:49] Finished 'min:css' after 88 ms
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

その他の技術情報Additional resources