提问



据我所知,您的所有JavaScript都合并为1个文件。当Rails将//= require_tree .添加到application.js清单文件的底部时,默认情况下会这样做。


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


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


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


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


谢谢


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


我能想出的最好的解决方案是用id s或class es包装div标签中的某些特征。在JavaScript代码中,您只需检查idclass是否在页面上,如果是,则运行与之关联的JavaScript代码。这样,如果动态元素不在页面上,则JavaScript代码不会运行 - 即使它已包含在由Sprockets打包的大量application.js文件中。


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


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

最佳参考


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



  例如,如果生成ProjectsController,则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 [103]

其它参考1


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


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


javascripts/
├── application.js
├── init.js
├── markup_based_js_execution
├── cars
│   ├── init .js
│   ├── index.js
│   └── ...
└── users
    └── ...


javascripts将如下所示:


// application.js

//= 
//= require init.js
//= require_tree cars
//= require_tree users





// init.js

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

SITENAME.common.init = function (){
  // Your js code for all pages here
}





// cars/init.js

SITENAME.cars.init = function (){
  // Your js code for the cars controller here
}





// cars/index.js

SITENAME.cars.index = function (){
  // Your js code for the index method of the cars controller
}


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


别忘了把它放到你的布局文件中:


<body data-controller="<%= controller_name %>" data-action="<%= action_name %>">


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


我希望这能帮到您。

其它参考2


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


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


//= require_tree .


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


//= require jquery
//= require jquery_ujs
//= require_directory .
//= require_directory ./api
//= require_directory ./admin


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


关键是:



  1. 你可以删除require_tree - Rails让你改变它所做的假设

  2. 名称application.js没有什么特别之处 - assets/javascript子目录中的任何文件都可以包含//=
  3. 的预处理器指令


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


Sujal

其它参考3


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


assets/javascripts/global/
assets/javascripts/cupcakes
assets/javascripts/something_else_specific


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


这种技术也可以用于style_sheets/

其它参考4


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


我能想出的最佳解决方案是用id s或class es包装div标签中的某些功能。在javascript代码中。然后你只需检查idclass是否在页面上,如果是,则运行与之关联的javascript代码。这样,如果动态元素不在页面上,则javascript代码不会运行 - 即使它已包含在由Sprockets打包的大量application.js文件中。


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


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

其它参考5


我知道我来这个派对有点晚了,但我想提出一个我最近一直在使用的解决方案。但是,我先提一下......


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


见:http://guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline [105]


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


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


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


<%= javascript_include_tag params[:controller] %>


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


我的数据属性方式


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


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


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


<input name="search" data-jstag="auto-suggest hint" />


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



  1. 迭代标有data-jstag
  2. 的DOM中的所有元素
  3. 对于每个元素,在空间上拆分属性值,创建一个标记字符串数组。

  4. 对于每个标记字符串,在哈希中为该标记执行查找。

  5. 如果找到匹配的密钥,请运行与之关联的函数,并将该元素作为参数传递。



所以说我在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
};


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


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


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


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


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

其它参考6


这已经很久以前得到了回答和接受,但我基于其中一些答案以及我对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


然后在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>


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


希望能帮助别人!

其它参考7


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


<%= javascript_include_tag params[:controller] %>


您还可以添加一行以基于每个操作自动加载脚本文件。


<%= javascript_include_tag params[:controller] + "/" + params[:action] %>


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

其它参考8


<%= javascript_include_tag params[:controller] %>

其它参考9


也许你会发现pluggable_js gem是合适的解决方案。[106]

其它参考10


LoadJS gem是另一种选择:[107]



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

  
  https://github.com/guidomb/loadjs[108]


其它参考11


菲利普的回答非常好。以下是使其有效的代码:


在application.html.erb中:


<body class="<%=params[:controller].parameterize%>">


假设您的控制器名为Projects,那将生成:


<body class="projects">


然后在projects.js.coffee中:


jQuery ->
  if $('body.projects').length > 0  
     $('h1').click ->
       alert 'you clicked on an h1 in Projects'

其它参考12


只有在告诉Rails(Sprockets,而不是)合并它们时,才会合并JavaScripts。

其它参考13


这就是我解决造型问题的方法:(原来是Haml)


%div{:id => "#{params[:controller].parameterize} #{params[:view]}"}
    = yield


这样我就可以使用以下命令启动所有特定于页面的 .css.sass 文件:


#post
  /* Controller specific code here */
  &#index
    /* View specific code here */
  &#new
  &#edit
  &#show


这样你就可以轻松避免任何冲突。
说到 .js.coffee 文件,您可以初始化元素,如;


$('#post > #edit') ->
  $('form > h1').css('float', 'right')


希望这有助于一些人。

其它参考14


您还可以将js分组到文件夹中,并继续使用资产管道根据页面选择性地加载您的JavaScript。

其它参考15


我同意你的回答,检查选择器是否在那里,使用:


if ($(selector).length) {
    // Put the function that does not need to be executed every page
}


(没有看到任何人添加实际的解决方案)

其它参考16


我没有看到一个真正把它放在一起的答案并为你解决。因此,我会尝试将 meleyal , sujal (a la ClosureCowboy ), Ryan的答案的第一部分,甚至 Gal的关于Backbone.js的大胆声明...所有在一起的方式这很简短明了。而且,谁知道,我甚至可以满足 Marnen Laibow-Koser的要求。


示例编辑



资产/Javascript角/的的application.js


//= require jquery
//= require jquery_ujs
//= require lodash.underscore.min
...




视图/布局/的 application.html.erb


  ...
  </footer>

  <!-- Javascripts ================================================== -->
  <!-- Placed at the end of the document so the pages load faster -->
  <%= javascript_include_tag "application" %>
  <%= yield :javascript %>

</body>
</html>




视图/富/的 index.html.erb


...
<% content_for :javascript do %>
  <%= javascript_include_tag params[:controller] %>
<% end %>




资产/Javascript角/的 foo.js


//= require moment
//= require_tree ./foostuff




资产/Javascript角/foostuff/的 foothis.js.coffee


alert "Hello world!"






简要说明




  • application.js 中删除//= require_tree .,并仅列出每个页面共享的JS。

  • 上面显示的 application.html.erb 中的两行告诉页面包含application.js和特定于页面的JS的位置。

  • 上面 index.html.erb 中显示的三行告诉您的视图查找某些特定于页面的JS并将其包含在名为:javascript的命名yield区域(或任何您想要的内容)这个名字)。在此示例中,控制器为foo,因此Rails将尝试在应用程序布局中的:javascript yield区域中包含foo.js。

  • foo.js (或控制器的名称)中列出特定于页面的JS。列出公共库,树,目录等等。

  • 保持您的自定义页面特定的JS在某个地方,您可以轻松地从其他自定义JS中引用它。在这个例子中,foo.js需要foostuff树,所以把你的自定义JS放在那里,比如 foothis.js.coffee

  • 这里没有严格的规定。如果需要,可以随意移动东西,甚至可以创建各种布局的多个屈服区域。这只是向前迈出的第一步。 (考虑到我们使用Backbone.js,我不会完全这样做。我也可以选择将foo.js放到一个名为foo而不是foostuff的文件夹中,但尚未确定。)



备注



你可以用CSS和<%= stylesheet_link_tag params[:controller] %>做类似的事情,但这超出了问题的范围。


如果我在这里错过了一个明显的最佳实践,请给我一个注释,然后我会适应.Rails对我来说还是一个新手,老实说,到目前为止,我对于它带来的混乱,它默认为企业开发和所有平均Rails程序生成的流量。

其它参考17


我有另一个解决方案,虽然原始对我来说很好,并且不需要任何花哨的选择性加载策略。放入你的nornal文档就绪函数,但然后测试当前的窗口位置,看看它是你的javascript用于的页面:


$(document).ready(function() {
   if(window.location.pathname.indexOf('/yourpage') != -1) {
          // the javascript you want to execute
   }
}


这仍然允许所有js由rails 3.x在一个小包中加载,但是不会产生很多开销或与js不打算的页面有任何冲突。

其它参考18


ryguy的回答是一个很好的答案,尽管它已被归入负面土地。


特别是如果你正在使用像Backbone JS这样的东西 - 每个页面都有自己的Backbone视图。那么erb文件只有一行内联javascript,可以启动正确的主干视图类。我认为这是一行胶水代码 因此,它的内联是正常的。优点是你可以保留你的require_tree,让浏览器缓存所有的javascript。


在show.html.erb中,你会有类似的东西:


<% provide :javascript do %>
  <%= javascript_include_tag do %>
    (new app.views.ProjectsView({el: 'body'})).render();
  <% end %>
<% end do %>


在你的布局文件中,你需要:


<%= yield :javascript %>

其它参考19


将所有commom JS文件移动到子文件夹,如app/assets/javascript/global,然后在application.js中,将//= require_tree .行修改为//= require_tree ./global


现在,您可以自由地将特定于控制器的JS放在app/assets/javascript/根目录中,它们将不会包含在已编译的JS中,仅在您通过控制器/视图上的= javascript_include_tag调用它们时使用。

其它参考20


虽然你在这里有几个答案,但我认为你的编辑可能是最好的选择。我们从Gitlab获得的团队设计模式是Dispatcher模式。它的功能类似于你所说的,但是页面名称是通过rails在body标签中设置的。例如,在你的布局文件中,只包含类似(在HAML中):[110]


%body{'data-page' => "#{controller}:#{action}" }


然后在你的javascripts文件夹的dispatcher.js.coffee文件中只有一个闭包和一个switch语句,如下所示:


$ ->
  new Dispatcher()

class Dispatcher
  constructor: ->
    page = $('body').attr('data-page')
    switch page
      when 'products:index'
        new Products() 
      when 'users:login'
        new Login()


您需要在单个文件中执行的操作(例如products.js.coffeelogin.js.coffee)将它们包含在一个类中,然后全局化该类符号,以便您可以在调度程序中访问它:


class Products
  constructor: ->
    #do stuff
@Products = Products


Gitlab有几个这方面的例子,如果你好奇的话,你可能想要解决这个问题:)

其它参考21


Paloma项目提供了管理页面特定javascript代码的有趣方法。[111]


他们的文档中的用法示例:



var UsersController = Paloma.controller('Users');

// Executes when Rails User#new is executed.
UsersController.prototype.new = function(){
   alert('Hello Sexy User!' );
};


其它参考22


步骤1。删除require_tree。在你的application.js和application.css中。


第2步。在布局文件夹中编辑application.html.erb(通过rails默认值)。在以下标签中添加params [[:controller]]。


<%= stylesheet_link_tag    'application', params[:controller], media: 'all', 'data-turbolinks-track' => true %>

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


第三步。在config/initializers/assets.rb中添加文件


%w( controller_one controller_two controller_three ).each do |controller|
  Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.js.coffee", "#{controller}.css", "#{controller}.scss"]
end


引用:
http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/[112]

其它参考23


我没有试过这个,但看起来如下:



  • 如果你有一个javascript的content_for(例如,其中包含真正的javascript),sprockets就不会知道它,因此这将与现在的方式相同。

  • 如果你想从大包的javascript中排除一个文件,你会进入config/sprockets.yml文件并相应地修改source_files。然后,您只需包含您在需要时排除的任何文件。


其它参考24


我以前使用这种方法做过:http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/。超级简单,依靠控制器来选择要加载的正确js。 [113]

其它参考25


我将一些答案合并到:


申请助手:


module ApplicationHelper
  def js_page_specific_include
    page_specific_js = params[:controller] + '_' + params[:action]
    if Rails.application.assets.find_asset(page_specific_js).nil?
      javascript_include_tag 'application', 'data-turbolinks-track' => true
    else
      javascript_include_tag 'application', page_specific_js, 'data-turbolinks-track' => true
    end
  end
end


布局/application.html.haml:


 <!DOCTYPE html>
%html{lang: 'uk'}
  %head   
    = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
   bla-bla-bla
    = js_page_specific_include   
   bla-bla-bla  

其它参考26


首先:从application.js中删除\\=require_tree
第二:所有的JS代码都必须在/app/assets/javascritpt中进行分配,所有的CSS代码都必须在/app/assets/stylesheets中进行分配。

其它参考27


在Ryan的带领下,这就是我所做的 -


application.js.coffee


$ ->
    view_method_name = $("body").data("view") + "_onload"
    eval("#{view_method_name}()") if eval("typeof #{view_method_name} == 'function'")
    view_action_method_name = $("body").data("view") + "_"+$("body").data("action")+"_onload"
    eval("#{view_action_method_name}()") if eval("typeof #{view_action_method_name} == 'function'")


users.js.coffee(控制器特定的coffeescript,例如controller:users,action:dashboard)


window.users_dashboard_onload = () ->
    alert("controller action called")
window.users_onload = () ->
    alert("controller called")


application.html.haml


%body{:data=>{:view=>controller.controller_name, :action=>controller.action_name}}

其它参考28


这里是如何做到的,特别是如果你不必为你的特定页面执行大量的库,但只是或多或少地运行几百行的JS。


由于将Javascript代码嵌入到HTML中非常好,只需在app/views shared.js目录下创建并将页面/页面特定代码放在 my_cool_partial.html.erb


<script type="text/javascript"> 
<!--
  var your_code_goes_here = 0;
  function etc() {
     ...
  }
-->
</script>


所以现在从你想要的任何地方你只需:


  = render :partial => 'shared.js/my_cool_partial'


那就是它,k?