根据我的理解,所有的JavaScript都合并到一个文件中。Rails在添加//= require_tree时默认这样做。到你的application.js manifest文件的底部。

这听起来像是一个真正的救星,但我有点担心特定于页面的JavaScript代码。这段代码是否在每一页上执行?我想要的最后一件事是当我的所有对象只在1页上需要时,为每一页实例化它们。

此外,代码是否也有可能发生冲突?

还是在页面底部放置一个小的脚本标记,只调用一个执行页面javascript代码的方法?

你不再需要require.js了吗?

谢谢

编辑:我很感激所有的答案…我认为他们并没有真正解决问题。其中一些是关于样式的,似乎没有关联……其他人只是提到javascript_include_tag…我知道这是存在的(很明显…),但Rails 3.1的前进方式似乎是将所有JavaScript打包到一个文件中,而不是在每个页面的底部加载单个JavaScript。

我能想到的最好的解决方案是用id或类将某些特性包装在div标签中。在JavaScript代码中,只需检查id或类是否在页面上,如果在,则运行与之关联的JavaScript代码。这样,如果动态元素不在页面上,JavaScript代码就不会运行——即使它包含在由Sprockets打包的庞大application.js文件中。

我上面的解决方案的好处是,如果一个搜索框包含在100个页面中的8个页面上,那么它将只在这8个页面上运行。你也不需要在网站的8个页面上包含相同的代码。事实上,您再也不需要在站点的任何地方包含手动脚本标记。

我想这就是我问题的真正答案。


JavaScripts只有在你告诉Rails(链轮)合并它们时才会合并。


我还没有尝试过这个方法,但看起来下面的方法是正确的:

如果你有一个content_for是javascript(例如与真正的javascript在里面),链轮将不知道它,因此这将以同样的方式工作,因为它现在。 如果你想从javascript包中排除一个文件,你可以进入config/sprockets。并相应地修改source_files文件。然后,您只需在需要的地方包括您排除的任何文件。


这是我如何解决样式问题:(原谅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')

希望这能有所帮助。


我很感激所有的答案……我认为他们并没有真正解决问题。其中一些是关于样式的,似乎没有关联……其他人只是提到javascript_include_tag…我知道这是存在的(很明显…),但Rails 3.1的前进方式似乎是将所有Javascript打包到一个文件中,而不是在每个页面的底部加载单个Javascript。

我能想到的最好的解决方案是用id或类将某些特性包装在div标签中。在javascript代码中。然后只需检查id或类是否在页面上,如果在,则运行与之关联的javascript代码。这样,如果动态元素不在页面上,javascript代码就不会运行——即使它包含在由Sprockets打包的庞大的application.js文件中。

我上面的解决方案的好处是,如果一个搜索框包含在100个页面中的8个页面上,那么它将只在这8个页面上运行。你也不需要在网站的8个页面上包含相同的代码。事实上,你再也不需要在你的站点的任何地方包含手动脚本标签了——除非可能是预加载数据。

我想这就是我问题的真正答案。


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

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

你的主application.js清单文件可以配置为从global/加载它的文件。特定的页面或页面组可以有它们自己的清单,这些清单从它们自己的特定目录加载文件。Sprockets会自动将application.js加载的文件与特定于页面的文件结合起来,这使得这个解决方案可以工作。

这种技术也可以用于style_sheets/。


菲利普的回答很好。下面是让它工作的代码:

在application.html.erb:

<身体类= " < % = params[:控制器].parameterize % > " >

假设你的控制器叫做Projects,它会生成:

<身体类= "项目" >

然后在projects.js.coffee中:

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

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

所以你的Rails javascripts文件夹对于两个控制器(cars和users)是这样的:

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

javascript是这样的:

// 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上。初始化执行。

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

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

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

我希望这对你有所帮助。


资产管道文档建议如何做特定于控制器的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


我知道你已经回答了自己的问题,但还有另一种选择

基本上,您假设//= require_tree。是必需的。事实并非如此。请随意删除它。在我当前的应用程序中,第一个应用程序使用3.1。老实说,我已经做了三个不同的顶级JS文件。我的application.js文件只有

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

通过这种方式,我可以创建子目录,其中包含自己的顶级JS文件,只包括我需要的内容。

关键是:

你可以删除require_tree——Rails允许你改变它所做的假设 application.js这个名字没有什么特别之处——assets/javascript子目录下的任何文件都可以包含带有//=的预处理指令

希望这对ClosureCowboy的回答有所帮助,并添加了一些细节。


我知道我来这个派对有点晚了,但我想提出一个我最近一直在用的解决方案。然而,让我首先提一下……

The Rails 3.1/3.2 Way(不,先生,我不喜欢它。)

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

为了完整起见,我在这个回答中包括以下内容,因为这不是一个不可行的解决方案……虽然我不太喜欢。

“Rails方式”是一个面向控制器的解决方案,而不是像这个问题的原始作者所要求的那样面向视图。有一些特定于控制器的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。

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

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

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

遍历DOM中标记有data-jstag的所有元素 对于每个元素,在空格上分割属性值,创建一个标记字符串数组。 对于每个标记字符串,在哈希中对该标记执行查找。 如果找到匹配的键,则运行与之关联的函数,将元素作为参数传递。

所以说,我有以下定义在我的应用程序。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
};

