使用Rails 3.1,您在哪里放置"特定于页面"的JavaScript代码?

Fir*_*lem 386 javascript ruby-on-rails ruby-on-rails-3.1 sprockets asset-pipeline

据我所知,您的所有JavaScript都合并为1个文件.当Rails添加//= require_tree .application.js清单文件的底部时,默认情况下会执行此操作.

这听起来像是一个真正的生命保护程序,但我有点担心特定于页面的JavaScript代码.此代码是否在每个页面上执行?我想要的最后一件事是,只需要在1页上需要为每个页面实例化我的所有对象.

此外,代码是否也存在冲突的可能性?

或者你是否script在页面底部放了一个小标签,只是调用一个执行页面javascript代码的方法?

那你不再需要require.js了吗?

谢谢

编辑:我很感谢所有的答案......我认为他们并没有真正解决问题.其中一些是关于造型而且似乎没有关联...而其他人只是提到javascript_include_tag......我知道存在(显然......)但是看起来Rails 3.1的方式将会包括所有的将您的JavaScript编入1个文件,而不是在每个页面的底部加载单独的JavaScript.

我能想到的最好的解决方案是divids或classes 包装标签中的某些功能.在JavaScript代码,你只要检查idclass在页面上,如果是,您在运行与之相关联的JavaScript代码.这样,如果动态元素不在页面上,则JavaScript代码不会运行 - 即使它已包含在application.js由Sprockets打包的大量文件中.

我的上述解决方案的好处是,如果在100个页面中的8个页面中包含搜索框,则它将仅在这8个页面上运行.您也不必在网站的8个页面上包含相同的代码.实际上,您永远不必在任何地方再次在您的网站上包含手动脚本标记.

我认为这是我问题的实际答案.

mel*_*yal 157

Asset Pipeline文档建议如何执行特定于控制器的JS:

例如,如果ProjectsController生成了a ,则会有一个新文件位于app/assets/javascripts/projects.js.coffee另一个位置app/assets/stylesheets/projects.css.scss.您应该将任何JavaScript或CSS唯一的控件放入其各自的资产文件中,因为这些文件可以仅为这些控制器加载,例如<%= javascript_include_tag params[:controller] %><%= stylesheet_link_tag params[:controller] %>.

链接到:asset_pipeline

  • 这是最优雅的方式.但是,您还需要删除// = require_tree这一行.来自application.js.coffee (50认同)
  • 对于特定于动作的控制,我在我的布局中有这个,因为并非每个控制器的每个动作都有特定的JS.`page_specific_js ="#{params [:controller]} _#{params [:action]}"`然后; `javascript_include_tag page_specific_js if Rails.application.assets.find_asset page_specific_js` (12认同)
  • 我完全同意这种方法.其他方法似乎非常笨重,最终仍然加载一个巨大的js文件.我正在进行的项目在组合/缩小后有近2mb的JS文件/插件等. (2认同)
  • 我对Rails相当新,但在我看来,这应该是默认行为. (2认同)
  • 控制器特定的操作是否仍然缩小?它们是否被添加到由sprockets创建的单个js文件中,或者elk会导致多个资产文件请求? (2认同)
  • 有多种建议的 DoItRight 方法(即使任何工作)这一事实向我证明,这没有得到很好的记录,或者整个方法都有缺陷。我也对“这是最优雅的方法”的说法提出异议。魔法一般都不好,老实说,我觉得整个资产管道充满了魔法。然而,约定可以是好的,但我觉得这条线与资产管道交叉太多了,这是一个很好的例子。`&lt;/end-of-rant&gt;`。 (2认同)

wel*_*n97 77

对于特定于页面的js,您可以使用Garber-Irish解决方案.

因此,对于两个控制器 - 汽车和用户,您的Rails javascripts文件夹可能看起来像这样:

javascripts/
??? application.js
??? init.js
??? markup_based_js_execution
??? cars
?   ??? init .js
?   ??? index.js
?   ??? ...
??? users
    ??? ...
Run Code Online (Sandbox Code Playgroud)

javascripts将如下所示:

// application.js

//= 
//= require init.js
//= require_tree cars
//= require_tree users
Run Code Online (Sandbox Code Playgroud)
// init.js

