2

我已经使用 JSImport 编写了一个外观,并且它可以工作。不幸的是,我通过反复试验得出了解决方案,我不完全理解为什么这个特定的解决方案有效,但我尝试过的其他解决方案却没有。

背景:我从一个使用 sbt 构建的工作项目开始,这是一个单页应用程序,它使用 scala.js 实现客户端代码,使用 scala 和 Play 框架实现服务器端。javascript 库与 web jar 一起打包,并使用 sbt jsDependencies 变量捆绑到客户端 js 文件中。我想实现一些需要库 up rev 的新功能,然后需要一些仅以 npm 格式提供的 javascript 库的 up rev。因此,现在我使用带有 scalajs-bundler 插件的 npmDependencies 包括客户端应用程序的所有 javascript 依赖项。这打破了一些导致我的问题的 scalajs 外观。

我将使用外观来log4javascript作为这个问题的示例。

该变量log4javascript是用于访问 api 其余部分的顶级对象。

当 js 库作为 web jars 包含时,这就是外观的log4javascript实现方式:

@js.native
@js.annotation.JSGlobalScope
object Log4JavaScript extends js.Object {
  val log4javascript:Log4JavaScript = js.native
}

更改为 npm 后:


import scala.scalajs.js.annotation.JSImport.Namespace

@JSImport("log4javascript", Namespace)
@js.native
object Log4JavaScript extends js.Object {
  def resetConfiguration(): Unit = js.native
  def getLogger(name:js.UndefOr[String]): JSLogger = js.native
  ...
}

按照用于编写导入模块的 scala.js文档,我预计对象名称(在本例中为 Log4JavaScript)必须与导出的符号名称匹配才能使绑定起作用。但是,log4javascript.js 中的顶级符号是log4javascript. 经过试验,似乎 scala 对象名称对绑定没有影响。无论我如何命名 scala 顶级对象,它都会正确绑定。

有人可以解释在将“命名空间”参数用于 JSImport 时,scala 对象/类/def/val 名称与 javascript 模块中的名称之间存在什么关系(如果有)?

根据 scala.js 文档,似乎我应该能够提供 js 对象的实际名称(我也尝试过“Log4JavaScript”)

@JSImport("log4javascript", "log4javascript")
@js.native
object SomeOtherName extends js.Object {
  def resetConfiguration(): Unit = js.native
  def getLogger(name:js.UndefOr[String]): JSLogger = js.native
  ...
}

但是,这无法绑定。当我尝试访问任何成员函数时,会出现运行时错误。

Log4JavaScript.resetConfiguration()

Uncaught TypeError: Cannot read property 'resetConfiguration' of undefined

有人可以解释为什么这不起作用吗?

log4javascript 还在log4javascript. 当 lib 作为 web jar 包含时,定义如下所示:

@js.native
@JSGlobal("log4javascript.AjaxAppender")
class AjaxAppender(url:String) extends Appender {
  def addHeader(header:String, value:String):Unit = js.native
}

切换到 npm 后,我不得不将类定义放在顶层对象中:

@js.native
trait Appender extends js.Object {
  ...
}

@JSImport("log4javascript", "log4javascript")
@js.native
object Log4JavaScript extends js.Object {
  ...
  class AjaxAppender(url: String) extends Appender {
    def addHeader(name: String, value: String): Unit = js.native 
  }
  ...
}

这似乎是明智的,但从 scala.js 文档看来,应该可以在顶级对象之外以这种方式定义它

@JSImport("log4javascript", "log4javascript.AjaxAppender")
@js.native
class AjaxAppender(url: String) extends Appender {
  def addHeader(name: String, value: String): Unit = js.native 
}

但是,这也无法绑定。有人可以解释定义上述类的正确方法吗?或者嵌套在Log4JavaScript对象内的定义是唯一正确的方法吗?

4

1 回答 1

2

有人可以解释在将“命名空间”参数用于 JSImport 时,scala 对象/类/def/val 名称与 javascript 模块中的名称之间存在什么关系(如果有)?

Scala.js 文档的这一部分对此进行了解释。定义外观的 Scala 对象的名称无关紧要。重要的是@JSImport注释的参数。第一个指示要从哪个模块导入,第二个指示要导入什么

在您的情况下, log4javascript 模块位于包目录中的log4javascript.js文件中。log4javascript所以,你的第一个参数应该是:

@JSImport("log4javascript/log4javascript.js", ...)
object Log4JavaScript ...

但是,log4javascript 被定义为一个 npm 模块,其主文件引用该log4javascript.js文件。这意味着您可以只使用包目录名称:

@JSImport("log4javascript", ...)
object Log4JavaScript ...

(有关NodeJS 如何解析模块的更多信息,请参阅本文)

注解的第二个参数@JSImport表示要导入的内容。在您的情况下,您想要导入整个模块,而不仅仅是其中的一个成员,因此您想要使用Namespace

@JSImport("log4javascript", Namespace)
object Log4JavaScript ...

这对应于以下 EcmaScript 导入语句:

import * as Log4JavaScript from 'log4javascript'

请注意,尽管 Scala 对象名称(Log4JavaScript此处的 ,)无关紧要,但其成员的名称确实很重要,如Scala.js 文档的这一部分中所述。

根据 scala.js 文档,似乎我应该能够提供 js 对象的实际名称(我也尝试过“Log4JavaScript”)

@JSImport("log4javascript", "log4javascript")
...

但是,这无法绑定。当我尝试访问任何成员函数时,会出现运行时错误。

当您编写该代码时,您会尝试访问模块的log4javascript成员。log4javascript但是那个模块没有这样的成员。

应该可以在顶级对象之外以这种方式定义它

@JSImport("log4javascript", "log4javascript.AjaxAppender")
...

但是,这也无法绑定。

同样,这意味着“从模块导入log4javascript.AjaxAppender成员log4javascript”,但该模块没有这样的成员。以下应该有效:

@JSImport("log4javascript", "AjaxAppender")
于 2019-07-01T09:14:52.547 回答