scala 和scalajs play web开发环境

程英资
2023-12-01

scala 和scalajs web开发环境

建立过程:

sbt new vmunier/play-scalajs.g8  #(新建play  scalajs  工程)

参照https://github.com/paulb79/play-scalajs-skeleton-project link 更改项目配置。
另一个有价值的参考项目为:https://github.com/beikern/foulkon-ui/blob/master/build.sbt link

对一些关键点的理解:

  1. build.sbt 和 plugins.sbt配置
    a. 必须将整个项目分割为3个子Project,每个Project的依赖插件单独配置,以保证 jvm, js互不干扰,并保留share的子project,跨平台项目作为 js jvm平台复用部分,涉及js平台的scala代码有特殊的要求,参考scalajs项目文档。

  2. 关键插件scalajs-bundler
    a. 依赖npm和 webpack,但仅需要安装npm, 相关依赖也不用显式添加webpack
    b. npm 获取 js依赖, webpack将js依赖及 scalajs编译生成的文件 compress到同一js文件中
    c. js依赖只有在 相关库有被显式使用的时候才会真正被添加(从现象来看是 用户个人的编码里有使用的 js依赖才会被添加, 第三方库中使用的js依赖不会,但如果用户使用了第三方scalajs库,则相关的js依赖也会添加),这一步与scalajs三方库 是否使用facade有关。可详细阅读 scalajs的document。
    d. 针对c项,部分三方scalajs库需要 原js库被export到js global空间中,这个需要使用webpack的 配置文件将相关的模块输出到global空间,可以参考:
    https://scalacenter.github.io/scalajs-bundler/cookbook.html#global-namespacelink
    f. 关于d项中 webpack.config.js配置项:

var webpack = require('webpack');

// Load the config generated by scalajs-bundler
var config = require('./scalajs.webpack.config');

// Exported modules
var globalModules = {
  "plotly.js-dist-min": "Plotly",
};

//const importRule = {
//  // Force require global modules
//  test: /.*-(fast|full)opt\.js$/,
//  loader:
//    "imports-loader?" +
//    Object.keys(globalModules)
//      .map(function(modName) {
//        return modName + "=" + globalModules[modName];
//      })
//      .join(",")
//};

const exposeRules = Object.keys(globalModules).map(function(modName) {
  // Expose global modules
  return {
    test: require.resolve(modName),
    loader: "expose-loader?" + globalModules[modName]
  };
});

//const allRules = 
const allRules = exposeRules.concat(config.module.rules);

config.module.rules =allRules
module.exports = config;

关于globalModules 的设定说明,key为子module的名,对应js中的依赖路径, value为输出到gloable中的辩识符


var globalModules = {
  "plotly.js-dist-min": "Plotly",
};

// 对于 expose来说,等效于 javascript中的var Plotly = require(plotly.js-dist-min)

//类似的其它例子:
// Exported modules
var globalModules = {
  "material-ui": "mui",
  "material-ui/styles" : "mui.Styles",
  "material-ui/svg-icons/index" : "mui.SvgIcons",
  "react": "React",
  "react-infinite": "Infinite",
  "react-paginate": "ReactPaginate",
};

e. 关于d项中的配置,build.stb的client的setting需要添加

//添加 js 依赖
 npmDependencies in Compile ++= Seq(       // js dep
      "react" -> "16.13.1",
      "react-dom" -> "16.13.1",
        "plotly.js-dist-min" -> "1.52.2"
    ),

// 添加 expose-loader
npmDevDependencies in Compile ++= Seq(      // for compile
      "expose-loader" -> "0.7.5",           //  expose lib js global    accord scalajs-bundler
//      "imports-loader" -> "0.8.0",
//      "webpack-merge" -> "4.1.2",
    ),

// 设定配置文件
   webpackConfigFile := Some(baseDirectory.value / "webpack.config.js"),    // 指明配置文件地址

附录:当前使用的完整sbt配置

build.sbt

lazy val server = (project in file("server")) //   server side set   noly jvm
  .settings(commonSettingsJs)
  .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 ++= {
      val log4jversion = "2.13.0"
      Seq(
        "com.vmunier" %% "scalajs-scripts" % "1.1.4",
//        ws,   // play http ws client libary
        "com.typesafe.play" %% "play-ahc-ws" % "2.8.7",
//        ehcache,  //cache for play ws
        guice,
        specs2 % Test,
        // scala parallel
        "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.1",

        "org.apache.kafka" % "kafka-clients" % "0.11.0.3" exclude("org.slf4j", "slf4j-log4j12"),
        "com.baidu.adu" % "st-interface" % "1.2.0-SNAPSHOT",
        "org.apache.logging.log4j" % "log4j-slf4j-impl" % log4jversion,
        "org.apache.logging.log4j" % "log4j-core" % log4jversion,
        "org.apache.logging.log4j" % "log4j-api" % log4jversion,
      )
    },
    // add dependency asser          accord scalajs-bundler
    npmAssets ++= NpmAssets.ofProject(client) { modules => (modules / "font-awesome").allPaths }.value,
  )
  .enablePlugins(PlayScala) // for play framwork
  .disablePlugins(PlayLogback)
  .enablePlugins(WebScalaJSBundlerPlugin) //   accord scalajs-bundler
  .dependsOn(sharedJvm)