SITENAME = new Object();
SITENAME.cars = new Object;
SITENAME.users = new Object;

SITENAME.common.init = function (){
  // Your js code for all pages here
}
Run Code Online (Sandbox Code Playgroud)
// cars/init.js

SITENAME.cars.init = function (){
  // Your js code for the cars controller here
}
Run Code Online (Sandbox Code Playgroud)
// cars/index.js

SITENAME.cars.index = function (){
  // Your js code for the index method of the cars controller
}
Run Code Online (Sandbox Code Playgroud)

和markup_based_js_execution将包含UTIL对象的代码,以及支持DOM的UTIL.init执行.

并且不要忘记将它放到您的布局文件中:

<body data-controller="<%= controller_name %>" data-action="<%= action_name %>">
Run Code Online (Sandbox Code Playgroud)

我还认为最好使用类而不是data-*属性,以获得更好的特定于页面的CSS.正如Jason Garber所提到的:特定于页面的CSS选择器可能变得非常笨拙(当您使用data-*属性时)

我希望这能帮到您.

  • 如果您需要一个可用于用户控制器中的所有操作但在其他控制器中不可用的变量,该怎么办?这种方法有没有一些范围问题? (4认同)
  • @ MarnenLaibow-Koser我个人认为,当你将所有js组合成一个文件并将其最小化时,在每个页面上加载所有js对大多数项目都有好处.我相信它对用户来说整体效果更快.至少它更像是一个js文件与多个冲突(即查看http://stackoverflow.com/questions/555696/put-javascript-in-one-js-file-or-break-it-out-into-multiple -js文件).如果它使代码更简单并且适合你,那么在body上使用类和id也没什么不好的.Modernizr(http://modernizr.com/)也做了这个,以及其他一些库. (2认同)
  • 对我来说,@ MarnenLaibow-Koser铁路资产管道似乎是与编译进行比较的一个很好的候选者.程序员将他们的javascript编写在漂亮的解耦模块中,然后将它们集中在一起,缩小并提供服务.就像编译语言一样,总会有程序员认为他们比编译器领先一步......但我认为这很少是真的. (2认同)

suj*_*jal 65

我看到你已经回答了自己的问题,但这是另一种选择:

基本上,你正在做出这样的假设

//= require_tree .
Run Code Online (Sandbox Code Playgroud)

是必须的.不是.随意删除它.在我目前的应用程序中,第一个我正在使用3.1.x老实说,我已经制作了三个不同的顶级JS文件.我的application.js文件只有

//= require jquery
//= require jquery_ujs
//= require_directory .
//= require_directory ./api
//= require_directory ./admin
Run Code Online (Sandbox Code Playgroud)

这样,我可以使用自己的顶级JS文件创建子目录,只包含我需要的内容.

关键是:

  1. 您可以删除require_tree- Rails允许您更改它所做的假设
  2. 名称没有什么特别之处application.js- assets/javascript子目录中的任何文件都可以包含预处理器指令//=

希望能帮助并为ClosureCowboy的答案添加一些细节.

Sujal

  • +1这对于像我这样的新手来说很有用.如果可以的话,我会给它+2. (8认同)
  • @sujal完全正确.Rails核心团队因糟糕的JavaScript管理而臭名昭着.随意忽略他们的建议,只需使用资产管道的*good*部分.:) (5认同)

Clo*_*boy 41

另一种选择:创建特定于页面或模型的文件,您可以在assets/javascripts/文件夹中创建目录.

assets/javascripts/global/
assets/javascripts/cupcakes
assets/javascripts/something_else_specific
Run Code Online (Sandbox Code Playgroud)

您的主application.js清单文件可以配置为从中加载其文件global/.特定页面或页面组可以有自己的清单,从其自己的特定目录加载文件.Sprockets会自动将加载的文件application.js与特定于页面的文件组合在一起,从而使该解决方案能够正常工作.

这种技术也可以使用style_sheets/.

  • 你现在让我渴望了杯形蛋糕.. Dangit! (13认同)

Fir*_*lem 23