引导事件将对搜索输入应用my_autosuggest_init和my_hint_init函数,在用户输入时将其转换为显示建议列表的输入,并在输入为空白和不集中时提供某种输入提示。

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

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

Even if I have some complicated workflow that seems controller-specific, I will just create a file for it in my lib folder, pack it into application.js, and tag it with something like 'new-thing-wizard'. When my bootstrap hits that tag, my nice, fancy wizard will be instantiated and run. It runs for that controller's view(s) when needed, but is not otherwise coupled to the controller. In fact, if I code my wizard right, I might be able to provide all configuration data in the views and therefore be able to re-use my wizard later for any other controller that needs it.

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


在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,e。G控制器:用户,动作:仪表盘)

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}}

你可以在你的布局文件(例如application.html.erb)中添加这一行来自动加载特定于控制器的javascript文件(当你生成控制器时创建的那个):

<%= javascript_include_tag params[:controller] %>

您还可以添加一行来在每个操作的基础上自动加载脚本文件。

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

只需将页面脚本放到以控制器名称命名的子目录中。在这些文件中,您可以使用=require包含其他脚本。 最好创建一个助手,只在文件存在的情况下包含该文件,以避免浏览器出现404错误。


我有另一个解决方案,虽然原始的工作对我来说很好,不需要任何花哨的选择性加载策略。放入普通的文档准备函数,然后测试当前的窗口位置,看看它是否是你的javascript所针对的页面:

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

这仍然允许rails 3加载所有的js。X放在一个小包中,但不会产生太多开销,也不会与js不打算用于的页面产生任何冲突。


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

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

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


你也可以将js分组在文件夹中,并继续使用资产管道根据页面有选择地加载你的javascript。


这个问题在很久以前就被回答和接受了,但是我基于其中一些答案和我使用Rails 3+的经验提出了我自己的解决方案。

资产管道是甜蜜的。使用它。

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

然后在你的application_controller中。Rb创建一个helper方法:

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 include中添加你的新helper,并以raw helper为前缀:

<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!

希望这能帮助到其他人!


Ryguy的答案是一个很好的答案,尽管它被投票为负分。

特别是如果你使用的是Backbone JS——每个页面都有自己的Backbone视图。然后,erb文件只有一行内联javascript,用于启动正确的骨干视图类。我认为它是一行“胶水代码”,因此它的内联是可以的。这样做的好处是你可以保留你的“require_tree”,它可以让浏览器缓存所有的javascript。

