编写javascript模块的scalajs外观时如何使用JSImport

时间:2019-06-29 18:19:03

标签: scala.js scalajs-bundler

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

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

我将以log4javascript的外观作为此问题的示例。

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

当js库作为web jar包含时,这是实现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 docs来编写导入模块,我希望对象名称(在这种情况下为Log4JavaScript)必须与导出的符号名称匹配,以便绑定起作用。但是,log4javascript.js中的顶级符号是log4javascript。经过实验之后,scala对象名称似乎对绑定没有影响。无论我如何命名scala顶级对象,它都能正确绑定。

在使用“命名空间” args到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对象中的定义是唯一的正确方法?

1 个答案:

答案 0 :(得分:1)

  

在使用“命名空间” args到JSImport时,有人可以解释scala对象/类/ def / val名称与javascript模块中的名称之间存在什么关系吗?

这在Scala.js文档的this part中进行了说明。定义立面的Scala对象的名称无关紧要。 @JSImport批注的参数有多重要。第一个表示要导入的模块,第二个表示要导入的模块。

对于您来说,log4javascript模块位于log4javascript.js软件包目录中的log4javascript文件中。因此,您的第一个参数应该是:

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

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

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

(有关NodeJS如何解析模块的更多信息,请参见this article

@JSImport批注的第二个参数指示要导入的内容。对于您的情况,您想导入整个模块,而不仅仅是模块的一部分,因此您想使用Namespace

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

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

import * as Log4JavaScript from 'log4javascript'

请注意,尽管Scala对象名称(此处为Log4JavaScript并不重要,但其成员的名称却很重要,如Scala的this part中所述.js文档。

  

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

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

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

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

  

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

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

但是,这也无法绑定。

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

@JSImport("log4javascript", "AjaxAppender")