В моем проекте Scala.js есть некоторые объекты, к которым я хотел бы получить динамический доступ. Я думаю, что смогу заставить это работать с помощью Dynamic, однако в настоящее время шаг fastOptJS / fullOptJS отбрасывает их. Есть ли способ (аннотация?) Пометить их как требуемые, чтобы никогда не отбрасывать их во время оптимизации?

Я не хочу нигде их перечислять, так как в моем приложении их сотни, и они часто добавляются / удаляются.

Примере:

trait Res {def value;}
object A {def value = "A...";}
object B {def value = "B...";}

def loadAB(name: String): String {
  scala.scalajs.js.Dynamic.global.selectDynamic(name).asInstanceOf[Res].value
}
0
Suma 21 Сен 2018 в 22:28

2 ответа

Лучший ответ

Вы не можете получить доступ к object, определенным в Scala.js, через js.Dynamic.global, потому что по умолчанию Scala.js ничего не помещает в global. Он помещает туда только вещи, экспортированные с @JSExportTopLevel.

@Simon Groenewolt предложил решение, основанное на @JSExportTopLevel, но в вашем случае это взлом с неоптимальными свойствами. В самом деле, это заставит каждый из этих объектов быть представленным в глобальной области видимости JavaScript, и это нехорошо, поскольку они могут быть изменены с помощью JavaScript (или изменены с помощью других библиотек JavaScript).

Кроме того, этот взлом перестанет работать, если вы (или ваши иждивенцы) испустите модули CommonJS. Он также больше не будет работать как таковой в Scala.js 1.x, поскольку динамический выбор из global больше не будет разрешен (вместо этого вам придется динамически находить глобальный объект заранее. ) .

Лучшим решением для вашего варианта использования является Reflect API. В частности, похоже, что вы хотите иметь возможность загружать object, которые расширяют Res по их имени, а это именно то, что дает вам Reflect, если вы аннотируете Res с @EnableReflectiveInstantiation:

package foo

import scala.scalajs.reflect.Reflect
import scala.scalajs.reflect.annotation.EnableReflectiveInstantiation

@EnableReflectiveInstantiation
trait Res {def value;}

object A {def value = "A...";}
object B {def value = "B...";}

def loadAB(name: String): String {
  Reflect
    .lookupLoadableModuleClass(name + "$")
    .getOrElse(throw new Exception("hum, that as not found")
    .loadModule()
    .asInstanceOf[Res]
    .value
}

println(loadAB("foo.A"))

Обратите внимание, что вам нужно использовать полное имя объектов.

2
sjrd 22 Сен 2018 в 09:27

Может быть, добавление к ним аннотации @JSExportTopLevel(<identifier>) даст желаемый эффект? Это предназначено для экспорта в JavaScript, но будет имеют побочный эффект предотвращения отказа от кода при объединении.

1
Simon Groenewolt 21 Сен 2018 в 20:50