Turbolinks和Controller特定的资产

Sar*_*wan 8 ruby-on-rails asset-pipeline turbolinks

我已修改application.html.erb为使用控制器特定资产:

application.html.erb:

<!DOCTYPE html>
<html>
<head>
  <title>My Application</title>
  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= stylesheet_link_tag params[:controller], 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag params[:controller], 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
</head>
<body>
... other html template ...
Run Code Online (Sandbox Code Playgroud)

问题是,我安装了turbolinks.当我在同一个控制器中导航时,turbolinks可以工作.但当我切换到另一个控制器时,turbolinks将执行完全重新加载.有没有什么办法解决这一问题?

Ric*_*eck 13

Turbolinks

Turbolinks使用<body>您的HTML页面,并使用Ajax更改它,使该<head>区域保持不变.

只有在您的<head>意志保持不变的情况下有效- 否则如何保持不变?因此,在更改控制器页面/资产的意义上,您将不得不在没有Turbolinks的情况下进行整页更新(至少如果您使用Turbolinks而不进行黑客攻击)

我建议修复此问题的方法是更改​​特定于控制器的资产结构,特别是尽可能少地更改<head>区域.

-

Turbolinks跟踪

阅读后Turbolinks documentation,您可以turbolinks-data-track从控制器特定资产中删除选项中受益:

<%= javascript_include_tag controller_name, 'data-turbolinks-track' => false %>
Run Code Online (Sandbox Code Playgroud)

您可以跟踪某些资产,例如application.js和application.css,您希望确保这些资产始终是Turbolinks会话中的最新版本.这是通过使用data-turbolinks-track标记这些资产链接来完成的,如下所示:

<link href="/assets/application-9bd64a86adb3cd9ab3b16e9dca67a33a.css" rel="stylesheet" type="text/css" data-turbolinks-track>

如果这些资产更改了URL(嵌入了md5标记以确保这一点),则该页面将执行完全重新加载而不是通过Turbolinks.这可确保所有Turbolinks会话始终使用最新的JavaScript和CSS.

-

CONTROLLER_NAME

您将从中受益的是使用controller_name帮助程序(代替params[:controller]):

<%= javascript_include_tag 'application', controller_name, 'data-turbolinks-track' => true %>
Run Code Online (Sandbox Code Playgroud)


wen*_*yan 5

将您的控制器特定 js 放在 body 标签中。由于 turbolinks 不会触及 html 正文,因此您每次访问该页面时都会重新加载那段 js 代码。

在你的情况下

<body>
  <%= javascript_include_tag params[:controller] %>
  ...
</body>
Run Code Online (Sandbox Code Playgroud)

但是如果你的控制器js中有很多代码,你可能会注意到上面的解决方案有点耗时。因为当您访问该页面时,会重新加载一大块代码。那么如何处理呢?


??以下方法可能会导致一些jquery事件被多次绑定。这是涡轮链接的问题。阅读这篇文章http://staal.io/blog/2013/01/18/dangers-of-turbolinks/

更有效的解决方案

基本上,当用户第一次访问您的网站时,您会加载所有 js 代码,确保不会运行特定于控制器的代码(分别将它们包装在函数中)并将它们保存在一个全局变量中,例如Site. 然后你通过编写类似的东西来触发你的控制器特定的js

<script>
$(document).on("ready page:load", function () {
  Site.load('<%= controller_name %>');
}
</script>
Run Code Online (Sandbox Code Playgroud)

在身体标签中。

这样,实际控制器特定的 js 代码只加载一次并在需要时触发。

执行

app/assets/javascripts/site.js

var site;
if(!window.Site) {
  site = window.Site = {};
  site.controllers = {}

  site.load = function (controller) {
    if (this.controllers.hasOwnProperty(controller)) {
      this.controllers[controller].call();
    }
  };

  site.add = function (controller, fn) {
    this.controllers[controller] = fn;
  }
}
Run Code Online (Sandbox Code Playgroud)

将您的控制器 js 包装在函数中并将它们保存到Site. 例如,users.js

app/assets/javascripts/site.js

Site.add("users", function () {
  // UserController related js code
}
Run Code Online (Sandbox Code Playgroud)

在你的 application.js 中加载它们。

app/assets/javascripts/site.js

//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require site
//= require_tree .
Run Code Online (Sandbox Code Playgroud)

然后在您的布局中触发控制器特定的 js。

app/views/layouts/application.html.erb

<body>
  <script>
  $(document).on("page:load", function () {
    Site.load('<%= controller_name %>');
  }
  </script>
  <%= yield %>
</body>
Run Code Online (Sandbox Code Playgroud)

特定于动作的 js 代码怎么样?

只需修改site.js和布局。我用以下代码段结束它。

(function (global) {
  var site;

  if(global.Site) {
    return;
  }

  site = global.Site = {};
  site.controllers = {};
  site.actions = {};

  site.load = function (name) {
    var keys = name.split(".");
    var c = keys[0];
    var cs = this.controllers;
    var as = this.actions;
    var i;

    if (cs.hasOwnProperty(c)) {
      for (i =0 ; i < cs[c].length; i++) {
        cs[c][i].call();
      }
    }

    if (keys.length > 1 && as.hasOwnProperty(name)) {
      for (i =0 ; i < as[name].length; i++) {
        as[name][i].call();
      }
    }
  };

  site.add = function (name, fn) {
    var keys = name.split(".");
    var lv = keys.length > 1 ? this.actions : this.controllers;

    if (!lv.hasOwnProperty(name)) {
      lv[name] = [fn];
    } else {
      lv[name].push(fn);
    }
  };

})(window);
Run Code Online (Sandbox Code Playgroud)

app/views/layouts/application.html.erb

<body>
  <script>
  $(document).on("ready page:load", function () {
    Site.load('<%= controller_name %>.<%= action_name %>');
  }
  </script>
  <%= yield %>
</body>
Run Code Online (Sandbox Code Playgroud)

有关的

这是一个有趣的视频,关于为您可能喜欢观看的单页应用程序加载 javascript、css 和其他资产。

OSCON 2014:Instagram.com 的运作方式;皮特·亨特

https://www.youtube.com/watch?v=VkTCL6Nqm6Y