我很欣赏所有答案......我认为他们并没有真正解决问题.其中一些是关于造型而且似乎没有关联...而其他人只是提到javascript_include_tag......我知道存在(显然......)但是看起来Rails 3.1的方式将会包括所有的将您的Javascript分成1个文件,而不是在每个页面底部加载单独的Javascript.

我能想到的最好的解决方案是divids或classes 包装标签中的某些功能.在javascript代码中.然后你只需检查idclass在页面上,如果是,您在运行与之相关联的JavaScript代码.这样,如果动态元素不在页面上,则javascript代码不会运行 - 即使它已包含在application.jsSprockets打包的海量文件中.

我的上述解决方案的好处是,如果在100个页面中的8个页面中包含搜索框,则它将仅在这8个页面上运行.您也不必在网站的8个页面上包含相同的代码.实际上,您永远不必在任何地方再次在您的网站上包含手动脚本标记 - 除了可能预加载数据.

我认为这是我问题的实际答案.

  • @ MarnenLaibow-Koser不向每个唯一页面添加手动脚本标记的原因是您必须在每个页面视图上下载该脚本内容.如果您能够使用资产管道将所有javascript打包到application.js中,那么用户只会下载这些脚本一次,并在所有后续页面加载中从缓存中提取application.js (4认同)

Rya*_*yan 16

我意识到我迟到了这个派对,但我想提出一个我最近一直在使用的解决方案.但是,我先提一下......

Rails 3.1/3.2方式(不,先生.我不喜欢它.)

请参阅:http://guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline

为了这个答案的完整性,我包括以下内容,因为它不是一个不可行的解决方案......虽然我并不关心它.

"Rails Way"是面向控制器的解决方案,而不是像这个问题的原始作者那样以视图为导向.有各自的控制器命名的控制器特定的JS文件.所有这些文件都放在一个文件夹树中,默认情况下不包含在任何application.js require指令中.

要包含特定于控制器的代码,以下内容将添加到视图中.

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

我厌恶这个解决方案,但它就在那里而且很快.据推测,您可以将这些文件称为"people-index.js"和"people-show.js",然后使用类似于"#{params[:controller]}-index"获取面向视图的解决方案.再次,快速修复,但它并不适合我.

我的数据属性方式

叫我疯了,但是当我部署时,我希望我的所有JS编译并缩小为application.js.我不想记得在整个地方都包含这些小的straggler文件.

我将所有的JS加载到一个紧凑的,即将成为浏览器的缓存文件中.如果我的application.js的某一部分需要在页面上触发,我让HTML告诉我,而不是Rails.

我使用一个名为的自定义数据属性,而不是将我的JS锁定到特定的元素ID或者使用标记类乱丢我的HTML data-jstags.

<input name="search" data-jstag="auto-suggest hint" />
Run Code Online (Sandbox Code Playgroud)

在每个页面上,我使用- 在这里插入首选的JS库方法 -在DOM加载完成后运行代码.此引导代码执行以下操作:

  1. 迭代标记为DOM的DOM中的所有元素 data-jstag
  2. 对于每个元素,在空间上拆分属性值,创建标记字符串数组.
  3. 对于每个标记字符串,在哈希中为该标记执行查找.
  4. 如果找到匹配的键,则运行与其关联的函数,并将该元素作为参数传递.

所以说我在application.js中的某处定义了以下内容:

function my_autosuggest_init(element) {
  /* Add events to watch input and make suggestions... */
}

function my_hint_init(element) {
  /* Add events to show a hint on change/blur when blank... */
  /* Yes, I know HTML 5 can do this natively with attributes. */
}

var JSTags = {
  'auto-suggest': my_autosuggest_init,
  'hint': my_hint_init
};
Run Code Online (Sandbox Code Playgroud)

bootstrapping事件将对搜索输入应用my_autosuggest_initmy_hint_init函数,将其转换为输入,在用户键入时显示建议列表,以及在输入空白且未聚焦时提供某种输入提示.

除非标记了某些元素data-jstag="auto-suggest",否则自动建议代码永远不会触发.但是,它始终存在,缩小并最终缓存在我的application.js中,以便我在页面上需要它.

