Как использовать JSImport при написании фасада scalajs для модулей javascript - PullRequest
1 голос
/ 29 июня 2019

Я написал фасад, используя JSImport, и он работает. К сожалению, я пришел к решению методом проб и ошибок, и я не до конца понимаю, почему это конкретное решение работает, а другие, которые я пробовал, - нет.

Справочная информация: я начинаю с рабочего проекта, созданного с использованием sbt, который представляет собой одностраничное приложение, которое реализует код на стороне клиента с scala.js, а на стороне сервера - scala и инфраструктуру Play. Библиотеки javascript были упакованы с веб-файлами jar и упакованы в файл js клиента с помощью переменной sbt jsDependencies. Я хотел реализовать некоторые новые функции, для которых требовалась библиотека до версии, а затем требовалась версия некоторых библиотек JavaScript, которые были доступны только в формате npm. Поэтому теперь я включаю все зависимости javascript для клиентского приложения, используя npmDependencies, с плагином scalajs-bundler. Это сломало некоторые фасады скалайдов, которые привели к моему вопросу.

Я буду использовать фасад для log4javascript в качестве примера для этого вопроса.

Переменная log4javascript - это объект верхнего уровня, используемый для доступа к остальной части API.

Когда js-библиотеки были включены в качестве веб-файлов, вот как был реализован фасад к 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.

Может ли кто-нибудь объяснить, какая связь существует, если таковая имеется, между именами объектов / классов / def / val scala и именами в модуле javascript при использовании аргумента «Пространство имен» для JSImport?

В соответствии с документацией 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. Когда библиотека была включена в качестве веб-файла, определение выглядело так:

@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 Ответ

1 голос
/ 01 июля 2019

Может ли кто-нибудь объяснить, какая существует связь между именами объектов / классов / def / val scala и именами в модуле javascript при использовании аргумента «Пространство имен» для JSImport?

Это объясняется в этой части документации 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")
...

Однако это также не удаетсяbind.

Опять же, это означает «импортировать элемент log4javascript.AjaxAppender из модуля log4javascript», но этот модуль не имеет такого члена.Должно работать следующее:

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