lazy val client = (project in file("client")) // client side  only  scala js
  .enablePlugins(ScalaJSPlugin)
  //    .enablePlugins(ScalaJSWeb)       // must not enable conflict with WEBJSBundler
  .enablePlugins(ScalaJSBundlerPlugin) // accord scalajs-bundler
  .enablePlugins(JSDependenciesPlugin)
  //  .enablePlugins()
  .settings(commonSettingsJs)
  .settings(

    //    scalaJSUseMainModuleInitializer := true,
    webpackBundlingMode := BundlingMode.LibraryAndApplication(),
    libraryDependencies ++= Seq( // scala dep
      "org.scala-js" %%% "scalajs-dom" % "1.1.0",

//       plotly face
            "org.openmole" %%% "scala-js-plotlyjs" % "1.5.1",

      //binding
      "com.thoughtworks.binding" %%% "binding" % "12.0.1",
      "org.lrng.binding" %%% "html" % "1.0.3",
      "org.querki" %%% "jquery-facade" % "2.0",

    ),
    npmDependencies in Compile ++= Seq( // js dep
      "plotly.js-dist-min" -> "1.52.2",

      // jquery  for semantic
      "jquery" -> "3.5.1"
),
npmDevDependencies in Compile ++= Seq( // for compile
  "expose-loader" -> "0.7.5", // expose lib js global    accord scalajs-bundler
  "imports-loader" -> "0.8.0",
  "webpack-merge" -> "4.2.2",

),
webpackConfigFile := Some(baseDirectory.value / "webpack.config.js"),
requireJsDomEnv in Test := true
)
.dependsOn(sharedJs)

lazy val shared = crossProject(JSPlatform, JVMPlatform)
  .crossType(CrossType.Pure)
  .in(file("shared"))
  .settings(commonSettings)
  .jsConfigure(_.enablePlugins(ScalaJSWeb))
  .settings(
    libraryDependencies ++= Seq(
      "com.typesafe.play" %%% "play-json" % "2.9.2",
    )
  )
lazy val sharedJvm = shared.jvm
lazy val sharedJs = shared.js



lazy val commonSettingsJs = Seq(
  scalaVersion := "2.13.1",
  organization := "idg.itsqa",
  //     binding-scala
  scalacOptions ++= {
    import Ordering.Implicits._
    if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) {
      Seq("-Ymacro-annotations")
    } else {
      Nil
    }
  },
  // Enable macro annotations by adding compiler plugins for Scala 2.12
  libraryDependencies ++= {
    import Ordering.Implicits._
    if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) {
      Nil
    } else {
      Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full))
    }
  }
)

lazy val commonSettings = commonSettingsJs
//  Seq(
//  scalaVersion := "2.13.1",
//  organization := "xx.xx"
//)



scalaVersion := "2.13.1"
organization := "xx.xx"

onLoad in Global := (Command
  .process("project server", _: State)) compose (onLoad in Global).value

完整插件 plugins.sbt

 // Comment to get more information during initialization
logLevel := Level.Info

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


addSbtPlugin("org.scala-js" % "sbt-jsdependencies" % "1.0.2")

// Use Scala.js v1.x
//addSbtPlugin("com.vmunier"               % "sbt-web-scalajs"           % "1.1.0")    // must disable because  conflict with ScalaJSBundlerPlugin's webpack-based source mapping
addSbtPlugin("org.scala-js"              % "sbt-scalajs"               % "1.3.0")  // 1.2.0

// If you prefer using Scala.js v0.6.x, uncomment the following plugins instead:
// addSbtPlugin("com.vmunier"                  % "sbt-web-scalajs"           % "1.1.0-0.6")
// addSbtPlugin("org.scala-js"                 % "sbt-scalajs"               % "0.6.33")

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

addSbtPlugin("ch.epfl.scala" % "sbt-web-scalajs-bundler" % "0.20.0")   // 0.18.0 for server web

//addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2")

完整 webpack.config.js

var Webpack = require('webpack');
const Merge = require("webpack-merge");

// Load the config generated by scalajs-bundler
var config = require('./scalajs.webpack.config');

// Exported modules
var globalModules = {
  "plotly.js-dist-min": "Plotly",
  "jquery": "jQuery"
//  "react":"React",
//  "react-dom": "ReactDom",
//  "@material-ui/core": "MaterialCore",
//  "@material-ui/icons": "MaterialIcons",
//  "@material-ui/lab": "MaterialLab",
};

const importRule = {
  // Force require global modules
  test: /.*-(fast|full)opt\.js$/,
  loader:
    "imports-loader?" +
    Object.keys(globalModules)
      .map(function(modName) {
        return modName + "=" + globalModules[modName];
      })
      .join(",")
};

const exposeRules = Object.keys(globalModules).map(function(modName) {
  // Expose global modules
  return {
    test: require.resolve(modName),
    loader: "expose-loader?" + globalModules[modName]
  };
});

//const allRules = exposeRules.concat(importRule).concat(config.module.rules);
const allRules = exposeRules.concat(importRule).concat(config.module.rules);

config.module.rules =allRules

Object.keys(config.entry).forEach(function(key) {
  // Prepend each entry with the globally exposed JS dependencies
  config.entry[key] = Object.keys(globalModules).concat(config.entry[key]);
});

// Globally expose the JS dependencies
//config.module.loaders = Object.keys(globalModules).map(function (pkg) {
//  return {
//    test: require.resolve(pkg),
//    loader: "expose-loader?" + globalModules[pkg]
//  }
//});


module.exports = config

示例使用的npm版本
npm -v = 6.14.10
node -v = 14.15.4

转载请注明本文出处:[查看原文][1]

*[1]https://editor.csdn.net/md/?articleId=113756406

 类似资料: