在 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>
【讨论】:
为什么不使用<%= javascript_include_tag "my_javascipt_file" %>
?
@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_tree
从application.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,例如
<%= javascript_include_tag "specific/whatever.js" %>
//.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'
。我应该用另一行替换它吗?
另外,我应该把这行<%= javascript_include_tag "specific/whatever.js" %>
放在content_for :header
块中吗?
我很奇怪你需要和config.assets.precompile
搞混。 app/assets/javascripts/*.js
中的所有内容不都是自动预编译的吗?
@AlexChaffee 只有自动可用的文件是 application.css
和 application.js
。其他必须包含在其他文件中,或者使用config.assets.precompile
列出。 Read more here
清单声明已被移动。我正在运行rails 4.2.0。并在 production.rb 文件中找到此注释:# config.assets.precompile
和 config.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 日历?