如何在scalajs-bundler中包含bootstrap



我正在使用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 文件中没有对引导程序的引用。

不应该包括引导程序吗?

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

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

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

1) 将模块放入捆绑包中

这是您在问题中提到的第一个问题。 向 npmDependencies 添加bootstrap不足以让 scalajs-bundler 将您的模块包含在捆绑包中。 除此之外,在scala代码的某个地方,你必须有一个JSImport("library_name",...)语句。 这告诉scalajs-bundler你实际上正在使用这个库,并且它需要包含在bundle中。 您可以在此处阅读有关JSImport的更多详细信息。 我发现这个描述有点模糊。以下是理查德-福伊(Richard-Foy@Julien对我的一个问题的回答,我发现这个问题更有帮助。在我的代码中,JSImport 要求包含在下面的第 3 部分中。请注意,您还需要为 jquery 库提供一个 JSImport,以确保它也包含在捆绑包中,因为 Bootstrap 依赖于它,并且您需要将jquery添加到 npmDependencies。

2) 创建 JavaScript 全局变量

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

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

理查德-福伊指出的解决方案@Julien是要遵循的一般配方,但我相信它有一个错误。 该错误不会在其示例中引起问题,因为库名称和全局变量名称是相同的。 问题是modNameglobalModules[modName]在importRule的返回行上交换。

这是我的 common.webpack.config.js scalajs-bundler 文件,指示它生成一个全局变量jQuery并将其导出。 请注意,我所做的唯一更改是将jquery: "jQuery"放在全局模块中,并在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
}
};

有关导入加载器和暴露加载器操作的一些附加资源。

3) 在运行时加载模块

通常,这不是您必须明确执行的操作。 但是,对于 Bootstrap(或任何扩展另一个 js 库的 js 库),您可能不会直接通过外观调用该库。 您很可能会使用猴子修补模式。 在这种情况下,jQuery 对象被强制转换为 Bootstrap 对象,而不直接调用 Bootstrap 库。 通常,这将编译得很好,但是您会在浏览器控制台中收到运行时错误,例如Uncaught TypeError: jq.modal is not a function。 以下是 jquery 的 Bootstrap 扩展的定义方式,以便您可以在错误消息中理解 jq 和模态:

@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.load()。我喜欢这个,因为没有必要记住在任何包装器工厂方法中显式调用它。

你必须实际使用该模块,否则 webpack 不会将其包含在生成的捆绑包中。 如果您的模块应该从全局命名空间使用,请按照此配方进行操作。

最新更新