如何在scalajs-bundler中包含引导程序

时间:2019-04-25 10:45:18

标签: twitter-bootstrap scalajs-bundler

我正在使用scalajs-bundler插件,并这样定义了build.sbt:

enablePlugins(ScalaJSBundlerPlugin)

name := "Reproduce"
scalaVersion := "2.12.8"

npmDependencies in Compile += "bootstrap" -> "3.4.1"

但是,当我运行“ sbt fastOptJS :: webpack”时,在生成的-fastopt-bundle.js文件中没有对引导的引用。

不应该包括引导程序吗?

2 个答案:

答案 0 :(得分:0)

您必须实际使用该模块,否则webpack不会将其包含在结果包中。 如果应该从全局名称空间使用您的模块,请遵循this recipe

答案 1 :(得分:0)

我遇到了完全相同的问题,无法在任何搜索中找到解决方案。希望这个答案(相当长)可以帮助其他人避免一些痛苦。

我认为要使用捆绑为npm模块的Bootstrap库(即使用scalajs-bundler通过npmDependencies参数打包),需要解决3个问题。

1)将引导程序库放入包中。
2)将jQuery符号作为全局变量提供给Bootstrap。
3)在运行时加载Bootstrap。

1)将模块放入捆绑包

这是您在问题中提到的第一个问题。向npmDependencies添加bootstrap不足以使scalajs-bundler将您的模块包括在包中。除此之外,在您的Scala代码中,您还必须具有JSImport(“ library_name”,...)语句。这告诉scalajs-bundler您实际上正在使用该库,并且它需要包含在捆绑软件中。您可以阅读有关JSImport here的更多详细信息。我发现该描述有些模糊。 Here是@Julien Richard-Foy对我的一个问题的回答,我发现它更有帮助。在我的代码中,满足JSImport要求的内容包含在下面的第3部分中。请注意,由于Bootstrap依赖于jquery库,因此您还需要为jquery lib提供一个JSImport以确保它包含在捆绑软件中,并且需要向npmDependencies中添加jquery

2)创建javascript全局变量

这是解决方案中最复杂的部分。

在我的应用程序中,我会在浏览器控制台(如jQuery is not defined)中收到错误消息。我花了一些时间来确定这是在Bootstrap lib中引起的。通过假设定义了全局变量jQuery,Bootstrap库取决于jquery库。不幸的是,仅通过npmDependencies和JSImport将jquery包括在捆绑软件中是不够的。您必须告诉scalajs-bundler创建jQuery全局变量并将其导出到捆绑软件中的所有模块。

solution @朱利安·理查德·福伊(Julien Richard-Foy)指出的是遵循的一般方法,但是我相信它有一个错误。该错误不会在它们的example中引起问题,因为库名和全局变量名是相同的。问题是在导入规则的返回行上交换了modNameglobalModules[modName]

这是我的scalajs-bundler的common.webpack.config.js文件,指示它生成全局变量jQuery并将其导出。请注意,我所做的唯一更改是将jquery: "jQuery"放在globalModules中,并在importRule的返回行上交换了modNameglobalModules[modName]的位置。否则,我将按照示例进行操作(即,还需要其他配置文件和对build.sbt的更改)。

var globalModules = {
  jquery: "jQuery"
};

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

module.exports = {
  performance: { hints: false },
  module: {
    rules: allRules
  }
};

有关imports-loaderexpose-loader的操作的一些其他资源。

3)在运行时加载模块

通常,您不必明确地执行此操作。但是,对于Bootstrap(或任何扩展了另一个js库的js库),您可能不会直接通过立面调用该库。您很可能会使用Monkey Patching模式。在这种情况下,jQuery对象被强制转换为Bootstrap对象,而无需直接调用Bootstrap库。通常,这可以正常编译,但是在浏览器控制台中会出现运行时错误,例如Uncaught TypeError: jq.modal is not a function。这是定义jquery的Bootstrap扩展的方式,以便您可以在错误消息中理解jq和modal:

  @js.native
  trait BootstrapJQuery extends JQuery {
    def modal(action: String): BootstrapJQuery = js.native
    def modal(options: js.Any): BootstrapJQuery = js.native
  }

  implicit def jq2bootstrap(jq: JQuery): BootstrapJQuery = jq.asInstanceOf[BootstrapJQuery]

解决方案是在隐式调用之前的某个时候对lib进行一些显式引用。

这就是我的做法。

  private object BootstrapLib {
    @js.native
    @JSImport("bootstrap", Namespace)
    object BootstrapModule extends js.Object

    private lazy val dummy = BootstrapModule

    def load() = dummy
  }
  BootstrapLib.load()

这包含在一个对象内,该对象包含Bootstrap组件的所有包装器定义。这样可以确保在使用任何Bootstrap包装器之前调用Bootstrap.load()。我喜欢这样做,因为不需要记住在任何包装工厂方法中都明确地调用它。