如果您需要将其他参数传递给标记的JS函数,则必须应用一些创造力.添加数据参数属性,提出某种参数语法,甚至使用混合方法.

即使我有一些看似特定于控制器的复杂工作流程,我也只是在我的lib文件夹中为它创建一个文件,将其打包到application.js中,并用"new-thing-wizard"标记它.当我的引导程序命中该标记时,我的漂亮,花哨的向导将被实例化并运行.它在需要时运行该控制器的视图,但不以其他方式耦合到控制器.实际上,如果我正确地编写了向导,我可能能够在视图中提供所有配置数据,因此可以在以后为任何其他需要它的控制器重用我的向导.

无论如何,这就是我现在一直在实现特定于页面的JS的方式,它对于简单的站点设计和更复杂/丰富的应用程序都很有帮助.希望我在这里提出的两种解决方案之一,我的方式或Rails方式,对将来遇到这个问题的任何人都有帮助.

  • 一个小细节:你的答案中有这样一个概念,即一旦js被浏览器缓存,它就没有任何影响.这不是真的.浏览器确实避免了下载,如果js文件被正确缓存,但它仍然*编译*每个页面渲染的代码.所以,你必须平衡权衡.如果你有很多聚合JS,但每页只使用一些,你可以通过打破JS来改善页面时间. (6认同)
  • Downvoting for"叫我疯了,但我希望我的所有JS编译并在部署时缩小为application.js." 你真的不想这样,因为它使用户加载他不需要的JavaScript,它使你的处理程序寻找甚至不会存在的属性.拥有app.js中的所有内容很诱人,Rails肯定会让它变得简单,但正确的做法是更好地模块化JavaScript. (2认同)

Mik*_*e A 7

这已经很久以前得到了回答和接受,但我根据其中的一些答案以及我对Rails 3+的经验提出了自己的解决方案.

资产管道很好.用它.

首先,在您的application.js文件中,删除//= require_tree.

然后在您application_controller.rb创建一个帮助方法:

helper_method :javascript_include_view_js //Or something similar

def javascript_include_view_js
    if FileTest.exists? "app/assets/javascripts/"+params[:controller]+"/"+params[:action]+".js.erb"
        return '<script src="/assets/'+params[:controller]+'/'+params[:action]+'.js.erb" type="text/javascript"></script>'
    end
end
Run Code Online (Sandbox Code Playgroud)

然后在你的application.html.erb布局文件中,在现有的javascript包含中添加你的新助手,前缀为raw帮助器:

<head>
    <title>Your Application</title>
    <%= stylesheet_link_tag "application", :media => "all" %>
    <%= javascript_include_tag "application" %>
    <%= raw javascript_include_view_js %>
</head>
Run Code Online (Sandbox Code Playgroud)

瞧,现在你可以使用你在rails中的其他地方使用的相同文件结构轻松创建特定于视图的javascript.只需粘贴您的文件app/assets/:namespace/:controller/action.js.erb!

希望能帮助别人!


小智 6

您可以在布局文件中添加此行(例如application.html.erb)以自动加载控制器特定的javascript文件(生成控制器时创建的文件):

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

您还可以添加一行以自动加载脚本文件.

<%= javascript_include_tag params[:controller] + "/" + params[:action] %>
Run Code Online (Sandbox Code Playgroud)

只需将页面脚本放入以控制器名称命名的子目录中即可.在这些文件中,您可以使用= require包含其他脚本.如果文件存在,创建一个帮助程序来包含文件会很好,以避免浏览器中的404失败.


小智 6

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

  • 这似乎可以回答这个问题.你可以在答案中添加更多内容吗? (2认同)

per*_*ine 6

也许你会发现pluggable_js gem是合适的解决方案.

  • 在我看来,这是最好的解决方案. (2认同)

mel*_*yal 5

LoadJS宝石是另一种选择:

LoadJS提供了一种在Rails应用程序中加载特定于页面的Javascript代码的方法,而不会失去Sprockets提供的魔力.您的所有Javascript代码将在一个Javascript文件中继续缩小,但其中某些部分仅针对某些页面执行.

https://github.com/guidomb/loadjs


归档时间:

查看次数:

79370 次

最近记录:

7 年,4 月 前