uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

静的リソース機能はGrails 2において中核的価値の一つである

タイトルは釣りっぽくもありますが、G*MagazineのVol.3Vol4にも紹介されているGrails Resoucesプラグインについて熱く語ってみます。


Grails ResouceプラグインGrails 2.0からGrails本体に同梱される標準プラグインとなりました。つまり明示的にインストールしなくても使えるので、それがプラグインであることをあまり意識せずに使うことになります。GORMなどといっしょですね。実際、Grails本体のリファレンスマニュアルのResourceの章(日本語)で主なところが説明されていますので、「Grailsの静的リソース管理機能」とでも言ったほうが今となっては適切なのでしょう*1

ただ、この記事では便宜上、リソースプラグインと呼びます。

背景

リソースプラグインは、Webアプリケーションにおけるブラウザ側にダウンロードされる(HTML以外の)コンポーネント、たとえばCSSJavaScript、画像などを、Grailsで開発するWebアプリから利用するためのものです。

Webアプリケーションでは、これらの要素が重要なことは論を待つまでもありませんね。特に昨今は、クライアント側のJavaScriptライブラリやフレームワークが盛況です。

その意味では、現代のWebアプケーションの開発において、フレームワークの役割は、サーバーサイドだけに留まっていてはしょうがない、と言えましょう。「GSPがあるから、その中にJavascriptでもCSS参照でも自由に書いて構わないよ、あとは僕は知らないよ」ってのは、現代のWebアプリフレームワークとしてはお話にならないわけです。さらに、特定のJavaScriptライブラリやフレームワークにサーバサイドが依存することも望ましくありません。

だから、サーバサイドフレームワークの観点から見ると、問題は、JavaScriptライブラリやCSSフレームワークを始めとするブラウザサイドのコンポーネントを切り替えたり、共存させたり、どう管理するか、です。リソースプラグインの目的の一つは、この問題を解決することです。

加えて、リソースプラグインは静的リソースに関する最適化も行います。つまり、CSSJavascriptを結合したりminifyしたりzip圧縮したりキャッシュしたりのいわゆるフロントエンド最適化(FEO)と呼ばれる技術分野を支援します。実際に、GrailsとPlay! Frameworkを比較すると、サイトの総合的レスポンスにおいてはGrailsの方が有意に優れているのですが、その理由を推察するにおそらく、リソースプラグインの自動最適化機能が効いてることが想像できます。このような最適化は、実運用サイトでは必要になることが多く、役に立つことでしょう。

プラグイン機構との関係

リソースプラグインの重要性は、Grailsのプラグイン機構の有用性と直結します。たとえば、Grailsのプラグインには、静的リソースをプラグインの形でアプリに組み込んで使うことを主目的とするものが腐るほどたくさんあります。ほんの一例としては、Grails Twitter Bootstrapプラグインだの、CoffeeScriptプラグインLess CSSプラグインだのです。

このような、静的リソースを含む再利用単位としてのプラグインを、アプリケーションから複数利用しようとする場合、動作の整合性を担保するために以下に留意する必要があります。

  • 静的リソースをHTML中で読み込むための順序関係
  • 競合回避(プラグイン由来のリソースは、衝突回避のため別のサブディレクトリに配置する等)
  • 複数のコンポーネントから共有されるリソースがあるとき、多重読み込みしないこと

リソースプラグインは、このような問題を適切にかつ自動的に解決してくれます。

Grailsの将来の方向性を示すもの

まとめると、リソースプラグインは、「静的リソースの集合からなるブラウザサイドコンポーネント(jQuery**、Twitter Bootsrapといった単位のもの)を、アプリケーションに組み込める部品としての独立性が高いプラグインの形にまとめることを前提として、それらを複数使った時でも矛盾なく、かつ統一的に自動的に機能するフロントエンド最適化機能を持った基盤」なのであり、Grails本体に標準添付されてることに強い必然性があることがお判りになるでしょう。

余談になるかもしれませんが、昨今のWebアプリケーションの風潮として、ビュー処理が、HTMLをサーバサイドで生成する形から、ブラウザ上のJavaScriptで処理する形にシフトしてきていることは明らかでしょう。単純なAJAX処理にとどまらず、最近ではJavaScriptフレームワーク上でMVCアーキテクチャパターンを採用するようなものも出てきているとのことです。いずれの場合でも、ビューとサーバ間はAJAXRest APIで通信するようになります*2。このような状況を背景として、リソースプラグインのGrailsへの標準プラグインとしての組み込みは、Grailsの開発ターゲットをそっち方向にも追随していこうとしていることを意味しているとも言えるでしょう。Grails開発のロードマップによれば、Grails 3では「単一ページのWebアプリを作成するためのWebサービスの先進的サポート」が予定されているそうですが、このこととも合せて、Grails開発の戦略的な方向性を感じ取ることができます。

