4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Play FrameworkによるScalajsの続き

Last updated at Posted at 2018-12-09

play + Scala-bundlerの利用

以前のエントリでScala.jsとPlayを使った、サーバ・クライアントの例を書きましたが、Scalajs-bundlerを使った際にいろいろと苦労したのでメモします。この辺はきちんとドキュメントを読めば書いてあるのですが、なかなか分かりにくいのです。

準備

例によってテンプレートよりプロジェクトを作成します。

sbt new vmunier/play-scalajs.g8

plugins.sbtにScalajs-bundlerのプラグインを追記します。一般のときとインポートするものが違うので注意です。依存性の関係から、バージョンは0.6系を使います。project/build.propertiesを適切なバージョン(例えばsbt.version=1.2.8)にするのを忘れてはいけません!

plugins.sbt
// Comment to get more information during initialization
logLevel := Level.Warn

// Resolvers
resolvers ++= Seq("Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/")

// Sbt plugins
addSbtPlugin("com.vmunier"               % "sbt-web-scalajs"           % "1.0.8-0.6")
addSbtPlugin("org.scala-js"              % "sbt-scalajs"               % "0.6.25")

// sbt-webと使う場合
addSbtPlugin("ch.epfl.scala" % "sbt-web-scalajs-bundler" % "0.14.0")

addSbtPlugin("com.typesafe.play"         % "sbt-plugin"                % "2.6.15")
addSbtPlugin("org.portable-scala"        % "sbt-scalajs-crossproject"  % "0.6.0")
addSbtPlugin("com.typesafe.sbt"          % "sbt-gzip"                  % "1.0.2")
addSbtPlugin("com.typesafe.sbt"          % "sbt-digest"                % "1.1.4")

build.sbtには、server側にWebScalaJSBundlerPluginを有効にします。client側にもScalaJSBundlerPluginが必要です。ここでは、reactをインポートできるようにしています。

build.sbt
import sbtcrossproject.{CrossType, crossProject}

lazy val server = (project in file("server"))
  .settings(commonSettings)
  .settings(
    scalaJSProjects := Seq(client),
    pipelineStages in Assets := Seq(scalaJSPipeline),
    pipelineStages := Seq(digest, gzip),
    // triggers scalaJSPipeline when using compile or continuous compilation
    compile in Compile := ((compile in Compile) dependsOn scalaJSPipeline).value,
    libraryDependencies ++= Seq(
      "com.vmunier" %% "scalajs-scripts" % "1.1.2",
      guice,
      specs2 % Test
    ),
  )
  .enablePlugins(PlayScala,WebScalaJSBundlerPlugin)
  .dependsOn(sharedJvm)

lazy val client = (project in file("client"))
  .settings(commonSettings)
  .settings(
    scalaJSUseMainModuleInitializer := true,
    libraryDependencies ++= Seq(
      "org.scala-js" %%% "scalajs-dom" % "0.9.6",
      "com.github.japgolly.scalajs-react" %%% "core" % "1.3.1"
    ),

    npmDependencies in Compile ++= Seq(
      "react" -> "16.5.1",
      "react-dom" -> "16.5.1"
    )
  )
  .enablePlugins(ScalaJSWeb,ScalaJSPlugin,ScalaJSBundlerPlugin)
  .dependsOn(sharedJs)

lazy val shared = crossProject(JSPlatform, JVMPlatform)
  .crossType(CrossType.Pure)
  .in(file("shared"))
  .settings(commonSettings)

lazy val sharedJvm = shared.jvm
lazy val sharedJs = shared.js

lazy val commonSettings = Seq(
  scalaVersion := "2.12.7",
  organization := "com.example"
)
// loads the server project at sbt startup
onLoad in Global := (onLoad in Global).value andThen { s: State => "project server" :: s }

トランスパイル済みのJavascriptの参照

Scala.jsでは、fastOptJSなどのコマンドを使って出力したJavascriptをHTML内にscriptタグで参照しますが、Playを使った場合は、TwirlとScalajs-Scriptsで動的に参照を記載します。ここで、前に述べたWebScalaJSBundlerPluginを有効にしていないと、webpackでバンドルされたJavascriptを参照できずに失敗します。

サンプルコードのままで、index.scala.htmlを以下のように変更します。@scalajs.html.scriptsというのがScalajs-Scriptsの文法で、最初の引数が、クライアントで出力されるJavascriptの接頭語の文字になります。いずれにせよ、これによってClient側で記載したMain関数部分がインポートされて、ブラウザ表示の際に自動起動されるようになります。

server/app/views/index.scala.html
@(message: String)

@main("Play with Scala.js") {
<div id="hello"></div>
@scalajs.html.scripts(
    "client",
    name => routes.Assets.versioned(name).toString,
    name => getClass.getResource(s"/public/$name") != null)
}

Client側の実装

一番簡単なReactのコードを実装してみます。scalaJSUseMainModuleInitializer := trueとしているので、このコードが自動起動されます。

client/src/main/scala/com/example/ScalaJSExample.scala
package com.example

import org.scalajs.dom
import dom.document
import japgolly.scalajs.react._
import japgolly.scalajs.react.vdom.html_<^._

object ScalaJSExample {

  def main(args: Array[String]): Unit = {
    val target =  document.getElementById("hello")
    val Hello =
      ScalaComponent.builder[String]("Hello")
        .render_P(name => <.div("Hello there ", name))
        .build

    Hello("everyone").renderIntoDOM(target)
  }
}

sbt runでPlayを起動します。http:localhost:9000にアクセスするとHello there everyoneと表示されていると思います。うん、非常につまらないサンプルですね。

出力されたHTMLに目を向けてみると、以下のように出力されていました。これがBundle済みのソースコードを参照しているというのが、いまだにピンときません。

<script src="/versionedAssets/client-fastopt-bundle.js" type="text/javascript"></script>

参考

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?