我正在玩Turbolinks 5和Rails,试图弄清楚它是如何改变游戏的。
传统上,我的网页通过一组包含库(jQuery,Knockout等)和特定于应用程序的代码的javascript工作。视图模型 - 全部附加到某个有组织结构中的窗口对象。应用程序区域中的所有控制器,例如前端将共享相同的JavaScript(和CSS)包。
在每个使用JavaScript的页面上,都会有一个初始化程序来启动。例如:
$.ready(function() { window.users.update.init(#{@user_edit_view_model.javascript_configuration.to_json.html_safe}) } )
此init函数的职责是设置一个挖空视图模型并将其绑定到某个节点。 javascript_configuration可能包含当前用户的初始化状态。
无论如何,这种方法似乎根本不适用于Turbolinks。
我知道只有当用户访问users#edit
作为第一页(或进行硬刷新)时才会触发,因此$.ready
显然不在图片之外。
如果我附加到turbolinks:load
事件,则上述代码不仅在用户输入users#edit
时触发,而且还在用户随后导航到的任何页面上触发(直到他/她完全刷新为止)在某些时候)。
我已经能够通过定义一个在第一次执行后删除回调的函数来解决这个问题。
# For running code after the current page is ready
window.turbolinks_in = (callback) ->
event_listener = ->
callback()
window.removeEventListener("turbolinks:load", event_listener)
window.addEventListener "turbolinks:load", event_listener
此外,我还设计了:
# For running code when leaving the current page
window.turbolinks_out = (callback) ->
event_listener = ->
callback()
window.removeEventListener("turbolinks:before-cache", event_listener)
window.addEventListener "turbolinks:before-cache", event_listener
这两个函数似乎允许我在页面加载和“卸载”时运行代码。
我的问题是:
turbolinks:before-cache
,那么在简要显示缓存页面后如何清理?我确实理解在替换(已经)缓存页面时不应该触发turbolinks:before-cache
在语义上是有意义的,那么它的位置是什么呢?像turbolinks:unload
这样的东西不存在。答案 0 :(得分:2)
- 看到我必须提出自己的包装函数,我怀疑Turbolinks是故意开发的,以阻止我使用的初始化流程。这是真的吗?
- 如果这是真的,那么惯用的方法是什么?我应该如何设置淘汰视图模型或者运行任何与特定页面相关的代码?
醇>
就在特定网页上运行代码而言,首先,我通常会避免为该网页添加内联script
。这通常适用于非Turbolinks应用程序,但在启用Turbolinks的应用程序中,状态和事件处理程序在页面加载之间保持不变,事情可能会变得有些混乱。理想情况下,您应该将所有JS包含在一个文件中。 (如果您想知道如何注入服务器生成的内容,我会稍后介绍一下。)
您在设置和拆除方面处于正确的界限,但我想知道您是否考虑实施“turbolinks_in
和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
因此,meta
不是直接在内联脚本中调用对象的head
函数,而是决定要初始化哪些对象。
如何决定?!
您似乎正在镜像JS对象的控制器名称和操作名称。这是一个很好的起点,我们只需要将这些名称放到客户端。一种常见的方法(我相信Basecamp使用)是使用<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')
}
})()
元素来输出一些服务器生成的属性:
<meta name="javascript_config" content="<%= @view_model.javascript_configuration.to_json %>">
只要您的对象遵循控制器/操作命名模式,您的应用程序的初始化功能可能类似于:
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)
}
您可以按照此方法添加JavaScript配置:
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')
}
})()
函数,我们会调用它:
SELECT DATE_FORMAT(DATE_TIME_FIELD, '%Y-%m-%d %H') AS DATE_TIME_HOUR
FROM sensor
GROUP BY DATE_FORMAT(DATE_TIME_FIELD, '%Y-%m-%d %H')
HAVING AVG(T)>4
把它们放在一起:
var devicePixelRatio = window.devicePixelRatio || 1;
var renderer = new THREE.WebGLRenderer({antialias: devicePixelRatio === 1});
显然,这种方法只适用于每页一个init,因此您可能希望对其进行调整以使其更多。实现这一点超出了这个答案的范围,但您可能希望查看T3,它会为您处理所有这些!