想像で言うと、将来はnode.jsとかnettyとかVert.x的なものがGrailsの(非サーブレット)フロントコントローラの一種として取り込まれて、GORMやサービスは同様に使い続けられる、というようなイメージですかね。逆に言うとそれに成功しなかったら、先進的WebアプリフレームワークとしてのGrailsのさらなる発展は無いでしょう(キリっ)。

静的リソースはどんな風に使うものなのか

話を戻しましょう。ここでは詳細はばっさり省いて、リソースプラグインの使い方について、ごくザッパクに説明します。

リソースプラグインを使う場合、以下の手順で静的リソースを定義し、使用することができます。

  1. 以下の情報を含めて静的リソースを定義します。リソースを定義する場所は、conf/Configs.groovyもしくはconf/ApplicationResouces.groovyのいずれか、もしくはプラグインの場合conf/XXXResouces.groovy というファイルです。既存プラグインが静的リソースを提供するのであれば、以下は定義済みなので書く必要はありません。
    • リソースのファイル名(file:'hoge.gif')
    • リソースが置かれる場所のサブディレクトリ(dir:'image/subdir')
    • プラグイン中で定義する場合、どのプラグインに所属するか
    • これらのリソースの集合をグループ化したもの(モジュール)を定義して名前をつける。
    • モジュール間の依存関係を定義する(このモジュールが動作するためには、あらかじめどのモジュールが読み込まれている必要があるか、という情報)
    • 「どのへんに配置するか情報」(disposition)の指定。head(最初)、defer(後ろの方)のいずれかを指定できます。
  2. その上で、「どのモジュールを使う」という宣言をGSPで行います

「どのへんに配置するか情報(disposition)」というのは何かというと、最終的には静的リソースは、<link type=css />や<script src=""/>などのタグでHTML中から参照されることで利用可能になるわけですが、これらのタグを、以下の(A),(B)のどっちの場所に突っ込むか、という指定です。

<html>
  <head>
    <title>たいとる</title>
    <!-- ★(A)閉じHEADタグの直前。(disposition=head)★-->
  </head>
  <body>
    <h1>ほげ</h1>
    <h1>ふが</h1>
    <!-- ★(B)閉じBODYタグの直前。(disposition=defer)★-->
  </body>
</html>

たとえば、

  • モジュールA
    • A1(dispositon=head)
    • A2(dispositon=defer)
  • モジュールB
    • B1(dispositon=head)
    • B2(dispositon=defer)

というリソースが定義されているとして、GSPの中で「モジュールAとモジュールBを使う」という宣言を行うと、生成されるHTMLは、

<html>
  <html>
    <title>たいとる</title>
    <A1を参照するタグ>
    <B1を参照するタグ>
  </html>
  <body>
    <h1>ほげ</h1>
    <h1>ふが</h1>
    <A2を参照するタグ>
    <B2を参照するタグ>
  </body>
</html>


というようになるわけです。

(A)および(B)の位置を指定するためには、<r:layoutResources>タグを指定します。このタグは、GSP中で2回必ず指定する必要があり、最初のがhead、2個目(つまり最後)がdeferの役割りになります。これらのタグは、sitemeshレイアウト中で指定することが多いでしょう。

また、「モジュールを使用しますよ」という指定をするためのタグが<r:require>です。
具体的には、例えばこんな感じです。

<html>
  <head>
    <title>たいとる</title>
    <r:require module="jquery"> <!-- jqueryモジュールを使いますよ -->
    <r:layoutResources>
  </head>
  <body>
    <h1>ほげ</h1>
    <h1>ふが</h1>
    <r:layoutResources>
  </body>
</html>

Sitemeshレイアウトの中で指定する場合には以下のようになるでしょう。

<html> <!-- layout/main.gsp -->
  <head>
    <r:require module="jquery"> <!-- requireはレイアウトを使う個々のGSPに書いても良い -->
    <g:layoutTitle/>
    <r:layoutResources>
  </head>
  <body>
    <g:layoutBody/>
    <r:layoutResources>
  </body>
</html>

詳しくは以下などをどうぞ。

おしまい。

*1:プラグインなので外すことはできますが

*2:もちろんHTML生成が無くなるわけではありません。ハイブリッドになります