在 Rails 3 应用程序中添加页面特定 JavaScript 的最佳方法?

Posted

技术标签:

【中文标题】在 Rails 3 应用程序中添加页面特定 JavaScript 的最佳方法?【英文标题】:Best way to add page specific JavaScript in a Rails 3 app? 【发布时间】:2011-03-27 03:21:31 【问题描述】:

Rails 3 有一些非常酷的不显眼的 javascript

但我想知道为特定页面添加附加 JavaScript 的最佳方法是什么。

例如,我以前可能做过的地方:

<%= f.radio_button :rating, 'positive', :onclick => "$('some_div').show();" %>

我们现在可以用类似的东西让它不显眼

<%= f.radio_button :rating, 'positive' %>

# then in some other file
$('user_rating_positive').click(function() 
  $('some_div').show();

所以我想我的问题是在哪里/如何包含该 JavaScript?我不想填写 application.js 文件,因为这个 JavaScript 只适用于这一个视图。我应该以某种方式为每个页面包含一个自定义 JavaScript 文件,还是将其粘贴到标题查找的实例变量中?

【问题讨论】:

Using Rails 3.1, where do you put your "page specific" javascript code? 的可能重复项 对于那些想要全面了解其工作原理和最佳方法的人,请阅读railsapps.github.io/rails-javascript-include-external.html。这是迄今为止我看到的关于这个主题的最好的文档。它不仅适用于 Rails,而且适用于任何从事 Web 开发的人。这就是为什么最好按照 Rails 的方式做事。我肯定会使用 Unholy Rails 来解决我未来的问题。哇。 @KateGregory 那一个更新了。 【参考方案1】:

我喜欢在 content_for :head 块中包含按视图的 Javascript,然后将 yield 包含在应用程序布局中的该块中。例如

如果它很短,那么:

<% content_for :head do %>
  <script type="text/javascript">
    $(function() 
      $('user_rating_positve').click(function() 
        $('some_div').show();
      
    );
  </script>
<% end %>

或者,如果更长,那么:

<% content_for :head do %>
  <script type="text/javascript">
    <%= render :partial => "my_view_javascript"
  </script>
<% end %>

然后,在你的布局文件中

<head>
  ...
  <%= yield :head %>
</head>

【讨论】:

为什么不使用&lt;%= javascript_include_tag "my_javascipt_file" %&gt; @AJP 我猜那它违背了资产管道的目的 @lulalala 但答案中的代码不是以更混乱的方式做到这一点吗? @AJP 它更混乱,但它比你的答案少了一个 http 请求。 我喜欢这篇文章railsapps.github.com/rails-javascript-include-external.html【参考方案2】:

如果您只想在一个页面上包含 javascript,您当然可以将其包含在页面内联中,但是如果您想对您的 javascript 进行分组并利用资产管道、缩小 js 等,则可以这样做并通过将您的 js 分成仅适用于站点的某些控制器/视图/部分的组来组合并仅加载到特定页面上的额外 js 资产。

将资产中的 js 移动到文件夹中,每个文件夹都有一个单独的清单文件,因此如果您有一个仅在后端使用的管理 js 库,您可以这样做:

资产 javascript 管理员 ...js admin.js(管理员组的清单) application.js(应用程序全局组的清单) 全球 ...js

在现有的application.js中

//= require jquery
//= require jquery_ujs
//= require_tree ./global // requires all js files in global folder

在新的 admin.js 清单文件中

//= require_tree ./admin // requires all js files in admin folder

确保通过编辑 config/production.rb 加载这个新的 js 清单

config.assets.precompile += %w( admin.js )

然后调整你的页面布局,这样你就可以在页面头部包含一些额外的js:

<%= content_for :header %>   

然后在您想要包含此特定 js 组(以及普通应用程序组)和/或任何页面特定 js、css 等的视图中:

<% content_for :header do %>
  <%= javascript_include_tag 'admin' %>  
<% end %>

您当然可以对 css 做同样的事情,并以类似的方式将其分组以仅应用于网站的某些区域。

【讨论】:

如果您将所有清单保存在同一个位置,您可以告诉 rails 预编译该文件夹中的所有内容。这样您只需要编辑一次 production.rb 并且不跟踪您添加到其中的内容。 不知道你可以这样做 - 所以你可以有一个包含你的清单的“清单”文件夹,以及各个 js 组?我可以试试这个。我真希望他们在默认情况下有一个更理智的设置,因为他们似乎认为你只会将所有 js 放入一个随处导入的大文件中。 奇怪,如果我使用 //= require_tree ./global,我会收到 Sprockets::FileNotFound 错误,但如果我使用 Rails 3.2.12 使用 //= require_directory ./global,则不会。 看起来你最终会得到很多脚本标签。我认为资产管道的目标是只有一个脚本标签?我只会编写 javascript,以便它们是特定于页面的。 我想我明白了。不过,避免额外的脚本标签对页面加载时间非常重要。【参考方案3】:

这些答案对我帮助很大!如果有人想要更多...

    如果您希望它们被预编译,您需要将 javascripts 放入清单中。但是,如果您需要来自 application.js.coffee 的每个 javascript 文件,那么每次您导航到不同的页面时都会加载所有 javacsripts,并且执行特定于页面的 javascripts 的目的将被打破。

因此,您需要创建自己的清单文件(例如speciifc.js),这将需要所有特定于页面的 javascript 文件。另外,将require_treeapplication.js修改

app/assets/javascripts/application.js

//= require jquery
//= require jquery_ujs
//= require_tree ./global

app/assets/javascripts/specific.js

//= require_tree ./specific

然后在您的environments/production.rb 中使用配置选项将此清单添加到预编译列表中,

config.assets.precompile += %w( specific.js )

完成!所有应始终加载的共享 javascripts 将被放置在app/assets/javascripts/global 文件夹中,而页面特定的javascripts 将被放置在app/assets/javascripts/specific 中。您可以简单地从视图中调用特定于页面的 javascript,例如

&lt;%= javascript_include_tag "specific/whatever.js" %&gt; //.js 是可选的。

这已经足够了,但我也想使用javascript_include_tag params[:controller]。当您创建控制器时,会在app/assets/javascripts 中生成一个关联的咖啡脚本文件,就像其他人提到的那样。有真正的controller-specific javascripts,只有当用户到达特定的控制器视图时才会加载。

所以我创建了另一个清单controller-specific.js

app/assets/javascripts/controller-specific.js

//= require_directory .

这将包括与控制器关联的所有自动生成的咖啡脚本。此外,您需要将其添加到预编译列表中。

config.assets.precompile += %w( specific.js controller-specific.js )

【讨论】:

非常感谢您的详细回答。我应该把这条线放在哪里 javascript_include_tag params[:controller] 。目前我的 application.html.erb 有这一行 javascript_include_tag 'application' 。我应该用另一行替换它吗? 另外,我应该把这行&lt;%= javascript_include_tag "specific/whatever.js" %&gt; 放在content_for :header 块中吗? 我很奇怪你需要和config.assets.precompile 搞混。 app/assets/javascripts/*.js 中的所有内容不都是自动预编译的吗? @AlexChaffee 只有自动可用的文件是 application.cssapplication.js。其他必须包含在其他文件中,或者使用config.assets.precompile 列出。 Read more here 清单声明已被移动。我正在运行rails 4.2.0。并在 production.rb 文件中找到此注释:# config.assets.precompileconfig.assets.version 已移至 config/initializers/assets.rb【参考方案4】:

我更喜欢以下...

在您的 application_helper.rb 文件中

def include_javascript (file)
    s = " <script type=\"text/javascript\">" + render(:file => file) + "</script>"
    content_for(:head, raw(s))
end

然后在您的特定视图中(本示例中为 app/views/books/index.html.erb)

<% include_javascript 'books/index.js' %>

...似乎对我有用。

【讨论】:

【参考方案5】:

如果您不想使用资产管道或complex work arounds 来获取必要的页面特定 javascript(我表示同情),这是最简单和最强大的方法,它与上面的答案相同,但代码更少只是为了使用:

<%= javascript_include_tag "my_javascipt_file" %>

注意:与使用 content_for :head 的答案相比,每个包含标签确实需要多一个 http 请求

【讨论】:

javascript_include_tag 正是我想要的,干杯 AJP 你把“my_javascipt_file”放在哪里?在资产/javascripts 中?但如果是这样,那不是在 application.js 中使用 //= require . 自动获取吗? @Linus 是的,//= require . 会将该脚本带入您网站上的任何页面,因此您必须执行 Kenny Grant(使用 //= require_tree ./global)或 bjg 建议之类的操作。 这对我来说是迄今为止最简单的方法,我将 application.js 中的 //= require_tree . 更改为 //= require_directory .,然后在 assets\javascripts 中创建了一个子目录。请注意,您必须在 config\initializers\assets.rb 中包含新目录才能通过添加以下行来预编译您的 js:Rails.application.config.assets.precompile += %w( new_dir/js_file.js )【参考方案6】:

看看pluggable_js gem。您可能会发现此解决方案更易于使用。

【讨论】:

【参考方案7】:

我的理解是,资产管道旨在通过将所有 js 混合到一个(缩小的)文件中来减少页面加载时间。虽然这表面上看起来令人反感,但它实际上是 C 和 Ruby 等流行语言中已经存在的特性。诸如“包含”标签之类的东西旨在防止文件的多次包含,并帮助程序员组织他们的代码。当您用 C 编写和编译程序时,所有这些代码都存在于正在运行的程序的每个部分中,但方法仅在使用该代码时才加载到内存中。从某种意义上说,编译后的程序不包含任何东西来保证代码是很好的模块化。我们通过以这种方式编写程序来使代码模块化,并且操作系统仅将给定位置所需的对象和方法加载到内存中。甚至有“特定方法的包含”之类的东西吗?如果您的 rails 应用程序是安静的,那么这基本上就是您所要求的。

如果您编写 javascript 以增强页面上 HTML 元素的行为,那么这些函数在设计上是“特定于页面的”。如果您编写了一些复杂的代码,无论其上下文如何,它都会执行,则可以考虑将该代码绑定到 html 元素(您可以使用 body 标记,如Garber-Irish method 中所述)。如果一个函数有条件地执行,性能可能会小于所有那些额外的脚本标签。

我正在考虑使用paloma gem,如rails apps project 中所述。然后,您可以通过在 paloma 回调中包含特定于页面的函数来使您的 javascript 页面特定于:

Paloma.callbacks['users']['new'] = function(params)
    // This will only run after executing users/new action
    alert('Hello New Sexy User');
; 

你使用 Rails,所以我知道你喜欢宝石 :)

【讨论】:

【参考方案8】:

您不应该在资产管道之外加载您的 JS 或 CSS 文件,因为您会失去使 Rails 如此出色的重要功能。而且您不需要其他宝石。我相信尽可能少使用宝石,这里不需要使用宝石。

您想要的是“特定于控制器的 Javascript”(“特定于操作的 Javascript 包含在底部)。这允许您为特定的控制器加载特定的 JavaScript 文件。尝试将您的 Javascript 连接到视图是一种善意of... 倒退且不遵循 MVC 设计模式。您希望将其与控制器或控制器内的操作相关联。

不幸的是,无论出于何种原因,Rails 开发人员决定默认情况下,每个页面都会加载位于资产目录中的每个 JS 文件。为什么他们决定这样做而不是默认启用“控制器特定的 Javascript”,我永远不会知道。这是通过 application.js 文件完成的,该文件默认包含以下代码行:

//= require_tree .

这被称为指令。这是 sprockets 用来加载 assets/javascripts 目录中的每个 JS 文件的方法。默认情况下,sprockets 会自动加载 application.js 和 application.css,require_tree 指令会加载各自目录下的每个 JS 和 Coffee 文件。

注意:当你搭建脚手架时(如果你不搭建脚手架,现在是开始的好时机),Rails 会自动为你生成一个 coffee 文件,为此脚手架的控制器。如果您希望它生成 标准 JS 文件而不是 coffee 文件,请删除默认启用的 coffee gem 在您的 Gemfile,而你的脚手架会创建 JS 文件。

好的,所以启用“控制器特定 Javascript”的第一步是从您的 application.js 文件中删除 require_tree 代码,或者如果您将其更改为 assets/javascripts 目录中的文件夹仍然需要全局 JS 文件。即:

//= require_tree ./global

第 2 步:进入您的 config/initializers/assets.rb 文件,并添加以下内容:

%w( controllerone controllertwo controllerthree ).each do |controller|
  Rails.application.config.assets.precompile += ["#controller.js", "#controller.css"]
end

插入所需的控制器名称。

第 3 步:将 application.html.erb 文件中的 javascript_include_tag 替换为此(注意 params[:controller] 部分:

<%= javascript_include_tag 'application', params[:controller], 'data-turbolinks-track': 'reload' %>

重启你的服务器和中提琴!使用脚手架生成的 JS 文件现在只会在调用该控制器时加载。

需要在你的控制器中的特定 ACTION 上加载特定的 JS 文件,即/articles/新 ?改为这样做:

application.html.erb

<%= javascript_include_tag "#controller_name/#action_name" if AppName::Application.assets.find_asset("#controller_name/#action_name") %>

config/initializers/assets.rb

config.assets.precompile += %w(*/*)

然后在 assets/javascripts 文件夹中添加一个与控制器同名的新文件夹,并将与您的操作同名的 js 文件放入其中。然后它将在该特定操作上加载它。

【讨论】:

【参考方案9】:

好吧,这可能是有史以来最糟糕的工作,但我创建了一个控制器方法,它刚刚渲染出 .js 文件

控制器

def get_script
   render :file => 'app/assessts/javascripts/' + params[:name] + '.js'
end
def get_page
   @script = '/' + params[:script_name] + '.js?body=1'
   render page
end

查看

%script:src => @script, :type => "text/javascript"

如果出于某种原因我们不想这样做,请告诉我。

【讨论】:

【参考方案10】:

添加 JS 的首选方式是在页脚中,因此您可以这样做:

show.html.erb:

<% content_for :footer_js do %>
   This content will show up in the footer section
<% end %>

layouts/application.html.erb

<%= yield :footer_js %>

【讨论】:

以上是关于在 Rails 3 应用程序中添加页面特定 JavaScript 的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

Rails,如何在特定页面上显示 jquery-ui-datepicker 日历?

添加具有特定标签的子模块而不是在一个命令中分支

Rails 将@variable 从一个显示页面传递到另一个显示页面

Rails - 使用特定属性值缓存所有用户

Rails-覆盖在使用部分或特定控制器视图时的页面元素

如何在 Rails 3.2.1 中使用控制器特定的样式表?