- 鉴于我必须提出自己的包装函数,我怀疑 Turbolinks 是故意开发的,以阻止我使用的初始化流程。真的吗?
- 如果是真的,惯用的方法是什么?我应该如何设置淘汰视图模型,或者就此而言,运行与特定页面相关的任何代码?
在特定页面上运行代码方面,首先,我通常会避免script
为该页面添加内联。这在非 Turbolinks 应用程序中通常可以正常工作,但在启用 Turbolinks 的应用程序中,在页面加载之间维护状态和事件处理程序,事情可能会变得有点混乱。理想情况下,您应该将所有 JS 包含在一个文件中。(如果您想知道如何注入服务器生成的内容,我稍后会介绍。)
您在设置和拆卸方面处于正确的位置,但我想知道您是否考虑为 setup on 实现一个初始化函数,而不是您的turbolinks_in
and函数,然后在 on和 on 上实现一个单独的拆卸函数(这是您所追求的“卸载”事件)。您的设置/拆卸代码可能如下所示:turbolinks_out
turbolinks:load
turbolinks:before-cache
turbolinks:before-render
document.addEventListener('turbolinks:load', window.MyApp.init)
document.addEventListener('turbolinks:before-cache', window.MyApp.destroy)
document.addEventListener('turbolinks:before-render', window.MyApp.destroy)
因此,与其直接在内init
联脚本中调用对象的函数,不如window.MyApp.init
决定初始化哪些对象。
它是如何决定的?!
似乎您正在为您的 JS 对象镜像控制器名称和操作名称。这是一个很好的起点,我们只需要将这些名称输入客户端。一种常见的方法(我相信 Basecamp 使用的)是使用meta
中的元素head
来输出一些服务器生成的属性:
<meta name="controller_name" content="<%= controller_name %>">
<meta name="action_name" content="<%= action_name %>">
只要您的对象遵循控制器/动作命名模式,您的应用程序的初始化函数可能类似于:
;(function () {
window.MyApp = {
init: function () {
var controller = window[getControllerName()]
var action
if (controller) action = controller[getActionName()]
if (action && typeof action.init === 'function') action.init()
}
}
function getControllerName () {
return getMeta('controller_name')
}
function getActionName () {
return getMeta('action_name')
}
function getMeta (name) {
var meta = document.querySelector('[name=' + name + ']')
if (meta) return meta.getAttribute('content')
}
})()
您可以按照这种方法来包含您的 JavaScript 配置:
<meta name="javascript_config" content="<%= @view_model.javascript_configuration.to_json %>">
然后您的应用程序的 init 函数可以使用配置调用相关的初始化程序,如下所示:
window.MyApp = {
init: function () {
var controller = window[getControllerName()]
var action
var config = getConfig()
if (controller) action = controller[getActionName()]
if (action && typeof action.init === 'function') action.init(config)
}
}
// …
function getConfig () {
return JSON.parse(getMeta('javascript_config') || null)
}
最后,我们需要拆解初始化的结果。为此,我们将存储一个对象的返回值init
,如果它包含一个destroy
函数,我们将调用它:
;(function () {
var result
window.MyApp = {
init: function () {
var controller = window[getControllerName()]
var action
var config = getConfig()
if (controller) action = controller[getActionName()]
if (action && typeof action.init === 'function') {
result = action.init(config)
}
},
// …
destroy: function () {
if (result && typeof result.destroy === 'function') result.destroy()
result = undefined
}
}
})()
把它们放在一起:
;(function () {
var result
window.MyApp = {
init: function () {
var controller = window[getControllerName()]
var action
var config = getConfig()
if (controller) action = controller[getActionName()]
if (action && typeof action.init === 'function') {
result = action.init(config)
}
},
destroy: function () {
if (result && typeof result.destroy === 'function') result.destroy()
result = undefined
}
}
function getControllerName () {
return getMeta('controller_name')
}
function getActionName () {
return getMeta('action_name')
}
function getConfig () {
return JSON.parse(getMeta('javascript_config') || null)
}
function getMeta (name) {
var meta = document.querySelector('[name=' + name + ']')
if (meta) return meta.getAttribute('content')
}
})()
显然,这种方法仅适用于每页一个 init,因此您可能希望对其进行调整以使用更多。实现这一点超出了这个答案的范围,但您可能希望查看T3,它会为您处理所有这些!