我完全明白为什么Turbolinks 5很棒,如果你正在阅读它,你可能也会这么做,但是我对它与块上其他脚本的播放效果非常沮丧。
到目前为止,没有简单的解释(人类可读),它说明了如何以允许它们运行的方式包装现有的jQuery脚本。 以此为例:https://github.com/Bttstrp/bootstrap-switch。它写得很好,易于理解。您将js和css加载到资产管道并在某个页面上实例化它。
# view.html.erb
<input type="checkbox" class="switch"> switch button
<script type="text/javascript">
$(".switch").bootstrapSwitch();
</script>
你去view.html,点击另一个页面,点击后面,你会看到两个按钮。
接下来,你花了5个小时寻找一种让Turbolinks加载bootstrapSwitch实例的方法,如果之前没有加载的话。好吧,即使你这样做,功能也会消失。点击它将无效。
$(document).on("turbolinks:load", function()...
会在每次Turbolink访问时加载它,而目前,我唯一可以使其工作而不是创建重复项的方法是使用
<%= content_for :head do %>
<meta name="turbolinks-cache-control" content="no-cache">
<% end %>
哪种感觉有点愚蠢。
我认为这一切都与使用idempotent有关 - https://github.com/turbolinks/turbolinks#making-transformations-idempotent但你怎么做到这一点?
有人可以把这个简单的插件作为一个例子,并分享一个简单,优雅的解决方案,使其工作,然后我们可以用其他脚本重现吗?
答案 0 :(得分:2)
使用Turbolinks开发应用程序确实需要特定的方法才能让事情顺利进行。由于页面加载和缓存的方式不同,运行脚本的某些模式在Turbolinks与不使用Turbolinks时的行为方式不同。这起初看起来似乎不友好,并且&#34;陷阱&#34;可能令人沮丧,但我发现只需一点点理解,它就会鼓励更有条理,更健壮的代码:)
正如您所知,重复切换的问题是插件在同一元素上被多次调用。这是因为Turbolinks在导航远离它之前缓存页面 ,因此缓存版本包括任何动态添加的HTML [1],例如通过插件添加的东西。向后/向前导航时,将恢复缓存版本,并且行为将重复:/
那么如何解决这个问题呢?使用添加HTML或事件侦听器的代码时,通常在缓存页面之前拆除行为是个好主意。 Turbolinks事件是turbolinks:before-cache
。所以你的设置/拆解可能是:
// app/assets/javascripts/switches.js
$(document)
.on('turbolinks:load', function () {
$('.switch').bootstrapSwitch()
})
.on('turbolinks:before-cache', function () {
$('.switch').bootstrapSwitch('destroy')
})
这有点难以测试,因为所有设置和拆除都是在事件处理程序中完成的。更重要的是,可能会有更多这样的案例,所以为了防止重复,你可能想要引入自己的&#34;迷你框架&#34;用于设置和拆除功能。以下介绍创建基本框架。
以下是我们的目标:使用名称调用window.App.addFunction
并使用函数注册要调用的函数。该函数获取元素并调用插件。它返回一个具有destroy
功能的对象,用于拆卸:
// app/assets/javascripts/switches.js
window.App.addFunction('switches', function () {
var $switches = $('.switch').bootstrapSwitch()
return {
destroy: function () {
$switches.bootstrapSwitch('destroy')
}
}
})
以下实现addFunction
,在functions
属性中存储已添加的函数:
// app/assets/javascripts/application.js
// …
window.App = {
functions: {},
addFunction: function (name, fn) {
this.functions[name] = fn
}
}
我们将在应用初始化时调用每个函数,并将每个函数调用的结果存储在results
数组中(如果存在):
// app/assets/javascripts/application.js
// …
var results = []
window.App = {
// …
init: function () {
for (var name in this.functions) {
var result = this.functions[name]()
if (result) results.push(result)
}
}
}
拆除应用程序涉及销毁对任何结果的调用destroy
(如果存在):
// app/assets/javascripts/application.js
// …
window.App = {
// …
destroy: function () {
for (var i = 0; i < results.length; i++) {
var result = results[i]
if (typeof result.destroy === 'function') result.destroy()
}
results = []
}
}
最后,我们初始化并拆除应用程序:
$(document)
.on('turbolinks:load', function () {
window.App.init.call(window.App)
})
.on('turbolinks:before-cache', window.App.destroy)
所以把这一切放在一起:
;(function () {
var results = []
window.App = {
functions: {},
addFunction: function (name, fn) {
this.functions[name] = fn
},
init: function () {
for (var name in this.functions) {
var result = this.functions[name]()
if (result) results.push(result)
}
},
destroy: function () {
for (var i = 0; i < results.length; i++) {
var result = results[i]
if (typeof result.destroy === 'function') result.destroy()
}
results = []
}
}
$(document)
.on('turbolinks:load', function () {
window.App.init.call(window.App)
})
.on('turbolinks:before-cache', window.App.destroy)
})()
现在,函数独立于调用它们的事件处理程序。这种脱钩有几个好处。首先,它是更可测试的:window.App.functions
中提供了功能。您还可以选择何时调用您的功能。例如,假设您决定不使用Turbolinks,那么您需要更改的唯一部分就是调用window.App.init
时。
[1]我认为这比默认浏览器行为更好(按下&#34;返回&#34;将用户返回到首次加载时的页面)。 Turbolinks&#34; Back&#34;在用户离开时将用户返回到页面,这可能是用户期望的。