如何使用 scalajs-bundler 包含 bootstrap
How to include bootstrap with scalajs-bundler
我正在使用 scalajs-bundler 插件并定义了我的 build.sbt 这样:
enablePlugins(ScalaJSBundlerPlugin)
name := "Reproduce"
scalaVersion := "2.12.8"
npmDependencies in Compile += "bootstrap" -> "3.4.1"
然而,当我 运行 "sbt fastOptJS::webpack" 时,在生成的 -fasopt-bundle.js 文件中没有对 bootstrap 的引用。
bootstrap 不应该包括在内吗?
您必须实际使用该模块,否则 webpack 不会将其包含在生成的包中。
如果您的模块应该从全局命名空间中使用,请遵循 this recipe.
我刚刚 运行 遇到了完全相同的问题,但无法在任何搜索中找到解决方案。希望这个(相当长的)答案能帮助其他人避免一些痛苦。
我认为有 3 个问题需要解决才能使用捆绑为 npm 模块的 Bootstrap 库(即使用 scalajs-bundler 通过 npmDependencies 参数将其打包)。
1) 将 bootstrap 库放入包中。
2) 使 jQuery 符号可用于 Bootstrap 作为全局变量。
3) 在 运行 时间加载 Bootstrap。
1) 将模块放入包中
这是您在问题中提到的第一个问题。添加 bootstrap
到 npmDependencies 不足以让 scalajs-bundler 将您的模块包含在包中。除此之外,在您的 Scala 代码中的某处,您必须有一个 JSImport("library_name", ...) 语句。这告诉 scalajs-bundler 您实际上正在使用该库并且需要将其包含在捆绑包中。您可以阅读有关 JSImport 的更多详细信息 here. I found that description a little vague. 是我对@Julien Richard-Foy 提出的问题的回答,我发现它更有帮助。在我的代码中,满足了 JSImport 要求,包括在下面的第 3 部分中。请注意,您还需要为 jquery 库提供一个 JSImport,以确保它也包含在捆绑包中,因为 Bootstrap 依赖于它,并且您需要将 jquery
添加到 npmDependencies .
2) 创建javascript全局变量
这是解决方案中最复杂的部分。
在我的应用程序中,我会在浏览器控制台中收到类似 jQuery is not defined
的错误。我花了一些时间才确定这是在 Bootstrap 库中引起的。 Bootstrap 库依赖于 jquery 库,假设定义了全局变量 jQuery。不幸的是,仅通过 npmDependencies 和 JSImport 在包中包含 jquery 是不够的。您必须告诉 scalajs-bundler 创建 jQuery 全局变量并将其导出到包中的所有模块。
solution @Julien Richard-Foy points to is the general recipe to follow, but I believe it has a bug. The bug doesn't cause a problem in their example因为库名和全局变量名完全相同。问题是 modName
和 globalModules[modName]
在 importRule 的 return 行交换了。
这是我的 common.webpack.config.js 文件 scalajs-bundler 指示它生成一个全局变量 jQuery
并导出它。请注意,我所做的唯一更改是将 jquery: "jQuery"
放在 globalModules 中,并在 importRule 的 return 行中交换 modName
和 globalModules[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-loader and expose-loader.
操作的一些额外资源
3) 在运行时间加载模块
通常这不是您必须明确执行的操作。但是,在 Bootstrap (或任何扩展另一个 js 库的 js 库)的情况下,您可能不会直接通过外观调用该库。您很可能会使用 Monkey Patching 模式。在这种情况下,一个 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]
解决方案是在调用隐式函数之前的某个时间对库进行一些显式引用。
这是我的做法。
private object BootstrapLib {
@js.native
@JSImport("bootstrap", Namespace)
object BootstrapModule extends js.Object
private lazy val dummy = BootstrapModule
def load() = dummy
}
BootstrapLib.load()
它包含在一个对象中,该对象包含 Bootstrap 组件的所有包装器定义。这个 gua运行tees Bootstrap.load()
在使用任何 Bootstrap 包装器之前被调用。我喜欢这个,因为不需要记住在任何包装器工厂方法中显式调用它。
我正在使用 scalajs-bundler 插件并定义了我的 build.sbt 这样:
enablePlugins(ScalaJSBundlerPlugin)
name := "Reproduce"
scalaVersion := "2.12.8"
npmDependencies in Compile += "bootstrap" -> "3.4.1"
然而,当我 运行 "sbt fastOptJS::webpack" 时,在生成的 -fasopt-bundle.js 文件中没有对 bootstrap 的引用。
bootstrap 不应该包括在内吗?
您必须实际使用该模块,否则 webpack 不会将其包含在生成的包中。 如果您的模块应该从全局命名空间中使用,请遵循 this recipe.
我刚刚 运行 遇到了完全相同的问题,但无法在任何搜索中找到解决方案。希望这个(相当长的)答案能帮助其他人避免一些痛苦。
我认为有 3 个问题需要解决才能使用捆绑为 npm 模块的 Bootstrap 库(即使用 scalajs-bundler 通过 npmDependencies 参数将其打包)。
1) 将 bootstrap 库放入包中。
2) 使 jQuery 符号可用于 Bootstrap 作为全局变量。
3) 在 运行 时间加载 Bootstrap。
1) 将模块放入包中
这是您在问题中提到的第一个问题。添加 bootstrap
到 npmDependencies 不足以让 scalajs-bundler 将您的模块包含在包中。除此之外,在您的 Scala 代码中的某处,您必须有一个 JSImport("library_name", ...) 语句。这告诉 scalajs-bundler 您实际上正在使用该库并且需要将其包含在捆绑包中。您可以阅读有关 JSImport 的更多详细信息 here. I found that description a little vague. jquery
添加到 npmDependencies .
2) 创建javascript全局变量
这是解决方案中最复杂的部分。
在我的应用程序中,我会在浏览器控制台中收到类似 jQuery is not defined
的错误。我花了一些时间才确定这是在 Bootstrap 库中引起的。 Bootstrap 库依赖于 jquery 库,假设定义了全局变量 jQuery。不幸的是,仅通过 npmDependencies 和 JSImport 在包中包含 jquery 是不够的。您必须告诉 scalajs-bundler 创建 jQuery 全局变量并将其导出到包中的所有模块。
solution @Julien Richard-Foy points to is the general recipe to follow, but I believe it has a bug. The bug doesn't cause a problem in their example因为库名和全局变量名完全相同。问题是 modName
和 globalModules[modName]
在 importRule 的 return 行交换了。
这是我的 common.webpack.config.js 文件 scalajs-bundler 指示它生成一个全局变量 jQuery
并导出它。请注意,我所做的唯一更改是将 jquery: "jQuery"
放在 globalModules 中,并在 importRule 的 return 行中交换 modName
和 globalModules[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-loader and expose-loader.
操作的一些额外资源3) 在运行时间加载模块
通常这不是您必须明确执行的操作。但是,在 Bootstrap (或任何扩展另一个 js 库的 js 库)的情况下,您可能不会直接通过外观调用该库。您很可能会使用 Monkey Patching 模式。在这种情况下,一个 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]
解决方案是在调用隐式函数之前的某个时间对库进行一些显式引用。
这是我的做法。
private object BootstrapLib {
@js.native
@JSImport("bootstrap", Namespace)
object BootstrapModule extends js.Object
private lazy val dummy = BootstrapModule
def load() = dummy
}
BootstrapLib.load()
它包含在一个对象中,该对象包含 Bootstrap 组件的所有包装器定义。这个 gua运行tees Bootstrap.load()
在使用任何 Bootstrap 包装器之前被调用。我喜欢这个,因为不需要记住在任何包装器工厂方法中显式调用它。