The Client
一般的なSPAと同じように、clientはひとつのHTMLファイルといくつものリソースファイル(JSとCSS)によって構成されています。リソースのうちの一つはScalaコードからScala.jsによって生成された実際のJavaScriptコードです。
HTMLはPlayのテンプレートシステムを利用しており、serverプロジェクト以下に twirl/views/index.scala.html
として定義されています。CSSやJSファイルへのリンクなど、普通のHTMLによくあるものが見つかるでしょう。すべてのHTMLはアプリケーションによって生成されるため、見てのとおり<body>
要素の中身はまったくの空です。@playscalajs
ディレクティブは、生成されたJavaScriptおよびその依存関係にあるコードをロードする<script>
タグを挿入するよう、ScalaJSPlay plugin に指示します。参考として、_asset
関数は各アセット(css, js, images)に、server/src/main/resources/application.confで定義されているCDNへのリンクをプリフィックスとして付与します。"${?APPLICATION_CDN}"の行は、環境変数が設定されている場合に、その値で本番環境でのデフォルト値を上書きする一つの方法であることに注意してください。_asset
関数は本番環境において、ファイルに変更を加えた際にクライアントのプロキシとブラウザキャッシュをクリアするために、main.min.cssをmain-<version>
.min.cssと自動的に変換することでバージョン番号をファイル名に付加することも行います。
より詳しい情報やベストプラクティスについては PlayFramework Fingerprintingを参照してください。
<body>
<div id="root">
</div>
@playscalajs.html.scripts(projectName = "client", fileName => _asset(fileName).toString)
</body>
</html>
ReactやjQuery、Bootstrap、chart componentといった外部のJavaScriptを参照する代わりに、ビルドシステムがそれらすべてを一つのJavaScriptファイル(client-jsdeps.js)に結合します。くわしくはこちらを参照してください。
最後のJavaScriptの参照はコンパイルされたアプリケーションコードです。
ブラウザがすべてのリソースをロードすると、SPAMain.scala
シングルトンクラスに定義されているSPAMain().main()
を呼び出します。これがアプリケーションのエントリーポイントです。
クラス自体はとてもシンプルです。
@JSExport("SPAMain")
object SPAMain extends JSApp {
@JSExport
def main(): Unit = {
// create stylesheet
GlobalStyles.addToDocument()
// create the router
val router = Router(BaseUrl.until_#, routerConfig)
// tell React to render the router in the document body
React.render(router(), dom.document.getElementById("root"))
}
}
外部からアクセス可能なクラスや関数には@JSExport
アノテーションが付与されているため、Scala.jsコンパイラはそれらを最適化によって除去せず、グローバルスコープ内で正しい名前で利用できるようにします。
main()
が行うのは、シンプルにrouterを生成し、ドキュメント内の<div id="root">
タグにレンダリングするようReactに指示することです。
ここまで何度か「React」の単語を目にして、あなたはそれが何なのか疑問に思ったかもしれません。React はFacebookによって開発されたユーザーインターフェースを構築するためのJavaScriptライブラリです。
「Scalaがとてもグレートなら、なぜJavaScriptのライブラリをScala.jsから使うんだ」とあなたは尋ねるかも知れません。同じようなScala.jsのライブラリでやるのが良さそうです。既にあるのでそちらを使うことにしましょう。David Barri (@japgolly)によるscalajs-reactというとてもナイスなReactラッパーがあります。
SPAを構築するのに使えるScala.jsのライブラリは他にもいくつかありますが、私はReactで行きたいので、チュートリアルではこれを使っていくことにします。:)