在show.html。你会得到这样的东西:

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

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

<%= yield :javascript %>

将你所有常用的JS文件移动到像'app/assets/javascript/global'这样的子文件夹中,然后在application.js中修改//= require_tree . JS文件。行到//= require_tree ./global。

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


<%= javascript_include_tag params[:controller] %>

LoadJS gem是另一个选项:

LoadJS提供了一种在Rails应用程序中加载特定页面Javascript代码的方法,而不会失去由Sprockets提供的魔力。你所有的Javascript代码都将被压缩到一个Javascript文件中,但它的某些部分只会在某些页面上执行。 https://github.com/guidomb/loadjs


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


下面是如何做到这一点,特别是如果你不需要为特定的页面执行大量的库,而只需要运行几百行JS或多或少。

因为它完全可以将Javascript代码嵌入到HTML中,只需在app/views共享.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?


我没有看到一个答案,真正把它放在一起,并为你铺开。因此,我将尝试把meleyal, sujal (la ClosureCowboy), Ryan回答的第一部分,甚至Gal关于Backbone.js的大胆陈述……以一种简短而清晰的方式把它们放在一起。谁知道呢,我甚至可能满足Marnen Laibow-Koser的要求。

例子编辑

资产javascripts / 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>

视图/ foo / index . html . erb

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

资产javascripts / foo js。

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

assets/javascripts/foostuff/foothis.js.coffee

alert "Hello world!"

简要描述

Remove //= require_tree . from application.js and list only the JS that each page shares. The two lines shown above in application.html.erb tell the page where to include application.js and your page-specific JS. The three lines shown above in index.html.erb tells your view to look for some page-specific JS and include it at a named yield region called ":javascript" (or whatever you want to name it). In this example, the controller is "foo" so Rails will attempt to include "foo.js" at the :javascript yield region in the application layout. List your page-specific JS in foo.js (or whatever the controller is named). List common libraries, a tree, directories, whatever. Keep your custom page-specific JS someplace where you can easily reference it apart from your other custom JS. In this example, foo.js requires the foostuff tree so put your custom JS there, such as foothis.js.coffee. There are no hard rules here. Feel free to move things around and perhaps even create multiple yield regions of various names in various layouts if needed. This just shows one possible first step forward. (I don't do it exactly like this given our use of Backbone.js. I might also choose to drop foo.js down into a folder called foo instead of foostuff but haven't decided that yet.)

笔记

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

如果我在这里错过了一个明显的最佳实践,给我发个便条,我会考虑适应。Rails对我来说相当新鲜,老实说,到目前为止,我对它默认给企业开发带来的混乱以及普通Rails程序产生的所有流量并没有太大印象。


我以前使用的方法是:http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/。超级简单,依赖于控制器选择适当的js来加载。


虽然你在这里有几个答案,我认为你的编辑可能是最好的选择。我们在团队中使用的一个设计模式是从Gitlab获得的,它就是Dispatcher模式。它的功能与您正在讨论的类似,但是页面名称是由rails在body标记中设置的。例如,在你的布局文件中,只包括(在HAML中):

%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.coffee或login.js.coffee)中,你所需要做的就是将它们封装在一个类中,然后将类符号全球化,这样你就可以在调度程序中访问它:

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

Gitlab有几个这样的例子,如果你好奇的话,你可能想要戳一下:)


我把一些答案组合成:

应用助手:

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  

Paloma项目提供了一种有趣的方法来管理页面特定的javascript代码。

他们文档中的用法示例:

var UsersController = Paloma.controller('Users'); //当Rails User#new执行时执行。 UsersController.prototype.new = function(){ 警报('你好,性感用户!”); };


步骤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 %>

步骤3。在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/


首先:从application.js中删除\\=require_tree 第二:你所有的JS代码必须在/app/assets/ javascript和你所有的CSS代码必须在/app/assets/stylesheets