この記事はG*Advent Calendar(Groovy,Grails,Gradle,Spock...) Advent Calendar 2014 - Qiitaの18日目です。
Asset PipelineはRuby on Railsによって生み出された仕組みです。Grailsでは、このRailsの仕組みを参考にしたAsset Pipelineプラグインが提供されています。
このAsset Pipelineプラグインは、Javascript、スタイルシート、画像といった静的なリソースを管理するプラグインです。アセットとは日本語で「資産」という意味で、Javascript、スタイルシート、画像といった静的なリソースを指しています。
静的リソースを管理するプラグインは、Grails 2.3まではResourcesプラグインがデフォルトのプラグインでしたが、Grails 2.4からはこのAsset Pipelineがデフォルトのプラグインになっています。
Asset Pipelineの機能
このプラグインが提供する主な機能は次のようなものがあります。
- アセットの結合、最小化、圧縮
- フィンガープリント
- CoffeeScriptやSASSといったアセットのコンパイル
アセットの結合は、複数のJavascriptやスタイルシートのファイルを、1つのJavascript、スタイルシートのファイルにまとめる機能です。ファイルを1つにまとめることで、リソースを取得するためのHTTPリクエストの数が減るため、不要なオーバーヘッドが削減され、Webアプリケーションのフロントエンドのパフォーマンス向上に繋がります。またアセットの最小化と圧縮によって、さらにファイルサイズを小さくできます。
フィンガープリンティングはファイル内容に応じて、自動的にファイル名にフィンガープリントを付与してくれる仕組みです。フィンガープリントはファイル内容が異なれば、異なるフィンガープリントが付与されます。Asset Pipelineでは、フィンガープリントはファイル内容のハッシュ値です。このハッシュ値はファイル名の末尾に挿入されます。たとえば、application-5dca4dc3f1f721f16daeb7ba450f1500.jsといったような形です。
フィンガープリンティングはキャシュバスティング (Cache Busting) またはキャッシュバスター (Cache Buster) と呼ばれるブラウザにキャッシュしたリソースを使わせないようにするための仕組みです。たとえばアプリケーションを再デプロイしたときに、リソースが更新されているにもかかわらず、更新したリソースのファイル名が更新前と同じファイル名の場合は、ブラウザが新しいリソースではなくキャッシュされたデータを使ってしまい不具合が起きてしまいます。フィンガープリンティングを使うと、ファイル内容が変われば自動的に別のファイル名になるため、ブラウザは別のファイルとして認識してキャシュしたデータを使うことなく、新たにリソースを取得しにいきます。
Gradleで使う
ここからが本題です。
Grailsのプラグインとして提供されていたAsset Pipelineプラグインですが、2.0からコア機能がasset-pipeline-coreとしてプラグインから独立した形で提供されるようになりました。これによりGrailsに依存することなく、様々なJVMプロジェクトで使用可能になっています。
それでは早速Gradleで使ってみましょう。
build.gradleの準備
build.gradle
を以下のように書きます(最新のバージョンやオプションは本家のドキュメントを参照してください)。
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.bertramlabs.plugins:asset-pipeline-gradle:2.0.19'
}
}
apply plugin: 'asset-pipeline'
assets {
minifyJs = true
minifyCss = true
}
アセットの格納
アセットはassets/javascripts
、assets/stylesheets
、assets/images
ディレクトリに格納します。名前の通りimages
には画像ファイル、javascript
にはスクリプトファイル、stylesheets
にはスタイルシートといったアセットを配置します。必要に応じてjavascripts/module/my.js
といったように、更にディレクトリ階層を作ることもできます。
images
、javascripts
、stylesheets
といったassetsディレクトリ直下のサブディレクトリは特別な意味を持っています。それは、Asset Piplineがアセットを処理したあとは、これらのサブディレクトリは存在しなくなるということです。これらのサブディレクトリは、同じ種類のファイルを管理しやすいようにする目的で存在しています。
例えばassets/javascript/my.js
とassets/stylesheets/my.css
といったファイルがあったとします。これらmy.js
、my.css
はAsset Piplineがリソースを処理したあとは同じディレクトリ中に格納されます。
また、assetsディレクトリ配下に、独自のサブディレクトリも作成することもできます。この場合も他のサブディレクトリと同じように振る舞います。
マニフェストとディレクティブ
Asset Pipelineではマニフェストとディレクトリという仕組みが用意されています。マニフェストは複数のリソースを纏めるための定義で、ディレクティブはそのマニュフェストの中でリソースを指定するための記法です。
マニフェストはassets/javascripts、assets/stylesheets配下に格納するリソースファイルのコメントとして記述します。コメント形式はリソースごとに異なります。たとえば、JavaScriptの場合は次のように記述できます。
// JavaScriptのマニュフェスト定義には//=を使います。
//
//= require jquery
//= require app/models.js
//= require_self
//= require_tree views
console.log("This is application.js");
スタイルシートの場合は次のように記述できます。
/* スタイルシートのマニュフェスト定義には*=を使います。
*
*= require_self
*= require header
*= require_tree .
*/
body {
background: #fff;
}
マニフェストファイルは、マニフェストファイルであると共にリソースファイルでもあります。そのため、JavaScirptのファイルであればJavaScriptを記述でき、スタイルシートであればスタイルがそのファイルの中で定義できます。
マニフェストの中でrequire
、require_tree
、require_self
といったキーワードがディレクティブです。このディレクティブを指定することで、外部のリソースファイルをこのファイルに読み込めます。
ディレクティブには次の種類が存在します。
-
require
- 指定されたリソースを読み込みます。
リソースの拡張子は省略することもできます。
- 指定されたリソースを読み込みます。
-
require_tree
- 指定されたパスのリソースを再帰的に読み込みます。
自身のアプリケーションのリソースだけが対象になり、プラグインのリソースは対象になりません。
- 指定されたパスのリソースを再帰的に読み込みます。
-
require_self
- 自身のリソースを読み込みます。
省略した場合は自動的に最後に読み込まれます。
- 自身のリソースを読み込みます。
-
require_full_tree
- 指定されたパスのリソース再帰的に読み込みます。
require_treeと異なり、アプリケーションのリソースだけでなくプラグインのリソースも対象になります。
- 指定されたパスのリソース再帰的に読み込みます。
-
encoding
- 処理するファイルのエンコーディングを指定します。
リソースの拡張子は省略できます。たとえばJavaScriptの場合、require jquery
はrequire jquery.js
と記述したのと同じです。
コンパイルしてみよう
GradleでアセットをコンパイルするにはassetCompile
タスクを実行しまう。
gradle assetCompile
コンパイルが成功するとbuild/assets
配下にコンパイル結果が生成されます。
まとめ
ここまでGradleでの使い方をみてきましたが、実はGrails 3からビルド環境がGradleで一新される関係で、同じようにGradleのプラグインでアセットが処理されるようになります(Grailsプラグインの依存は別途必要ですが)。また、コア機能が独立したことにより他のJVMアプリケーションからも使えるようなった関係でSpring Bootから使用できるサポートなんかも入っています。Spring BootではAsset Pipelineの機能を提供しないなんて話もあるので、このプラグイン使うのもひとつの手ではないでしょうか。
ではでは。